tomwer 1.2.9__py3-none-any.whl → 1.3.0a0__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 (253) hide show
  1. orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
  2. orangecontrib/tomwer/widgets/__init__.py +1 -0
  3. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
  4. orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
  5. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
  6. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +11 -5
  7. orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
  8. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +31 -18
  9. orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +14 -7
  10. orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
  11. orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
  12. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
  13. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +4 -4
  14. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
  15. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
  16. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +2 -0
  17. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
  18. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
  19. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
  20. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
  21. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
  22. orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
  23. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  24. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
  25. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
  26. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
  27. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  28. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
  29. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
  30. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
  31. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +9 -8
  32. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +3 -3
  33. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +179 -169
  34. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
  35. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
  36. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +7 -13
  37. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +7 -17
  38. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +3 -4
  39. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
  40. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
  41. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
  42. tomwer/__main__.py +11 -58
  43. tomwer/app/canvas.py +8 -0
  44. tomwer/app/canvas_launcher/config.py +13 -11
  45. tomwer/app/darkref.py +1 -1
  46. tomwer/app/darkrefpatch.py +1 -1
  47. tomwer/app/imagekeyeditor.py +5 -5
  48. tomwer/app/imagekeyupgrader.py +5 -5
  49. tomwer/app/intensitynormalization.py +2 -2
  50. tomwer/app/radiostack.py +2 -2
  51. tomwer/app/zstitching.py +74 -3
  52. tomwer/core/cluster/cluster.py +26 -0
  53. tomwer/core/log/logger.py +7 -5
  54. tomwer/core/process/conditions/filters.py +1 -1
  55. tomwer/core/process/control/datalistener/datalistener.py +3 -3
  56. tomwer/core/process/control/nxtomoconcatenate.py +13 -13
  57. tomwer/core/process/control/nxtomomill.py +83 -25
  58. tomwer/core/process/control/scantransfer.py +11 -10
  59. tomwer/core/process/control/scanvalidator.py +3 -2
  60. tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
  61. tomwer/core/process/control/test/test_email.py +4 -4
  62. tomwer/core/process/control/test/test_h52nx_process.py +59 -7
  63. tomwer/core/process/control/test/test_volume_link.py +64 -64
  64. tomwer/core/process/control/timer.py +1 -1
  65. tomwer/core/process/control/volumesymlink.py +200 -200
  66. tomwer/core/process/edit/darkflatpatch.py +6 -6
  67. tomwer/core/process/edit/imagekeyeditor.py +17 -18
  68. tomwer/core/process/icat/__init__.py +0 -0
  69. tomwer/core/process/icat/createscreenshots.py +100 -0
  70. tomwer/core/process/icat/gallery.py +377 -0
  71. tomwer/core/process/icat/icatbase.py +36 -0
  72. tomwer/core/process/icat/publish.py +228 -0
  73. tomwer/core/process/icat/screenshots.py +26 -0
  74. tomwer/core/process/output.py +52 -0
  75. tomwer/core/process/reconstruction/axis/axis.py +17 -10
  76. tomwer/core/process/reconstruction/axis/mode.py +4 -0
  77. tomwer/core/process/reconstruction/axis/params.py +9 -4
  78. tomwer/core/process/reconstruction/darkref/darkrefs.py +8 -6
  79. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +1 -1
  80. tomwer/core/process/reconstruction/darkref/params.py +1 -1
  81. tomwer/core/process/reconstruction/lamino/tofu.py +4 -4
  82. tomwer/core/process/reconstruction/nabu/castvolume.py +1 -1
  83. tomwer/core/process/reconstruction/nabu/helical.py +9 -5
  84. tomwer/core/process/reconstruction/nabu/nabucommon.py +32 -62
  85. tomwer/core/process/reconstruction/nabu/nabuscores.py +387 -61
  86. tomwer/core/process/reconstruction/nabu/nabuslices.py +33 -21
  87. tomwer/core/process/reconstruction/nabu/nabuvolume.py +37 -14
  88. tomwer/core/process/reconstruction/nabu/settings.py +2 -2
  89. tomwer/core/process/reconstruction/nabu/utils.py +129 -24
  90. tomwer/core/process/reconstruction/output.py +108 -0
  91. tomwer/core/process/reconstruction/saaxis/saaxis.py +233 -263
  92. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +140 -86
  93. tomwer/core/process/reconstruction/scores/params.py +4 -1
  94. tomwer/core/process/reconstruction/scores/scores.py +13 -0
  95. tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
  96. tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
  97. tomwer/core/process/reconstruction/test/test_darkref_copy.py +3 -3
  98. tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
  99. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
  100. tomwer/core/process/stitching/nabustitcher.py +2 -2
  101. tomwer/core/process/test/test_axis.py +6 -6
  102. tomwer/core/process/test/test_dark_and_flat.py +10 -7
  103. tomwer/core/process/test/test_data_transfer.py +7 -6
  104. tomwer/core/process/test/test_nabu.py +4 -4
  105. tomwer/core/process/test/test_normalization.py +2 -2
  106. tomwer/core/scan/edfscan.py +4 -1
  107. tomwer/core/scan/hdf5scan.py +19 -500
  108. tomwer/core/scan/nxtomoscan.py +532 -0
  109. tomwer/core/scan/scanbase.py +42 -20
  110. tomwer/core/scan/scanfactory.py +13 -13
  111. tomwer/core/scan/test/test_future_scan.py +2 -2
  112. tomwer/core/scan/test/test_h5.py +12 -10
  113. tomwer/core/scan/test/test_process_registration.py +2 -2
  114. tomwer/core/scan/test/test_scan.py +4 -3
  115. tomwer/core/settings.py +20 -0
  116. tomwer/core/test/test_scanutils.py +8 -7
  117. tomwer/core/test/test_utils.py +33 -26
  118. tomwer/core/utils/__init__.py +0 -466
  119. tomwer/core/utils/deprecation.py +1 -1
  120. tomwer/core/utils/dictutils.py +14 -0
  121. tomwer/core/utils/lbsram.py +35 -0
  122. tomwer/core/utils/nxtomoutils.py +1 -1
  123. tomwer/core/utils/scanutils.py +6 -6
  124. tomwer/core/utils/spec.py +263 -0
  125. tomwer/core/volume/volumefactory.py +2 -2
  126. tomwer/gui/cluster/slurm.py +260 -60
  127. tomwer/gui/cluster/test/test_cluster.py +13 -0
  128. tomwer/gui/cluster/test/test_supervisor.py +2 -2
  129. tomwer/gui/configuration/__init__.py +0 -0
  130. tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
  131. tomwer/gui/configuration/level.py +22 -0
  132. tomwer/gui/control/actions.py +54 -0
  133. tomwer/gui/control/datalist.py +78 -16
  134. tomwer/gui/control/datalistener.py +4 -16
  135. tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
  136. tomwer/gui/control/history.py +2 -2
  137. tomwer/gui/control/observations.py +2 -2
  138. tomwer/gui/control/reducedarkflatselector.py +1 -1
  139. tomwer/gui/control/selectorwidgetbase.py +36 -9
  140. tomwer/gui/control/serie/seriecreator.py +5 -22
  141. tomwer/gui/control/test/test_email.py +1 -1
  142. tomwer/gui/control/test/test_scanvalidator.py +6 -5
  143. tomwer/gui/control/test/test_single_tomo_obj.py +2 -2
  144. tomwer/gui/control/tomoobjdisplaymode.py +8 -0
  145. tomwer/gui/debugtools/datasetgenerator.py +3 -3
  146. tomwer/gui/edit/dkrfpatch.py +16 -22
  147. tomwer/gui/edit/imagekeyeditor.py +8 -11
  148. tomwer/gui/edit/nxtomoeditor.py +111 -44
  149. tomwer/gui/edit/nxtomowarmer.py +4 -4
  150. tomwer/gui/edit/test/test_dkrf_patch.py +7 -7
  151. tomwer/gui/edit/test/test_image_key_editor.py +3 -3
  152. tomwer/gui/edit/test/test_nx_editor.py +40 -16
  153. tomwer/gui/icat/__init__.py +0 -0
  154. tomwer/gui/icat/createscreenshots.py +80 -0
  155. tomwer/gui/icat/gallery.py +214 -0
  156. tomwer/gui/icat/publish.py +187 -0
  157. tomwer/gui/reconstruction/axis/axis.py +171 -57
  158. tomwer/gui/reconstruction/axis/radioaxis.py +80 -95
  159. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
  160. tomwer/gui/reconstruction/lamino/tofu/projections.py +1 -1
  161. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +3 -6
  162. tomwer/gui/reconstruction/nabu/castvolume.py +1 -1
  163. tomwer/gui/reconstruction/nabu/check.py +9 -9
  164. tomwer/gui/reconstruction/nabu/helical.py +29 -12
  165. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
  166. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +110 -33
  167. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +9 -12
  168. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +219 -29
  169. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +3 -6
  170. tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
  171. tomwer/gui/reconstruction/nabu/slices.py +6 -7
  172. tomwer/gui/reconstruction/nabu/volume.py +22 -10
  173. tomwer/gui/reconstruction/normalization/intensity.py +15 -23
  174. tomwer/gui/reconstruction/saaxis/corrangeselector.py +7 -23
  175. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
  176. tomwer/gui/reconstruction/saaxis/saaxis.py +7 -9
  177. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +2 -1
  178. tomwer/gui/reconstruction/scores/control.py +2 -9
  179. tomwer/gui/reconstruction/scores/scoreplot.py +11 -5
  180. tomwer/gui/reconstruction/test/test_axis.py +23 -12
  181. tomwer/gui/reconstruction/test/test_lamino.py +8 -3
  182. tomwer/gui/reconstruction/test/test_nabu.py +28 -9
  183. tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
  184. tomwer/gui/reconstruction/test/test_sadeltabeta.py +2 -2
  185. tomwer/gui/settings.py +5 -28
  186. tomwer/gui/stackplot.py +2 -5
  187. tomwer/gui/stitching/action.py +49 -0
  188. tomwer/gui/stitching/config/axisparams.py +7 -24
  189. tomwer/gui/stitching/config/output.py +10 -8
  190. tomwer/gui/stitching/config/positionoveraxis.py +22 -23
  191. tomwer/gui/stitching/normalization.py +117 -0
  192. tomwer/gui/stitching/stitchandbackground.py +4 -6
  193. tomwer/gui/stitching/stitching.py +265 -43
  194. tomwer/gui/stitching/stitching_preview.py +62 -5
  195. tomwer/gui/stitching/stitching_raw.py +2 -5
  196. tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
  197. tomwer/gui/utils/buttons.py +112 -29
  198. tomwer/gui/utils/inputwidget.py +33 -25
  199. tomwer/gui/utils/scandescription.py +4 -0
  200. tomwer/gui/utils/step.py +144 -0
  201. tomwer/gui/utils/unitsystem.py +2 -5
  202. tomwer/gui/utils/vignettes.py +176 -15
  203. tomwer/gui/visualization/dataviewer.py +1 -4
  204. tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
  205. tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
  206. tomwer/gui/visualization/scanoverview.py +1 -1
  207. tomwer/gui/visualization/sinogramviewer.py +1 -10
  208. tomwer/gui/visualization/test/test_diffviewer.py +3 -3
  209. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
  210. tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
  211. tomwer/gui/visualization/test/test_stacks.py +3 -3
  212. tomwer/gui/visualization/test/test_volumeviewer.py +2 -2
  213. tomwer/io/utils/raw_and_processed_data.py +84 -0
  214. tomwer/io/utils/tomoobj.py +4 -6
  215. tomwer/resources/gui/icons/ruler.png +0 -0
  216. tomwer/resources/gui/icons/ruler.svg +273 -0
  217. tomwer/resources/gui/icons/short_description.png +0 -0
  218. tomwer/resources/gui/icons/short_description.svg +58 -0
  219. tomwer/resources/gui/icons/url.png +0 -0
  220. tomwer/resources/gui/icons/url.svg +58 -0
  221. tomwer/synctools/stacks/edit/darkflatpatch.py +2 -2
  222. tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
  223. tomwer/synctools/stacks/reconstruction/axis.py +4 -4
  224. tomwer/synctools/stacks/reconstruction/castvolume.py +2 -2
  225. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +4 -10
  226. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  227. tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
  228. tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
  229. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
  230. tomwer/synctools/test/test_darkRefs.py +7 -58
  231. tomwer/synctools/test/test_foldertransfer.py +6 -6
  232. tomwer/synctools/utils/scanstages.py +6 -6
  233. tomwer/tests/conftest.py +34 -0
  234. tomwer/tests/datasets.py +13 -0
  235. tomwer/tests/test_scripts.py +92 -39
  236. tomwer/tests/utils.py +5 -0
  237. tomwer/version.py +3 -3
  238. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/METADATA +39 -39
  239. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/RECORD +248 -209
  240. tomwer/resources/gui/icons/esrf_1.svg +0 -307
  241. tomwer/resources/gui/icons/triangle.svg +0 -80
  242. tomwer/synctools/test/test_scanstages.py +0 -162
  243. tomwer/tests/utils/__init__.py +0 -247
  244. tomwer/tests/utils/utilstest.py +0 -220
  245. /tomwer/app/{saaxis.py → multicor.py} +0 -0
  246. /tomwer/app/{sadeltabeta.py → multipag.py} +0 -0
  247. /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
  248. /tomwer-1.2.9-py3.11-nspkg.pth → /tomwer-1.3.0a0-py3.11-nspkg.pth +0 -0
  249. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/LICENSE +0 -0
  250. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/WHEEL +0 -0
  251. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/entry_points.txt +0 -0
  252. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/namespace_packages.txt +0 -0
  253. {tomwer-1.2.9.dist-info → tomwer-1.3.0a0.dist-info}/top_level.txt +0 -0
@@ -1,36 +1,3 @@
1
- # coding: utf-8
2
- ###########################################################################
3
- # Copyright (C) 2016 European Synchrotron Radiation Facility
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
- #
23
- #############################################################################
24
-
25
- """contain utils for score process
26
- """
27
-
28
- __authors__ = [
29
- "H.Payno",
30
- ]
31
- __license__ = "MIT"
32
- __date__ = "28/10/2021"
33
-
34
1
  try:
35
2
  from nabu.pipeline.fullfield.reconstruction import ( # noqa F401
36
3
  FullFieldReconstructor,
@@ -50,12 +17,15 @@ else:
50
17
  import logging
51
18
  import os
52
19
  from copy import deepcopy
20
+ import subprocess
53
21
  from typing import Iterable, Optional, Union
54
22
 
55
23
  from nabu.pipeline.fullfield.nabu_config import (
56
24
  nabu_config as nabu_fullfield_default_config,
57
25
  )
58
26
  from processview.core.manager.manager import ProcessManager
27
+ from sluurp.job import SBatchScriptJob
28
+ from sluurp.executor import submit as submit_to_slurm_cluster
59
29
 
60
30
  from tomwer.core.futureobject import FutureTomwerObject
61
31
  from tomwer.core.process.reconstruction.nabu.nabuslices import (
@@ -63,15 +33,20 @@ from tomwer.core.process.reconstruction.nabu.nabuslices import (
63
33
  _NabuBaseReconstructor,
64
34
  generate_nabu_configfile,
65
35
  )
36
+ from tomwer.core.process.reconstruction.nabu.utils import (
37
+ slice_index_to_int,
38
+ get_nabu_multicor_file_prefix,
39
+ )
66
40
  from tomwer.core.process.reconstruction.nabu.target import Target
67
41
  from tomwer.core.progress import Progress
68
42
  from tomwer.core.scan.scanbase import TomwerScanBase
69
- from tomwer.core.utils.slurm import is_slurm_available
43
+ from tomwer.core.utils.slurm import get_slurm_script_name, is_slurm_available
70
44
  from tomwer.utils import docstring
45
+ from tomwer.core.process.reconstruction.nabu.utils import nabu_std_err_has_error
71
46
 
72
47
  from ..nabu import settings as nabu_settings
73
- from . import utils
74
- from .nabucommon import ResultsLocalRun, ResultSlurmRun, ResultsWithStd
48
+ from . import utils, settings
49
+ from .nabucommon import ResultsLocalRun, ResultSlurmRun, ResultsWithStd, ResultsRun
75
50
 
76
51
  _logger = logging.getLogger(__name__)
77
52
 
@@ -156,7 +131,7 @@ def run_nabu_one_slice_several_config(
156
131
  std_outs.append(res.std_out)
157
132
  std_errs.append(res.std_err)
158
133
  if isinstance(res, ResultsLocalRun):
159
- recons_urls[var_value] = res.results_urls
134
+ recons_urls[var_value] = res.results_identifiers
160
135
  if isinstance(res, ResultSlurmRun):
161
136
  future_tomo_obj = FutureTomwerObject(
162
137
  tomo_obj=scan,
@@ -167,6 +142,77 @@ def run_nabu_one_slice_several_config(
167
142
  return success, recons_urls, std_outs, std_errs, future_tomo_objs
168
143
 
169
144
 
145
+ def run_nabu_multicor(
146
+ scan: TomwerScanBase,
147
+ nabu_config: dict,
148
+ cors: tuple,
149
+ cluster_config: Optional[dict],
150
+ dry_run: bool,
151
+ slice_index: Union[int, str],
152
+ file_format: str,
153
+ process_id: Optional[int] = None,
154
+ instanciate_classes_only: bool = False,
155
+ output_file_prefix_pattern=None,
156
+ ):
157
+ if cluster_config in (None, {}):
158
+ target = Target.LOCAL
159
+ elif isinstance(cluster_config, dict):
160
+ if not is_slurm_available():
161
+ raise RuntimeError("Slurm computation requested but unvailable")
162
+ target = Target.SLURM
163
+ else:
164
+ raise TypeError(
165
+ f"cluster_config should be None or a dict not {type(cluster_config)}"
166
+ )
167
+
168
+ if process_id is not None:
169
+ try:
170
+ process_name = ProcessManager().get_process(process_id=process_id).name
171
+ except KeyError:
172
+ process_name = "unknow"
173
+ else:
174
+ process_name = ""
175
+
176
+ reconstructor = _ReconstructorMultiCor(
177
+ scan=scan,
178
+ nabu_config=nabu_config,
179
+ cors=cors,
180
+ slice_index=slice_index,
181
+ target=target,
182
+ dry_run=dry_run,
183
+ file_format=file_format,
184
+ cluster_config=cluster_config,
185
+ process_name=process_name,
186
+ output_file_prefix_pattern=output_file_prefix_pattern,
187
+ )
188
+ if instanciate_classes_only:
189
+ return reconstructor
190
+
191
+ try:
192
+ result = reconstructor.run()
193
+ except TimeoutError as e:
194
+ _logger.error(e)
195
+ return None
196
+ else:
197
+ recons_urls = {}
198
+ std_outs = []
199
+ std_errs = []
200
+
201
+ success = result.success
202
+ if isinstance(result, ResultsWithStd):
203
+ std_outs.append(result.std_out)
204
+ std_errs.append(result.std_err)
205
+ if isinstance(result, ResultsLocalRun):
206
+ recons_urls = result.results_identifiers
207
+ if isinstance(result, ResultSlurmRun):
208
+ future_tomo_obj = FutureTomwerObject(
209
+ tomo_obj=scan,
210
+ process_requester_id=process_id,
211
+ futures=result.future_slurm_jobs,
212
+ )
213
+ return success, recons_urls, (future_tomo_obj,), std_outs, std_errs
214
+
215
+
170
216
  class _Reconstructor(_NabuBaseReconstructor):
171
217
  def __init__(
172
218
  self,
@@ -207,14 +253,9 @@ class _Reconstructor(_NabuBaseReconstructor):
207
253
 
208
254
  @docstring(_NabuBaseReconstructor)
209
255
  def run(self) -> Iterable:
210
- if self.slice_index == "middle":
211
- if self.scan.dim_2 is not None:
212
- self.slice_index = self.scan.dim_2 // 2
213
- else:
214
- _logger.warning(
215
- "scan.dim_2 returns None, unable to deduce middle " "pick 1024"
216
- )
217
- self.slice_index = 1024
256
+ self.slice_index = slice_index_to_int(
257
+ slice_index=self.slice_index, scan=self.scan
258
+ )
218
259
 
219
260
  results = {}
220
261
  if self.advancement:
@@ -222,24 +263,11 @@ class _Reconstructor(_NabuBaseReconstructor):
222
263
  for var_value, config in self.nabu_configs.items():
223
264
  if self._cancelled:
224
265
  break
225
-
226
- # in 1.2 there is some strange pipes. But this is rework on the next version so just fixe pipelines in this version
227
- if "nabu_params" in config:
228
- nabu_config = deepcopy(config["nabu_params"])
229
- nabu_config["output"].update(config["output"])
230
- if "pipeline" not in nabu_config:
231
- nabu_config["pipeline"] = {}
232
- nabu_config["pipeline"].update(config.get("pipeline", {}))
233
- nabu_config["reconstruction"].update(config.get("reconstruction", {}))
234
- # end work around 1.2
235
- else:
236
- nabu_config = deepcopy(config)
237
-
238
- nabu_config, conf_file = self.preprocess_config(nabu_config, var_value)
266
+ config, conf_file = self.preprocess_config(deepcopy(config), var_value)
239
267
 
240
268
  # add some tomwer metadata and save the configuration
241
269
  # note: for now the section is ignored by nabu but shouldn't stay that way
242
- with utils.TomwerInfo(nabu_config) as config_to_dump:
270
+ with utils.TomwerInfo(config) as config_to_dump:
243
271
  generate_nabu_configfile(
244
272
  conf_file,
245
273
  nabu_fullfield_default_config,
@@ -327,7 +355,7 @@ class _Reconstructor(_NabuBaseReconstructor):
327
355
  _config["reconstruction"]["end_z"] = self.slice_index
328
356
  return _config, file_prefix
329
357
 
330
- def preprocess_config(self, config, value: float):
358
+ def preprocess_config(self, config, value) -> tuple:
331
359
  dataset_params = self.scan.get_nabu_dataset_info()
332
360
  if "dataset" in config:
333
361
  dataset_params.update(config["dataset"])
@@ -355,3 +383,301 @@ class _Reconstructor(_NabuBaseReconstructor):
355
383
  cfg_folder, file_prefix + nabu_settings.NABU_CONFIG_FILE_EXTENSION
356
384
  )
357
385
  return config, conf_file
386
+
387
+
388
+ class _ReconstructorMultiCor(_NabuBaseReconstructor):
389
+ def __init__(
390
+ self,
391
+ nabu_config: dict,
392
+ cors: tuple,
393
+ file_format,
394
+ slice_index: Union[int, str] = "middle",
395
+ output_file_prefix_pattern=None,
396
+ *args,
397
+ **kwargs,
398
+ ):
399
+ if not isinstance(cors, tuple):
400
+ raise TypeError(
401
+ f"cors are expected to be an instance of tuple. Get {type(cors)} instead"
402
+ )
403
+ self.__cors = cors
404
+ self.__slice_index = slice_index
405
+ self.__nabu_config = nabu_config
406
+ self.file_format = file_format
407
+ self._output_file_prefix_pattern = output_file_prefix_pattern
408
+
409
+ super().__init__(*args, **kwargs)
410
+
411
+ @property
412
+ def cors(self) -> tuple:
413
+ return self.__cors
414
+
415
+ @property
416
+ def slice_index(self) -> Union[int, str]:
417
+ return self.__slice_index
418
+
419
+ @property
420
+ def nabu_config(self) -> dict:
421
+ return self.__nabu_config
422
+
423
+ def _process_config(
424
+ self,
425
+ config_to_dump: dict,
426
+ config_file: str,
427
+ file_format: str,
428
+ info: Optional[str],
429
+ process_name: str,
430
+ ):
431
+ """
432
+ process provided configuration
433
+
434
+ :param str info:
435
+ """
436
+ if self.dry_run is True or self.only_create_config_file():
437
+ return ResultsRun(
438
+ success=True,
439
+ config=config_to_dump,
440
+ )
441
+ elif self.target is Target.LOCAL:
442
+ _logger.info(f"run {info} for {self.scan} with {config_to_dump}")
443
+ return self._run_nabu_multicor_locally(
444
+ conf_file=config_file,
445
+ file_format=file_format,
446
+ config_to_dump=config_to_dump,
447
+ )
448
+ elif self.target is Target.SLURM:
449
+ _logger.info(
450
+ f"run {info} on slurm for {self.scan.path} with {config_to_dump}"
451
+ )
452
+ return self._run_nabu_multicor_on_slurm(
453
+ conf_file=config_file,
454
+ config_to_dump=config_to_dump,
455
+ cluster_config=self.cluster_config.to_dict(),
456
+ process_name=process_name,
457
+ info=info,
458
+ )
459
+ else:
460
+ raise ValueError(f"{self.target} is not recognized as a valid target")
461
+
462
+ @staticmethod
463
+ def convert_cor_from_rel_to_abs(scan, cor):
464
+ if scan.dim_1 is not None:
465
+ return cor + scan.dim_1 / 2.0
466
+ else:
467
+ _logger.warning("enable to get image half width. Set it to 1024")
468
+ return cor + 1024
469
+
470
+ @staticmethod
471
+ def convert_cor_from_abs_to_rel(scan, cor):
472
+ if scan.dim_1 is not None:
473
+ return cor - scan.dim_1 / 2.0
474
+ else:
475
+ _logger.warning("enable to get image half width. Set it to 1024")
476
+ return cor - 1024
477
+
478
+ def _run_nabu_multicor_locally(
479
+ self,
480
+ conf_file: str,
481
+ file_format: str,
482
+ config_to_dump: dict,
483
+ ) -> ResultsLocalRun:
484
+ """
485
+ run locally nabu for a single configuration file.
486
+
487
+ :param str conf_file: path to the nabu .cfg file
488
+ :param str file_format: format of the generated file
489
+ :param dict config_to_dump: configuration saved in the .cfg as a dictionary
490
+ :return: results of the local run
491
+ :rtype: ResultsLocalRun
492
+ """
493
+ if not has_nabu:
494
+ raise ImportError("Fail to import nabu")
495
+ slice_index = slice_index_to_int(self.slice_index, scan=self.scan)
496
+
497
+ cor_in_nabu_ref = tuple(
498
+ [self.convert_cor_from_rel_to_abs(self.scan, cor) for cor in self.cors]
499
+ )
500
+ cor_in_nabu_ref = ",".join([str(cor) for cor in cor_in_nabu_ref])
501
+ command = " ".join(
502
+ (
503
+ "python3",
504
+ "-m",
505
+ settings.NABU_MULTICOR_PATH,
506
+ conf_file, # input file
507
+ f"{slice_index}", # slice
508
+ f"{cor_in_nabu_ref}", # cor
509
+ )
510
+ )
511
+ _logger.info(f'call nabu from "{command}"')
512
+
513
+ self._process = subprocess.Popen(
514
+ command,
515
+ shell=True,
516
+ cwd=self.scan.path,
517
+ stdout=subprocess.PIPE,
518
+ stderr=subprocess.PIPE,
519
+ )
520
+ try:
521
+ outs, errs = self._process.communicate()
522
+ except (TimeoutError, KeyboardInterrupt):
523
+ self._process.kill()
524
+ outs, errs = self._process.communicate()
525
+
526
+ file_prefix = get_nabu_multicor_file_prefix(self.scan)
527
+
528
+ recons_vol_identifiers = utils.get_multi_cor_recons_volume_identifiers(
529
+ slice_index=slice_index,
530
+ location=config_to_dump["output"]["location"],
531
+ file_prefix=file_prefix,
532
+ scan=self.scan,
533
+ file_format=file_format,
534
+ cors=[
535
+ self.convert_cor_from_rel_to_abs(self.scan, cor) for cor in self.cors
536
+ ],
537
+ )
538
+ # convert back from abs ref to rel ref
539
+ recons_vol_identifiers = {
540
+ self.convert_cor_from_abs_to_rel(self.scan, cor): identifiers
541
+ for cor, identifiers in recons_vol_identifiers.items()
542
+ }
543
+ return ResultsLocalRun(
544
+ success=not nabu_std_err_has_error(errs),
545
+ results_identifiers=recons_vol_identifiers,
546
+ std_out=outs,
547
+ std_err=errs,
548
+ config=config_to_dump, # config_slices,
549
+ )
550
+
551
+ def _run_nabu_multicor_on_slurm(
552
+ self,
553
+ conf_file: str,
554
+ config_to_dump: dict,
555
+ cluster_config: dict,
556
+ process_name: str,
557
+ info: str,
558
+ ) -> ResultSlurmRun:
559
+ """
560
+ Run a nabu reconstruction on slurm of a single configuration
561
+
562
+ :return: results of the slurm run
563
+ :rtype: ResultSlurmRun
564
+ """
565
+ if not isinstance(conf_file, str):
566
+ raise TypeError(f"conf_file is expected to be a strg not {type(conf_file)}")
567
+ if not isinstance(config_to_dump, dict):
568
+ raise TypeError(
569
+ f"config_to_dump is expected to be a strg not {type(config_to_dump)}"
570
+ )
571
+ if not is_slurm_available():
572
+ raise RuntimeError("slurm not available")
573
+ if not isinstance(cluster_config, dict):
574
+ raise ValueError(
575
+ f"cluster config is expected to be a dict not {type(cluster_config)}"
576
+ )
577
+
578
+ # create slurm cluster
579
+ project_name = cluster_config.get(
580
+ "job_name", "tomwer_{scan}_-_{process}_-_{info}"
581
+ )
582
+ project_name = project_name.format(
583
+ scan=str(self.scan), process=process_name, info=info
584
+ )
585
+ # project name should not contain any spaces as it will be integrated in a script and interpreted.
586
+ project_name = project_name.replace(" ", "_")
587
+ cluster_config["job_name"] = project_name
588
+
589
+ slice_index = slice_index_to_int(self.slice_index, scan=self.scan)
590
+ cor_in_nabu_ref = tuple(
591
+ [self.convert_cor_from_rel_to_abs(self.scan, cor) for cor in self.cors]
592
+ )
593
+ cor_in_nabu_ref = ",".join([str(cor) for cor in cor_in_nabu_ref])
594
+
595
+ # submit job
596
+ script_name = get_slurm_script_name(prefix="nabu")
597
+ # for now force job name
598
+ cluster_config["job_name"] = f"tomwer-nabu {conf_file}"
599
+ job = SBatchScriptJob(
600
+ slurm_config=cluster_config,
601
+ script=(
602
+ f"python3 -m {settings.NABU_MULTICOR_PATH} {conf_file} {slice_index} {cor_in_nabu_ref}",
603
+ ),
604
+ script_path=os.path.join(self.scan.path, "slurm_scripts", script_name),
605
+ clean_script=False,
606
+ working_directory=self.scan.working_directory,
607
+ )
608
+ future_slurm_job = submit_to_slurm_cluster(job)
609
+
610
+ callbacks = self._get_futures_slurm_callback(config_to_dump)
611
+ assert isinstance(
612
+ callbacks, tuple
613
+ ), f"callbacks is expected to an instance of tuple and not {type(callbacks)}"
614
+ for callback in callbacks:
615
+ future_slurm_job.add_done_callback(callback.process)
616
+
617
+ return ResultSlurmRun(
618
+ success=True,
619
+ config=config_to_dump,
620
+ future_slurm_jobs=(future_slurm_job,),
621
+ std_out=None,
622
+ std_err=None,
623
+ job_id=job.job_id,
624
+ )
625
+
626
+ def preprocess_config(self, config):
627
+ dataset_params = self.scan.get_nabu_dataset_info()
628
+ if "dataset" in config:
629
+ dataset_params.update(config["dataset"])
630
+ config["dataset"] = dataset_params
631
+
632
+ config["resources"] = utils.get_nabu_resources_desc(
633
+ scan=self.scan, workers=1, method="local"
634
+ )
635
+ # force overwrite results
636
+ if "output" not in config:
637
+ config["output"] = {}
638
+ config["output"].update({"overwrite_results": 1})
639
+
640
+ cfg_folder = os.path.join(
641
+ config["output"]["location"],
642
+ nabu_settings.NABU_CFG_FILE_FOLDER,
643
+ )
644
+ os.makedirs(cfg_folder, exist_ok=True)
645
+
646
+ cfg_folder = os.path.join(
647
+ self.nabu_config["output"]["location"],
648
+ nabu_settings.NABU_CFG_FILE_FOLDER,
649
+ )
650
+ os.makedirs(cfg_folder, exist_ok=True)
651
+
652
+ conf_file = os.path.join(
653
+ cfg_folder,
654
+ f"{self.scan.get_dataset_basename()}_multi_cor"
655
+ + nabu_settings.NABU_CONFIG_FILE_EXTENSION,
656
+ )
657
+ return config, conf_file
658
+
659
+ def run(self) -> Iterable:
660
+ nabu_config, conf_file = self.preprocess_config(deepcopy(self.nabu_config))
661
+
662
+ # the policy is to save nabu .cfg file at the same location as the
663
+ # force overwrite results
664
+
665
+ # add some tomwer metadata and save the configuration
666
+ # note: for now the section is ignored by nabu but shouldn't stay that way
667
+ with utils.TomwerInfo(nabu_config) as config_to_dump:
668
+ generate_nabu_configfile(
669
+ conf_file,
670
+ nabu_fullfield_default_config,
671
+ config=config_to_dump,
672
+ options_level="advanced",
673
+ )
674
+
675
+ results = self._process_config(
676
+ config_to_dump=nabu_config,
677
+ config_file=conf_file,
678
+ file_format=self.file_format,
679
+ info="nabu sa-axis reconstruction",
680
+ process_name=self.process_name,
681
+ )
682
+
683
+ return results
@@ -41,6 +41,12 @@ from processview.core.manager.manager import ProcessManager, DatasetState
41
41
  from tomwer.core.futureobject import FutureTomwerObject
42
42
  from tomwer.core.utils.scanutils import data_identifier_to_scan
43
43
  from tomwer.core.utils.slurm import is_slurm_available
44
+ from tomwer.core.process.reconstruction.nabu.utils import slice_index_to_int
45
+ from tomwer.core.process.icat.gallery import (
46
+ IcatScreenshots,
47
+ deduce_dataset_gallery_location,
48
+ )
49
+ from tomwer.core.volume.volumefactory import VolumeFactory
44
50
 
45
51
  try:
46
52
  from nabu.pipeline.fullfield.reconstruction import ( # noqa F401
@@ -81,7 +87,7 @@ from tomoscan.io import HDF5File
81
87
 
82
88
  from tomwer.core.process.task import Task
83
89
  from tomwer.core.scan.edfscan import EDFTomoScan
84
- from tomwer.core.scan.hdf5scan import HDF5TomoScan
90
+ from tomwer.core.scan.nxtomoscan import NXtomoScan
85
91
  from tomwer.core.scan.scanbase import TomwerScanBase
86
92
  from tomwer.core.scan.scanfactory import ScanFactory
87
93
  from tomwer.io.utils.h5pyutils import EntryReader
@@ -210,7 +216,7 @@ def run_slices_reconstruction(
210
216
  assert not is_cluster_job, "cluster job should not return ResultsLocalRun"
211
217
  stderrs.append(result.std_err)
212
218
  stdouts.append(result.std_out)
213
- output_urls.extend(result.results_urls)
219
+ output_urls.extend(result.results_identifiers)
214
220
  # if slice_index is None this mean that we are simply creating the
215
221
  # .cfg file for nabu full volume.
216
222
  elif isinstance(result, ResultSlurmRun):
@@ -263,11 +269,7 @@ class NabuSlicesTask(
263
269
  "dry_run",
264
270
  "serialize_output_data",
265
271
  ),
266
- output_names=(
267
- "data",
268
- "nabu_params",
269
- "future_tomo_obj",
270
- ),
272
+ output_names=("data", "nabu_params", "future_tomo_obj", "screenshots"),
271
273
  ):
272
274
  """
273
275
  Definition of the nabu reconstruction volume reconstruction process
@@ -313,7 +315,7 @@ class NabuSlicesTask(
313
315
  assert isinstance(configuration, dict), "configuration is expected to be a dict"
314
316
 
315
317
  entry = "entry"
316
- if isinstance(scan, HDF5TomoScan):
318
+ if isinstance(scan, NXtomoScan):
317
319
  entry = scan.entry
318
320
 
319
321
  output_urls = []
@@ -358,7 +360,7 @@ class NabuSlicesTask(
358
360
  ), "cluster job should not return ResultsLocalRun"
359
361
  stderrs.append(result.std_err)
360
362
  stdouts.append(result.std_out)
361
- output_urls.extend(result.results_urls)
363
+ output_urls.extend(result.results_identifiers)
362
364
  # if slice_index is None this mean that we are simply creating the
363
365
  # .cfg file for nabu full volume.
364
366
  elif isinstance(result, ResultSlurmRun):
@@ -392,6 +394,21 @@ class NabuSlicesTask(
392
394
  scan.set_latest_reconstructions(output_urls)
393
395
  future_tomo_obj = None
394
396
 
397
+ volume_urls = []
398
+ for rec_identifier in scan.latest_reconstructions:
399
+ volume_urls.extend(
400
+ VolumeFactory.from_identifier_to_vol_urls(rec_identifier)
401
+ )
402
+
403
+ self.outputs.screenshots = IcatScreenshots(
404
+ data_dir=deduce_dataset_gallery_location(scan),
405
+ screenshots={
406
+ os.path.splitext(os.path.basename(url.file_path()))[0]: url
407
+ for url in volume_urls
408
+ },
409
+ scan=scan,
410
+ )
411
+
395
412
  process_index = scan.pop_process_index()
396
413
  # update processes information / registration
397
414
  gc.collect()
@@ -668,12 +685,9 @@ class SingleSliceRunner(_NabuBaseReconstructor):
668
685
  :raise: TIMEOUT_SLURM_JOB_SUBMISSION if not all workers spwan
669
686
  """
670
687
  if isinstance(self.slice_index, str):
671
- if self.slice_index == "middle":
672
- self._slice_index = self.scan.dim_2 // 2
673
- else:
674
- raise ValueError(
675
- f"slice index is expected to an int or 'middle' and not {type(self.slice_index)}"
676
- )
688
+ self._slice_index = slice_index_to_int(
689
+ slice_index=self.slice_index, scan=self.scan
690
+ )
677
691
  elif (
678
692
  isinstance(self.slice_index, float)
679
693
  and int(self.slice_index) == self.slice_index
@@ -784,9 +798,6 @@ class SingleSliceRunner(_NabuBaseReconstructor):
784
798
  file_format=file_format,
785
799
  scan=self.scan,
786
800
  slice_index=None,
787
- start_z=None,
788
- end_z=None,
789
- expects_single_slice=True,
790
801
  )
791
802
 
792
803
  return (CallBack(callback, self.scan),)
@@ -799,10 +810,11 @@ class SingleSliceRunner(_NabuBaseReconstructor):
799
810
  ):
800
811
  if pag:
801
812
  assert db is not None, "if paganin defined, db should not be None"
802
- if slice_index == "middle":
803
- slice_index = scan.dim_2 // 2
813
+ if slice_index is not None:
814
+ slice_index = slice_index_to_int(slice_index, scan=scan)
815
+
804
816
  assert type(db) in (int, type(None))
805
- if isinstance(scan, HDF5TomoScan):
817
+ if isinstance(scan, NXtomoScan):
806
818
  basename, _ = os.path.splitext(scan.master_file)
807
819
  basename = os.path.basename(basename)
808
820
  try: