tomwer 1.2.0a1__py3-none-any.whl → 1.2.0a3__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 (219) hide show
  1. orangecontrib/tomwer/tutorials/append_raw_darks_and_flats_frames_to_NXtomos.ows +44 -0
  2. orangecontrib/tomwer/tutorials/copy_reduced_darks_and_flats_meth1.ows +55 -0
  3. orangecontrib/tomwer/tutorials/copy_reduced_darks_and_flats_meth2.ows +48 -0
  4. orangecontrib/tomwer/tutorials/default_cor_search.ows +40 -0
  5. orangecontrib/tomwer/tutorials/hello_world_python_script.ows +50 -0
  6. orangecontrib/tomwer/tutorials/simple_slice_reconstruction_on_slurm.ows +50 -0
  7. orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +8 -8
  8. orangecontrib/tomwer/widgets/__init__.py +1 -1
  9. orangecontrib/tomwer/widgets/cluster/FutureSupervisorOW.py +0 -1
  10. orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +8 -6
  11. orangecontrib/tomwer/widgets/control/AdvancementOW.py +0 -1
  12. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +1 -6
  13. orangecontrib/tomwer/widgets/control/DataListOW.py +0 -1
  14. orangecontrib/tomwer/widgets/control/DataListenerOW.py +4 -4
  15. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +0 -1
  16. orangecontrib/tomwer/widgets/control/DataTransfertOW.py +7 -7
  17. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +0 -1
  18. orangecontrib/tomwer/widgets/control/DataWatcherOW.py +0 -3
  19. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +3 -2
  20. orangecontrib/tomwer/widgets/control/EmailOW.py +82 -0
  21. orangecontrib/tomwer/widgets/control/FilterOW.py +3 -3
  22. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +1 -1
  23. orangecontrib/tomwer/widgets/control/NotifierOW.py +0 -1
  24. orangecontrib/tomwer/widgets/control/ReduceDarkFlatSelectorOW.py +93 -0
  25. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +29 -5
  26. orangecontrib/tomwer/widgets/control/TimerOW.py +1 -2
  27. orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +0 -1
  28. orangecontrib/tomwer/widgets/control/VolumeSelector.py +0 -1
  29. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +4 -10
  30. orangecontrib/tomwer/widgets/control/icons/email.png +0 -0
  31. orangecontrib/tomwer/widgets/control/icons/email.svg +58 -0
  32. orangecontrib/tomwer/widgets/control/icons/reduced_darkflat_selector.png +0 -0
  33. orangecontrib/tomwer/widgets/control/icons/reduced_darkflat_selector.svg +199 -0
  34. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +0 -1
  35. orangecontrib/tomwer/widgets/debugtools/ObjectInspectorOW.py +0 -1
  36. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +1 -2
  37. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +1 -2
  38. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +0 -1
  39. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +0 -1
  40. orangecontrib/tomwer/widgets/other/PythonScriptOW.py +29 -1
  41. orangecontrib/tomwer/widgets/other/TomoObjsHub.py +28 -0
  42. orangecontrib/tomwer/widgets/other/icons/hub.png +0 -0
  43. orangecontrib/tomwer/widgets/other/icons/hub.svg +113 -0
  44. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +18 -12
  45. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +0 -2
  46. orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +21 -6
  47. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +29 -7
  48. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +18 -5
  49. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +40 -13
  50. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +37 -10
  51. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +2 -3
  52. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +5 -4
  53. orangecontrib/tomwer/widgets/stitching/StitcherOW.py +0 -1
  54. orangecontrib/tomwer/widgets/stitching/ZStitchingConfigOW.py +0 -1
  55. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +10 -4
  56. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  57. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +0 -1
  58. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +0 -1
  59. orangecontrib/tomwer/widgets/visualization/RadioStackOW.py +7 -5
  60. orangecontrib/tomwer/widgets/visualization/SampleMovedOW.py +1 -1
  61. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -3
  62. orangecontrib/tomwer/widgets/visualization/SliceStackOW.py +7 -5
  63. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +4 -4
  64. tomwer/__main__.py +139 -5
  65. tomwer/app/axis.py +16 -5
  66. tomwer/app/canvas_launcher/config.py +1 -1
  67. tomwer/app/canvas_launcher/mainwindow.py +164 -6
  68. tomwer/app/darkref.py +10 -181
  69. tomwer/app/darkrefpatch.py +10 -131
  70. tomwer/app/diffframe.py +11 -0
  71. tomwer/app/imagekeyeditor.py +12 -19
  72. tomwer/app/intensitynormalization.py +1 -0
  73. tomwer/app/lamino.py +7 -2
  74. tomwer/app/patchrawdarkflat.py +131 -0
  75. tomwer/app/radiostack.py +10 -0
  76. tomwer/app/reducedarkflat.py +205 -0
  77. tomwer/app/saaxis.py +27 -8
  78. tomwer/app/sadeltabeta.py +29 -8
  79. tomwer/app/samplemoved.py +11 -0
  80. tomwer/app/scanviewer.py +12 -0
  81. tomwer/app/sinogramviewer.py +11 -0
  82. tomwer/app/slicestack.py +11 -0
  83. tomwer/app/zstitching.py +12 -0
  84. tomwer/core/futureobject.py +4 -2
  85. tomwer/core/process/conditions/filters.py +26 -4
  86. tomwer/core/process/control/datadiscovery.py +4 -0
  87. tomwer/core/process/control/datawatcher/datawatcher.py +5 -1
  88. tomwer/core/process/control/email.py +148 -0
  89. tomwer/core/process/control/nxtomoconcatenate.py +9 -2
  90. tomwer/core/process/control/nxtomomill.py +58 -16
  91. tomwer/core/process/control/scanselector.py +4 -0
  92. tomwer/core/process/control/scantransfer.py +52 -23
  93. tomwer/core/process/control/test/test_concatenate_nxtomos.py +1 -0
  94. tomwer/core/process/control/test/test_email.py +52 -0
  95. tomwer/core/process/control/test/test_h52nx_process.py +106 -0
  96. tomwer/core/process/control/test/test_volume_link.py +5 -4
  97. tomwer/core/process/control/timer.py +27 -6
  98. tomwer/core/process/control/tomoobjserie.py +4 -0
  99. tomwer/core/process/control/volumeselector.py +4 -0
  100. tomwer/core/process/control/volumesymlink.py +47 -8
  101. tomwer/core/process/edit/darkflatpatch.py +49 -8
  102. tomwer/core/process/edit/imagekeyeditor.py +63 -13
  103. tomwer/core/process/reconstruction/axis/__init__.py +1 -1
  104. tomwer/core/process/reconstruction/axis/axis.py +61 -41
  105. tomwer/core/process/reconstruction/axis/params.py +4 -6
  106. tomwer/core/process/reconstruction/darkref/darkrefs.py +53 -16
  107. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +12 -2
  108. tomwer/core/process/reconstruction/lamino/__init__.py +1 -1
  109. tomwer/core/process/reconstruction/lamino/tofu.py +22 -2
  110. tomwer/core/process/reconstruction/nabu/nabucommon.py +93 -14
  111. tomwer/core/process/reconstruction/nabu/nabuscores.py +70 -33
  112. tomwer/core/process/reconstruction/nabu/nabuslices.py +219 -41
  113. tomwer/core/process/reconstruction/nabu/nabuvolume.py +240 -108
  114. tomwer/core/process/reconstruction/nabu/utils.py +10 -36
  115. tomwer/core/process/reconstruction/normalization/normalization.py +10 -3
  116. tomwer/core/process/reconstruction/saaxis/__init__.py +1 -0
  117. tomwer/core/process/reconstruction/saaxis/saaxis.py +564 -376
  118. tomwer/core/process/reconstruction/sadeltabeta/__init__.py +1 -0
  119. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +481 -268
  120. tomwer/core/process/reconstruction/scores/params.py +21 -8
  121. tomwer/core/process/reconstruction/test/test_darkref_copy.py +2 -0
  122. tomwer/core/process/reconstruction/test/test_saaxis.py +21 -8
  123. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +8 -5
  124. tomwer/core/process/script/python.py +7 -2
  125. tomwer/core/process/stitching/nabustitcher.py +10 -3
  126. tomwer/core/process/task.py +2 -9
  127. tomwer/core/process/test/test_axis.py +25 -15
  128. tomwer/core/process/test/test_conditions.py +6 -6
  129. tomwer/core/process/test/test_dark_and_flat.py +20 -15
  130. tomwer/core/process/test/test_data_transfer.py +8 -8
  131. tomwer/core/process/test/test_data_watcher.py +1 -1
  132. tomwer/core/process/test/test_lamino.py +6 -6
  133. tomwer/core/process/test/test_nabu.py +13 -8
  134. tomwer/core/process/test/test_normalization.py +1 -0
  135. tomwer/core/process/test/test_timer.py +6 -6
  136. tomwer/core/process/visualization/dataviewer.py +4 -0
  137. tomwer/core/process/visualization/diffviewer.py +4 -0
  138. tomwer/core/process/visualization/imagestackviewer.py +4 -0
  139. tomwer/core/process/visualization/radiostack.py +4 -0
  140. tomwer/core/process/visualization/samplemoved.py +4 -0
  141. tomwer/core/process/visualization/sinogramviewer.py +4 -0
  142. tomwer/core/process/visualization/slicestack.py +4 -0
  143. tomwer/core/process/visualization/volumeviewer.py +4 -0
  144. tomwer/core/scan/hdf5scan.py +4 -4
  145. tomwer/core/scan/scanbase.py +5 -1
  146. tomwer/core/scan/test/test_process_registration.py +9 -9
  147. tomwer/core/settings.py +59 -1
  148. tomwer/core/test/test_lamino.py +2 -1
  149. tomwer/core/utils/__init__.py +16 -0
  150. tomwer/core/utils/locker.py +0 -1
  151. tomwer/core/utils/resource.py +6 -11
  152. tomwer/core/utils/scanutils.py +2 -0
  153. tomwer/gui/cluster/slurm.py +91 -7
  154. tomwer/gui/cluster/supervisor.py +16 -11
  155. tomwer/gui/cluster/test/test_cluster.py +16 -1
  156. tomwer/gui/conditions/filter.py +3 -3
  157. tomwer/gui/control/datalist.py +24 -11
  158. tomwer/gui/control/email.py +183 -0
  159. tomwer/gui/control/reducedarkflatselector.py +545 -0
  160. tomwer/gui/control/singletomoobj.py +23 -1
  161. tomwer/gui/control/test/test_email.py +35 -0
  162. tomwer/gui/control/test/test_reducedarkflat_selector.py +280 -0
  163. tomwer/gui/reconstruction/axis/CompareImages.py +1 -1
  164. tomwer/gui/reconstruction/axis/axis.py +10 -6
  165. tomwer/gui/reconstruction/axis/radioaxis.py +14 -6
  166. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +2 -0
  167. tomwer/gui/reconstruction/darkref/darkrefwidget.py +4 -4
  168. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +3 -1
  169. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +34 -33
  170. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +1 -1
  171. tomwer/gui/reconstruction/normalization/intensity.py +5 -5
  172. tomwer/gui/reconstruction/saaxis/corrangeselector.py +1 -0
  173. tomwer/gui/reconstruction/saaxis/saaxis.py +6 -6
  174. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +6 -6
  175. tomwer/gui/reconstruction/scores/scoreplot.py +6 -4
  176. tomwer/gui/samplemoved/__init__.py +2 -2
  177. tomwer/gui/stackplot.py +18 -7
  178. tomwer/gui/stacks.py +2 -2
  179. tomwer/gui/stitching/stitchandbackground.py +2 -2
  180. tomwer/gui/stitching/stitching.py +1 -1
  181. tomwer/gui/stitching/stitching_raw.py +1 -1
  182. tomwer/gui/utils/__init__.py +1 -85
  183. tomwer/gui/utils/illustrations.py +1 -1
  184. tomwer/gui/utils/inputwidget.py +41 -36
  185. tomwer/gui/utils/slider.py +2 -2
  186. tomwer/gui/utils/utils.py +93 -0
  187. tomwer/gui/visualization/dataviewer.py +8 -5
  188. tomwer/gui/visualization/diffviewer/diffviewer.py +4 -4
  189. tomwer/gui/visualization/reconstructionparameters.py +26 -6
  190. tomwer/gui/visualization/sinogramviewer.py +7 -1
  191. tomwer/gui/visualization/test/test_reconstruction_parameters.py +2 -4
  192. tomwer/gui/visualization/volumeviewer.py +2 -0
  193. tomwer/resources/__init__.py +55 -43
  194. tomwer/resources/gui/icons/compose.png +0 -0
  195. tomwer/resources/gui/icons/compose.svg +75 -0
  196. tomwer/synctools/datatransfert.py +3 -1
  197. tomwer/synctools/stacks/edit/darkflatpatch.py +39 -34
  198. tomwer/synctools/stacks/edit/imagekeyeditor.py +8 -27
  199. tomwer/synctools/stacks/processingstack.py +45 -9
  200. tomwer/synctools/stacks/reconstruction/axis.py +6 -5
  201. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -0
  202. tomwer/synctools/stacks/reconstruction/lamino.py +3 -3
  203. tomwer/synctools/stacks/reconstruction/nabu.py +49 -140
  204. tomwer/synctools/stacks/reconstruction/normalization.py +1 -0
  205. tomwer/synctools/stacks/reconstruction/saaxis.py +19 -33
  206. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +16 -32
  207. tomwer/synctools/test/test_darkRefs.py +19 -10
  208. tomwer/synctools/test/test_foldertransfer.py +7 -7
  209. tomwer/third_party/nabu/preproc/phase.py +6 -8
  210. tomwer/third_party/nabu/utils.py +2 -3
  211. tomwer/version.py +1 -1
  212. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/METADATA +15 -54
  213. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/RECORD +219 -192
  214. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/WHEEL +1 -1
  215. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/entry_points.txt +3 -3
  216. /tomwer-1.2.0a1-py3.9-nspkg.pth → /tomwer-1.2.0a3-py3.11-nspkg.pth +0 -0
  217. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/LICENSE +0 -0
  218. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/namespace_packages.txt +0 -0
  219. {tomwer-1.2.0a1.dist-info → tomwer-1.2.0a3.dist-info}/top_level.txt +0 -0
@@ -39,8 +39,13 @@ from nabu.pipeline.config import generate_nabu_configfile
39
39
  from nabu.pipeline.fullfield.nabu_config import (
40
40
  nabu_config as nabu_fullfield_default_config,
41
41
  )
42
- from processview.core.manager.manager import ProcessManager
42
+ from processview.core.manager.manager import ProcessManager, DatasetState
43
+ from processview.core.superviseprocess import SuperviseProcess
44
+
43
45
  from silx.io.utils import h5py_read_dataset
46
+ from silx.utils.deprecation import deprecated_warning
47
+
48
+ from tomoscan.io import HDF5File
44
49
 
45
50
  from tomwer.core.cluster.cluster import SlurmClusterConfiguration
46
51
  from tomwer.core.futureobject import FutureTomwerObject
@@ -52,6 +57,8 @@ from tomwer.core.scan.scanfactory import ScanFactory
52
57
  from tomwer.core.utils.scanutils import data_identifier_to_scan
53
58
  from tomwer.io.utils.h5pyutils import EntryReader
54
59
  from tomwer.utils import docstring
60
+ from tomwer.core.utils import concatenate_dict
61
+ from tomwer.io.utils import format_stderr_stdout
55
62
 
56
63
  from . import settings, utils
57
64
  from .nabucommon import ResultsLocalRun, ResultSlurmRun, _NabuBaseReconstructor
@@ -93,23 +100,23 @@ def run_volume_reconstruction(
93
100
  else:
94
101
  target = Target.SLURM
95
102
 
96
- # beam shape is not directly used by nabu (uses ctf_geometry directly)
97
- config.get("phase", {}).pop("beam_shape", None)
98
-
99
- config_volume = copy.copy(config)
100
- config_nabu_slices = copy.deepcopy(scan.nabu_recons_params)
101
- if "tomwer_slices" in config_nabu_slices:
102
- del config_nabu_slices["tomwer_slices"]
103
-
104
- if "phase" in config_nabu_slices and "delta_beta" in config_nabu_slices["phase"]:
105
- pag_dbs = config_nabu_slices["phase"]["delta_beta"]
106
- if isinstance(pag_dbs, str):
107
- pag_dbs = utils.retrieve_lst_of_value_from_str(pag_dbs, type_=float)
108
- if len(pag_dbs) > 1:
109
- raise ValueError(
110
- "Several value of delta / beta found for volume reconstruction"
111
- )
112
- scan.clear_latest_vol_reconstructions()
103
+ # # beam shape is not directly used by nabu (uses ctf_geometry directly)
104
+ # config.get("phase", {}).pop("beam_shape", None)
105
+
106
+ # config_volume = copy.copy(config)
107
+ # config_nabu_slices = copy.deepcopy(scan.nabu_recons_params)
108
+ # if "tomwer_slices" in config_nabu_slices:
109
+ # del config_nabu_slices["tomwer_slices"]
110
+
111
+ # if "phase" in config_nabu_slices and "delta_beta" in config_nabu_slices["phase"]:
112
+ # pag_dbs = config_nabu_slices["phase"]["delta_beta"]
113
+ # if isinstance(pag_dbs, str):
114
+ # pag_dbs = utils.retrieve_lst_of_value_from_str(pag_dbs, type_=float)
115
+ # if len(pag_dbs) > 1:
116
+ # raise ValueError(
117
+ # "Several value of delta / beta found for volume reconstruction"
118
+ # )
119
+ # scan.clear_latest_vol_reconstructions()
113
120
 
114
121
  if process_id is not None:
115
122
  try:
@@ -121,8 +128,7 @@ def run_volume_reconstruction(
121
128
 
122
129
  volume_reconstructor = VolumeRunner(
123
130
  scan=scan,
124
- config_volume=config_volume,
125
- config_slices=config_nabu_slices,
131
+ config_nabu=config,
126
132
  cluster_config=cluster_config,
127
133
  dry_run=dry_run,
128
134
  target=target,
@@ -171,6 +177,7 @@ def run_volume_reconstruction(
171
177
  if res is not None
172
178
  else []
173
179
  )
180
+
174
181
  return succeed, stdouts, stderrs, configs, future_tomo_obj
175
182
 
176
183
 
@@ -185,8 +192,7 @@ class VolumeRunner(_NabuBaseReconstructor):
185
192
  def __init__(
186
193
  self,
187
194
  scan: TomwerScanBase,
188
- config_volume: dict,
189
- config_slices: dict,
195
+ config_nabu,
190
196
  cluster_config: Optional[dict],
191
197
  dry_run: bool,
192
198
  target: Target,
@@ -199,58 +205,44 @@ class VolumeRunner(_NabuBaseReconstructor):
199
205
  cluster_config=cluster_config,
200
206
  process_name=process_name,
201
207
  )
202
- self._config_volume = config_volume
203
- self._config_slices = config_slices
208
+ self._config = config_nabu
204
209
 
205
210
  @property
206
- def config_volume(self):
207
- return self._config_volume
208
-
209
- @property
210
- def config_slices(self):
211
- return self._config_slices
211
+ def configuration(self):
212
+ return self._config
212
213
 
213
214
  @docstring(_NabuBaseReconstructor)
214
215
  def run(self) -> Iterable:
215
216
  dataset_params = self.scan.get_nabu_dataset_info()
216
- if "dataset" in self._config_slices:
217
- dataset_params.update(self._config_slices["dataset"])
218
- self._config_slices["dataset"] = dataset_params
219
- self._config_slices["resources"] = utils.get_nabu_resources_desc(
217
+ if "dataset" in self.configuration:
218
+ dataset_params.update(self.configuration["dataset"])
219
+ self.configuration["dataset"] = dataset_params
220
+ self.configuration["resources"] = utils.get_nabu_resources_desc(
220
221
  scan=self.scan, workers=1, method="local"
221
222
  )
222
223
 
223
224
  # force overwrite results
224
- if "output" not in self.config_slices:
225
- self._config_slices["output"] = {}
226
- config_slices, extra_opts = self._treateOutputConfig(
227
- self.config_slices, self.config_volume
228
- )
225
+ if "output" not in self.configuration:
226
+ self.configuration["output"] = {}
227
+ config_slices, cfg_folder = self._treateOutputConfig(self.configuration)
228
+
229
+ # force overwriting results
229
230
  config_slices["output"].update({"overwrite_results": 1})
230
231
 
231
232
  # check and clamp `start_z` and `end_z`
232
- if "reconstruction" in self.config_slices:
233
+ if "reconstruction" in self.configuration:
233
234
  for key in ("start_z", "end_z"):
234
- value = self.config_slices["reconstruction"].get(key)
235
+ value = config_slices["reconstruction"].get(key)
235
236
  if value is None:
236
237
  continue
237
238
 
238
239
  value = int(value)
239
- if value < 0:
240
- _logger.warning(f"{key} < 0 found. Set it to 0 (vs value)")
241
- value = 0
242
240
  if self.scan.dim_2 is not None and value >= self.scan.dim_2:
243
241
  _logger.warning(
244
242
  f"{key} > max_size (radio height: {self.scan.dim_2}). Set it to -1 (maximum)"
245
243
  )
246
244
  value = -1
247
- self._config_slices["reconstruction"][key] = value
248
-
249
- cfg_folder = os.path.join(
250
- config_slices["output"]["location"], settings.NABU_CFG_FILE_FOLDER
251
- )
252
- if not os.path.exists(cfg_folder):
253
- os.makedirs(cfg_folder)
245
+ config_slices["reconstruction"][key] = value
254
246
 
255
247
  name = (
256
248
  config_slices["output"]["file_prefix"] + settings.NABU_CONFIG_FILE_EXTENSION
@@ -260,6 +252,9 @@ class VolumeRunner(_NabuBaseReconstructor):
260
252
  conf_file = os.path.join(cfg_folder, name)
261
253
  _logger.info(f"{self.scan}: create {conf_file}")
262
254
 
255
+ # make sure output location exists
256
+ os.makedirs(config_slices["output"]["location"], exist_ok=True)
257
+
263
258
  # add some tomwer metadata and save the configuration
264
259
  # note: for now the section is ignored by nabu but shouldn't stay that way
265
260
  with utils.TomwerInfo(config_slices) as config_to_dump:
@@ -269,20 +264,19 @@ class VolumeRunner(_NabuBaseReconstructor):
269
264
  config=config_to_dump,
270
265
  options_level="advanced",
271
266
  )
272
-
273
- return tuple(
274
- [
275
- self._process_config(
276
- config_to_dump=config_to_dump,
277
- config_file=conf_file,
278
- start_z=config_to_dump["reconstruction"]["start_z"],
279
- end_z=config_to_dump["reconstruction"]["end_z"],
280
- info="nabu volume reconstruction",
281
- file_format=config_slices["output"]["file_format"],
282
- process_name=self.process_name,
283
- ),
284
- ]
285
- )
267
+ return tuple(
268
+ [
269
+ self._process_config(
270
+ config_to_dump=config_to_dump,
271
+ config_file=conf_file,
272
+ start_z=config_to_dump["reconstruction"]["start_z"],
273
+ end_z=config_to_dump["reconstruction"]["end_z"],
274
+ info="nabu volume reconstruction",
275
+ file_format=config_slices["output"]["file_format"],
276
+ process_name=self.process_name,
277
+ )
278
+ ]
279
+ )
286
280
 
287
281
  @docstring(_NabuBaseReconstructor)
288
282
  def _get_futures_slurm_callback(self, config_to_dump) -> tuple:
@@ -316,26 +310,27 @@ class VolumeRunner(_NabuBaseReconstructor):
316
310
 
317
311
  return (CallBack(callback, self.scan),)
318
312
 
319
- def _treateOutputConfig(self, config_s, config_v) -> tuple:
313
+ def _treateOutputConfig(self, config) -> tuple:
320
314
  """
321
315
 
322
316
  :return: (nabu config dict, nabu extra options)
323
317
  """
324
- config_s = copy.deepcopy(config_s)
325
- config_s = super()._treateOutputSliceConfig(config_s)
318
+ config = copy.deepcopy(config)
319
+ config, nabu_cfg_folder = super()._treateOutputSliceConfig(config)
320
+ os.makedirs(config["output"]["location"], exist_ok=True)
321
+
326
322
  # adapt config_s to specific volume treatment
327
- if "postproc" in config_v:
328
- config_s["postproc"] = config_v["postproc"]
323
+ if "postproc" in config:
324
+ config["postproc"] = config["postproc"]
329
325
 
330
- extra_opts = config_v
331
- if "start_z" in extra_opts:
332
- config_s["reconstruction"]["start_z"] = extra_opts["start_z"]
333
- del extra_opts["start_z"]
334
- if "end_z" in extra_opts:
335
- config_s["reconstruction"]["end_z"] = extra_opts["end_z"]
336
- del extra_opts["end_z"]
326
+ if "start_z" in config:
327
+ config["reconstruction"]["start_z"] = config["start_z"]
328
+ del config["start_z"]
329
+ if "end_z" in config:
330
+ config["reconstruction"]["end_z"] = config["end_z"]
331
+ del config["end_z"]
337
332
 
338
- return config_s, extra_opts
333
+ return config, nabu_cfg_folder
339
334
 
340
335
  @docstring(_NabuBaseReconstructor)
341
336
  def _get_file_basename_reconstruction(self, pag, db, ctf):
@@ -354,8 +349,16 @@ class VolumeRunner(_NabuBaseReconstructor):
354
349
  if isinstance(self.scan, HDF5TomoScan):
355
350
  basename, _ = os.path.splitext(self.scan.master_file)
356
351
  basename = os.path.basename(basename)
352
+ try:
353
+ # if there is more than one entry in the file append the entry name to the file basename
354
+ with HDF5File(self.scan.master_file, mode="r") as h5f:
355
+ if len(h5f.keys()) > 1:
356
+ basename = "_".join((basename, self.scan.entry.strip("/")))
357
+ except Exception:
358
+ pass
357
359
  else:
358
360
  basename = os.path.basename(self.scan.path)
361
+
359
362
  if pag:
360
363
  return "_".join((basename + "pag", "db" + str(db).zfill(4), "vol"))
361
364
  elif ctf:
@@ -364,15 +367,27 @@ class VolumeRunner(_NabuBaseReconstructor):
364
367
  return "_".join((basename, "vol"))
365
368
 
366
369
 
367
- class NabuVolume(
370
+ class NabuVolumeTask(
368
371
  Task,
369
- input_names=("data",),
370
- optional_input_names=("nabu_params",),
371
- output_names=("data", "volumes"),
372
+ SuperviseProcess,
373
+ input_names=("data", "nabu_params"),
374
+ output_names=("data", "volumes", "future_tomo_obj"),
375
+ optional_input_names=(
376
+ "dry_run",
377
+ "nabu_extra_params", # some parameter that must update 'nabu_params' before launching the reconstruction. Such as z range...
378
+ "serialize_output_data",
379
+ ),
372
380
  ):
373
381
  def __init__(
374
- self, varinfo=None, inputs=None, node_id=None, node_attrs=None, execinfo=None
382
+ self,
383
+ process_id=None,
384
+ varinfo=None,
385
+ inputs=None,
386
+ node_id=None,
387
+ node_attrs=None,
388
+ execinfo=None,
375
389
  ):
390
+ SuperviseProcess.__init__(self, process_id=process_id)
376
391
  Task.__init__(
377
392
  self,
378
393
  varinfo=varinfo,
@@ -382,15 +397,40 @@ class NabuVolume(
382
397
  execinfo=execinfo,
383
398
  )
384
399
  self._dry_run = inputs.get("dry_run", False)
400
+ self._current_processing = None
385
401
 
386
402
  def run(self):
387
403
  scan = data_identifier_to_scan(self.inputs.data)
388
404
  if scan is None:
389
405
  self.outputs.data = None
390
406
  return
391
- nabu_params = self.inputs.nabu_params
392
- if nabu_params is None:
393
- nabu_params = scan.nabu_params
407
+
408
+ # update scan reconstruction parameters used
409
+ nabu_params = concatenate_dict(
410
+ copy.deepcopy(self.inputs.nabu_params),
411
+ self.get_input_value("nabu_extra_params", dict()),
412
+ )
413
+ scan.nabu_recons_params = nabu_params
414
+ scan.clear_latest_vol_reconstructions()
415
+
416
+ cluster_config = nabu_params.pop("cluster_config", None)
417
+ if cluster_config == {}:
418
+ cluster_config = None
419
+ elif isinstance(cluster_config, SlurmClusterConfiguration):
420
+ cluster_config = cluster_config.to_dict()
421
+
422
+ if "tomwer_slices" in nabu_params:
423
+ del nabu_params["tomwer_slices"]
424
+
425
+ if "phase" in nabu_params and "delta_beta" in nabu_params["phase"]:
426
+ pag_dbs = nabu_params["phase"]["delta_beta"]
427
+ if isinstance(pag_dbs, str):
428
+ pag_dbs = utils.retrieve_lst_of_value_from_str(pag_dbs, type_=float)
429
+ if len(pag_dbs) > 1:
430
+ raise ValueError(
431
+ "Several value of delta / beta found for volume reconstruction"
432
+ )
433
+
394
434
  if isinstance(scan, TomwerScanBase):
395
435
  scan = scan
396
436
  elif isinstance(scan, dict):
@@ -398,30 +438,94 @@ class NabuVolume(
398
438
  else:
399
439
  raise ValueError(f"input type {scan} is not managed")
400
440
 
401
- if scan.nabu_recons_params is None:
402
- # if reconstruction parameters are not registered, register them
403
- scan.nabu_recons_params = nabu_params
441
+ if cluster_config is None:
442
+ target = Target.LOCAL
443
+ else:
444
+ target = Target.SLURM
404
445
 
405
- run_volume_reconstruction(
406
- scan=scan,
407
- config=self.get_configuration() or {},
408
- dry_run=self.dry_run,
409
- )
410
- # register result
411
- entry = "entry"
412
- if isinstance(scan, HDF5TomoScan):
413
- entry = scan.entry
414
- with scan.acquire_process_file_lock():
415
- self.register_process(
416
- process_file=scan.process_file,
417
- entry=entry,
418
- configuration=self.get_configuration(),
419
- results={},
420
- process_index=scan.pop_process_index(),
421
- overwrite=True,
446
+ state = None
447
+ details = None
448
+ try:
449
+ self._current_processing = VolumeRunner(
450
+ scan=scan,
451
+ config_nabu=nabu_params,
452
+ cluster_config=cluster_config,
453
+ dry_run=self._dry_run,
454
+ target=target,
455
+ process_name=self.program_name,
422
456
  )
423
- self.outputs.data = scan
457
+ res = self._current_processing.run()[0]
458
+ except Exception as e:
459
+ _logger.error(f"Failed to process {scan}. Error is {e}")
460
+ state = DatasetState.FAILED
461
+ details = None
462
+ future_tomo_obj = None
463
+ else:
464
+ # tag latest reconstructions
465
+ if isinstance(res, ResultsLocalRun) and res.results_urls is not None:
466
+ scan.set_latest_vol_reconstructions(res.results_urls)
467
+ # create future if needed
468
+ if isinstance(res, ResultSlurmRun):
469
+ future_tomo_obj = FutureTomwerObject(
470
+ tomo_obj=scan,
471
+ futures=tuple(res.future_slurm_jobs),
472
+ process_requester_id=self.process_id,
473
+ )
474
+
475
+ else:
476
+ future_tomo_obj = None
477
+
478
+ if self._cancelled:
479
+ state = DatasetState.CANCELLED
480
+ details = "cancelled by user"
481
+ _logger.info(f"Slices computation for {scan} cancelled")
482
+ else:
483
+ succeed = res.success
484
+ stdouts = (
485
+ [
486
+ res.std_out,
487
+ ]
488
+ if hasattr(res, "std_out")
489
+ else []
490
+ )
491
+ stderrs = (
492
+ [
493
+ res.std_err,
494
+ ]
495
+ if hasattr(res, "std_err")
496
+ else []
497
+ )
498
+
499
+ if not succeed:
500
+ mess = f"Volume computed for {scan} failed."
501
+ _logger.processFailed(mess)
502
+ state = DatasetState.FAILED
503
+ else:
504
+ mess = f"Volume computed for {scan}."
505
+ _logger.processSucceed(mess)
506
+ state = DatasetState.SUCCEED
507
+
508
+ # format stderr and stdout
509
+ elmts = [
510
+ format_stderr_stdout(stderr=stderr, stdout=stdout)
511
+ for stderr, stdout in zip(stderrs, stdouts)
512
+ ]
513
+ elmts.insert(0, mess)
514
+ details = "\n".join(elmts)
515
+ finally:
516
+ ProcessManager().notify_dataset_state(
517
+ dataset=scan,
518
+ process=self,
519
+ state=state,
520
+ details=details,
521
+ )
522
+
523
+ if self.get_input_value("serialize_output_data", True):
524
+ self.outputs.data = scan.to_dict()
525
+ else:
526
+ self.outputs.data = scan
424
527
  self.outputs.volumes = scan.latest_vol_reconstructions
528
+ self.outputs.future_tomo_obj = future_tomo_obj
425
529
 
426
530
  def set_configuration(self, configuration: dict) -> None:
427
531
  Task.set_configuration(self, configuration=configuration)
@@ -446,7 +550,35 @@ class NabuVolume(
446
550
  @staticmethod
447
551
  def retrieve_last_relative_cor(scan):
448
552
  with EntryReader(scan.process_file_url) as h5f:
449
- latest_nabu_node = Task.get_most_recent_process(h5f, NabuVolume)
553
+ latest_nabu_node = Task.get_most_recent_process(h5f, NabuVolumeTask)
450
554
  path = "configuration/reconstruction/rotation_axis_position"
451
555
  if path in latest_nabu_node:
452
556
  return h5py_read_dataset(latest_nabu_node[path])
557
+
558
+ def cancel(self):
559
+ """
560
+ stop current processing
561
+ """
562
+ self._cancelled = True
563
+ if self._current_processing is not None:
564
+ self._current_processing.cancel()
565
+
566
+
567
+ class NabuVolume(NabuVolumeTask):
568
+ def __init__(
569
+ self,
570
+ process_id=None,
571
+ varinfo=None,
572
+ inputs=None,
573
+ node_id=None,
574
+ node_attrs=None,
575
+ execinfo=None,
576
+ ):
577
+ deprecated_warning(
578
+ name="tomwer.core.process.reconstruction.nabu.nabuvolume.NabuVolume",
579
+ type_="class",
580
+ reason="improve readibility",
581
+ since_version="1.2",
582
+ replacement="NabuVolumeTask",
583
+ )
584
+ super().__init__(process_id, varinfo, inputs, node_id, node_attrs, execinfo)
@@ -159,7 +159,7 @@ def get_recons_volume_identifier(
159
159
  if file_format in ("hdf5", "h5", "hdf"):
160
160
  if slice_index is not None:
161
161
  # case of a single hdf5 file
162
- file_name = "_".join((file_prefix, str(slice_index).zfill(4)))
162
+ file_name = "_".join((file_prefix, str(slice_index).zfill(6)))
163
163
  else:
164
164
  file_name = file_prefix
165
165
  file_name = ".".join((file_name, file_format))
@@ -179,23 +179,13 @@ def get_recons_volume_identifier(
179
179
  elif file_format in ("vol", "raw"):
180
180
  if slice_index is not None:
181
181
  # case of a single hdf5 file
182
- file_name = "_".join((file_prefix, str(slice_index).zfill(4)))
182
+ file_name = "_".join((file_prefix, str(slice_index).zfill(6)))
183
183
  else:
184
184
  file_name = file_prefix
185
- # on 2022.3.x the location seems invalid for nabu. Add this second location to
186
- # handle most of the case.
187
- fix_nabu = os.path.join(file_prefix, file_name)
188
-
189
185
  file_name = ".".join((file_name, file_format))
190
186
  file_path = os.path.join(location, file_name)
191
187
 
192
- file_name_fix_nabu = ".".join((fix_nabu, file_format))
193
- file_path_fix_nabu = os.path.join(location, file_name_fix_nabu)
194
-
195
- volumes = (
196
- RawVolume(file_path=file_path_fix_nabu),
197
- RawVolume(file_path=file_path),
198
- )
188
+ volumes = (RawVolume(file_path=file_path),)
199
189
  elif file_format in ("jp2", "jp2k", "edf", "tiff"):
200
190
  if file_format in ("jp2k", "jp2"):
201
191
  constructor = JP2KVolume
@@ -205,31 +195,15 @@ def get_recons_volume_identifier(
205
195
  constructor = TIFFVolume
206
196
  else:
207
197
  raise NotImplementedError
208
- output_extension = file_format
209
198
  basename = file_prefix
199
+ file_path = location
200
+ volumes = (
201
+ constructor(
202
+ folder=location,
203
+ volume_basename=basename,
204
+ ),
205
+ )
210
206
 
211
- if slice_index is None and start_z is None and end_z is None:
212
- # if this is a single slice
213
- file_path = ".".join(
214
- (
215
- os.path.join(location, file_prefix, file_prefix),
216
- output_extension,
217
- )
218
- )
219
- # TODO: in the future we might want to provide data_slice according to z values
220
- volumes = (
221
- constructor(
222
- folder=os.path.join(location, file_prefix),
223
- volume_basename=basename,
224
- ),
225
- )
226
- else:
227
- volumes = (
228
- constructor(
229
- folder=os.path.join(location, file_prefix),
230
- volume_basename=basename,
231
- ),
232
- )
233
207
  # case of the multitiff. Not handled by tomwer
234
208
  # elif file_format == "tiff":
235
209
  # # for single frame tiff nabu uses another convention by saving it 'directly'.
@@ -55,10 +55,14 @@ _logger = logging.getLogger(__name__)
55
55
 
56
56
 
57
57
  class SinoNormalizationTask(
58
- Task, SuperviseProcess, input_names=("data",), output_names=("data",)
58
+ Task,
59
+ SuperviseProcess,
60
+ input_names=("data",),
61
+ output_names=("data",),
62
+ optional_input_names=("serialize_output_data",),
59
63
  ):
60
64
  """
61
- Task to define the normalization to apply to a sinogram
65
+ Task to define the normalization to apply to a sinogram before reconstructing it with nabu
62
66
  """
63
67
 
64
68
  def __init__(
@@ -148,7 +152,10 @@ class SinoNormalizationTask(
148
152
  else:
149
153
  final_norm_info.update({"tomwer_processing_res_code": True})
150
154
  scan.intensity_normalization.set_extra_infos(final_norm_info)
151
- self.outputs.data = scan
155
+ if self.get_input_value("serialize_output_data", True):
156
+ self.outputs.data = scan.to_dict()
157
+ else:
158
+ self.outputs.data = scan
152
159
 
153
160
  def _compute_from_manual_roi(self, scan):
154
161
  params = SinoNormalizationParams.from_dict(self.get_configuration())
@@ -1,2 +1,3 @@
1
1
  from .params import SAAxisParams # noqa F401
2
2
  from .saaxis import SAAxisProcess # noqa F401
3
+ from .saaxis import SAAxisTask # noqa F401