nxtomomill 2.2.0.dev0__tar.gz → 2.2.0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. {nxtomomill-2.2.0.dev0/nxtomomill.egg-info → nxtomomill-2.2.0.dev2}/PKG-INFO +2 -2
  2. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/__main__.py +1 -1
  3. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/h52nx.py +53 -2
  4. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/baseacquisition.py +130 -89
  5. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/standardacquisition.py +30 -11
  6. nxtomomill-2.2.0.dev2/nxtomomill/converter/hdf5/acquisition/tests/test_acquisition.py +188 -0
  7. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/_context.py +8 -0
  8. nxtomomill-2.2.0.dev2/nxtomomill/converter/hdf5/acquisition/utils/_keys.py +29 -0
  9. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/hdf5converter.py +7 -0
  10. nxtomomill-2.2.0.dev2/nxtomomill/converter/hdf5/tests/test_err_handling.py +48 -0
  11. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/tests/test_h52nx_utils.py +23 -5
  12. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/tests/test_hdf5converter.py +297 -0
  13. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/confighandler.py +2 -1
  14. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/tests/test_confighandler.py +1 -0
  15. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/dx2nx.py +9 -0
  16. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/general_section.py +6 -3
  17. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/unit_section.py +3 -0
  18. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/_context.py +2 -0
  19. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/general_section.py +27 -1
  20. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/h52nx.py +1 -0
  21. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/keys_section.py +3 -0
  22. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/tests/test_edf2nx_model.py +12 -0
  23. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/tests/test_h52nx_model.py +15 -2
  24. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2/nxtomomill.egg-info}/PKG-INFO +2 -2
  25. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill.egg-info/SOURCES.txt +1 -1
  26. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill.egg-info/requires.txt +1 -1
  27. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill.egg-info/top_level.txt +0 -2
  28. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/pyproject.toml +2 -2
  29. nxtomomill-2.2.0.dev0/nxtomomill/converter/edf/_Blosc2Jp2kWrapper.py +0 -63
  30. nxtomomill-2.2.0.dev0/nxtomomill/converter/hdf5/acquisition/tests/test_acquisition.py +0 -86
  31. nxtomomill-2.2.0.dev0/nxtomomill/converter/hdf5/acquisition/utils/_keys.py +0 -10
  32. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/LICENSE +0 -0
  33. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/README.md +0 -0
  34. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/doc/conf.py +0 -0
  35. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/__init__.py +0 -0
  36. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/__init__.py +0 -0
  37. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/blissfluo2nx.py +0 -0
  38. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/blissfluoconfig.py +0 -0
  39. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/dxfile2nx.py +0 -0
  40. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/edf2nx.py +0 -0
  41. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/edf2nx_check.py +0 -0
  42. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/edfconfig.py +0 -0
  43. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/fluo2nx.py +0 -0
  44. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/fluoconfig.py +0 -0
  45. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/fscan2nx.py +0 -0
  46. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/h5config.py +0 -0
  47. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/nxcopy.py +0 -0
  48. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/patch_nx.py +0 -0
  49. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/split_nxfile.py +0 -0
  50. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_bliss_fluo2nx_app.py +0 -0
  51. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_dxfile2nx_app.py +0 -0
  52. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_edf2nx_app.py +0 -0
  53. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_edf_config_app.py +0 -0
  54. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_fluo2nx_app.py +0 -0
  55. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_h52nx_app.py +0 -0
  56. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_h5_config_app.py +0 -0
  57. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_nx_copy.py +0 -0
  58. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_patch_nx_app.py +0 -0
  59. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/tests/test_split_nxfile_app.py +0 -0
  60. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/z_concatenate_scans.py +0 -0
  61. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/app/zstages2nxs.py +0 -0
  62. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/__init__.py +0 -0
  63. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/baseconverter.py +0 -0
  64. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/dxfile/__init__.py +0 -0
  65. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/dxfile/dxfileconverter.py +0 -0
  66. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/dxfile/tests/test_dxfile.py +0 -0
  67. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/edf/__init__.py +0 -0
  68. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/edf/checks.py +0 -0
  69. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/edf/edfconverter.py +0 -0
  70. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/edf/tests/test_checks.py +0 -0
  71. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/edf/tests/test_edf2nx.py +0 -0
  72. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/edf/tests/test_edf2nx_check.py +0 -0
  73. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fluo/__init__.py +0 -0
  74. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fluo/blissfluoscan.py +0 -0
  75. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fluo/fluoconverter.py +0 -0
  76. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fluo/fluoscan.py +0 -0
  77. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fluo/tests/__init__.py +0 -0
  78. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fluo/tests/test_fluoscan2D.py +0 -0
  79. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fluo/tests/test_fluoscan3D.py +0 -0
  80. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fscan/__init__.py +0 -0
  81. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/fscan/fscanconverter.py +0 -0
  82. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/__init__.py +0 -0
  83. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/__init__.py +0 -0
  84. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/_bliss_activeconfig.py +0 -0
  85. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/backandforth.py +0 -0
  86. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/blisstomoconfig.py +0 -0
  87. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/multitomo.py +0 -0
  88. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/pcotomoacquisition.py +0 -0
  89. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/bliss_tomo_datasets.py +0 -0
  90. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/test_acquisition_utils.py +0 -0
  91. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/test_back_and_forth.py +0 -0
  92. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/test_bliss_tomo_config.py +0 -0
  93. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/test_holotomo.py +0 -0
  94. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/test_legacy.py +0 -0
  95. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/test_multitomo_zseries.py +0 -0
  96. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/tests/test_multitomoacquisition.py +0 -0
  97. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/__init__.py +0 -0
  98. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/_scan_type_finder.py +0 -0
  99. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/bliss_scan_type.py +0 -0
  100. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/detector.py +0 -0
  101. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/machine_current.py +0 -0
  102. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/series.py +0 -0
  103. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/utils/timestamps.py +0 -0
  104. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisition/zseriesacquisition.py +0 -0
  105. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/acquisitionConstructor.py +0 -0
  106. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/post_processing/__init__.py +0 -0
  107. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/post_processing/dark_flat_copy.py +0 -0
  108. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/tests/test_frame_flip.py +0 -0
  109. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/hdf5/utils.py +0 -0
  110. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/converter/version.py +0 -0
  111. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/__init__.py +0 -0
  112. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/__init__.py +0 -0
  113. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/configbase.py +0 -0
  114. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/dxconfig.py +0 -0
  115. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/edfconfig.py +0 -0
  116. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/fluoconfig.py +0 -0
  117. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/hdf5config.py +0 -0
  118. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/config/tests/__init__.py +0 -0
  119. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/tests/test_frame_group.py +0 -0
  120. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/io/tests/utils.py +0 -0
  121. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/__init__.py +0 -0
  122. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/base/ConfigBase.py +0 -0
  123. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/base/FrmFlatToNestedBase.py +0 -0
  124. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/base/NestedModelBase.py +0 -0
  125. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/base/__init__.py +0 -0
  126. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/base/general_section.py +0 -0
  127. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/base/instrument_section.py +0 -0
  128. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/base/source_section.py +0 -0
  129. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/blissfluo2nx/__init__.py +0 -0
  130. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/blissfluo2nx/blissfluo2nx.py +0 -0
  131. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/blissfluo2nx/general_section.py +0 -0
  132. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/__init__.py +0 -0
  133. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/dark_and_flat_section.py +0 -0
  134. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/detector_section.py +0 -0
  135. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/edf2nx.py +0 -0
  136. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/keys_section.py +0 -0
  137. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/edf2nx/sample_section.py +0 -0
  138. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/fluo2nx/__init__.py +0 -0
  139. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/fluo2nx/fluo2nx.py +0 -0
  140. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/fluo2nx/general_section.py +0 -0
  141. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/FrameGroup.py +0 -0
  142. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/__init__.py +0 -0
  143. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/_acquisitionstep.py +0 -0
  144. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/entries_and_title_section.py +0 -0
  145. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/extra_params_section.py +0 -0
  146. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/frame_type_section.py +0 -0
  147. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/h52nx/multitomo_section.py +0 -0
  148. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/tests/test_context.py +0 -0
  149. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/tests/test_fluo2nx_model.py +0 -0
  150. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/tests/test_io_utils.py +0 -0
  151. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/models/utils.py +0 -0
  152. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/settings.py +0 -0
  153. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/test.py +0 -0
  154. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/__init__.py +0 -0
  155. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/datasets.py +0 -0
  156. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/resources/__init__.py +0 -0
  157. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/resources/edf2nx_config_files/__init__.py +0 -0
  158. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/resources/fluo2nx_config_files/__init__.py +0 -0
  159. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/resources/h52nx_config_files/__init__.py +0 -0
  160. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/test_version.py +0 -0
  161. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/utils/__init__.py +0 -0
  162. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/utils/bliss.py +0 -0
  163. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/tests/utils/dxfile.py +0 -0
  164. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/__init__.py +0 -0
  165. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/_os.py +0 -0
  166. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/flat_reducer.py +1 -1
  167. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/h5pyutils.py +0 -0
  168. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/hdf5.py +0 -0
  169. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/io.py +0 -0
  170. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/nexus.py +0 -0
  171. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/pintutils.py +0 -0
  172. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/tests/test_flat_reducer.py +0 -0
  173. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/tests/test_h5pyutils.py +0 -0
  174. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/tests/test_nexus_utils.py +0 -0
  175. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/tests/test_utils.py +0 -0
  176. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill/utils/utils.py +0 -0
  177. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill.egg-info/dependency_links.txt +0 -0
  178. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/nxtomomill.egg-info/entry_points.txt +0 -0
  179. {nxtomomill-2.2.0.dev0 → nxtomomill-2.2.0.dev2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nxtomomill
3
- Version: 2.2.0.dev0
3
+ Version: 2.2.0.dev2
4
4
  Summary: applications and library to convert raw format to NXtomo format
5
5
  Author-email: Henri Payno <henri.payno@esrf.fr>, Pierre Paleo <pierre.paleo@esrf.fr>, Pierre-Olivier Autran <pierre-olivier.autran@esrf.fr>, Jérôme Lesaint <jerome.lesaint@esrf.fr>, Alessandro Mirone <mirone@esrf.fr>
6
6
  License:
@@ -58,7 +58,7 @@ Requires-Dist: silx>=2.0
58
58
  Requires-Dist: nxtomo>=3.1.0dev1
59
59
  Requires-Dist: pint
60
60
  Requires-Dist: packaging
61
- Requires-Dist: tomoscan[full]>=3.0.0dev0
61
+ Requires-Dist: tomoscan>=3.0.0dev0
62
62
  Requires-Dist: tomobase>=0.0.0dev0
63
63
  Requires-Dist: tqdm
64
64
  Requires-Dist: pydantic
@@ -84,7 +84,7 @@ class Launcher(_Launcher):
84
84
  replacement=deprecation_info.replacement,
85
85
  since_version=deprecation_info.since,
86
86
  )
87
- super().execute(argv=argv)
87
+ return super().execute(argv=argv)
88
88
 
89
89
  def print_help(self):
90
90
  """Print the help to stdout."""
@@ -10,12 +10,14 @@ For a complete tutorial you can have a look at: :ref:`Tomoh52nx`
10
10
  """
11
11
 
12
12
  import argparse
13
+ import cProfile
13
14
  import logging
15
+ import os
14
16
 
15
17
  from tqdm import tqdm
16
18
 
17
19
  from nxtomomill import utils
18
- from nxtomomill.converter import from_h5_to_nx
20
+ from nxtomomill.converter.hdf5.hdf5converter import _H5ToNxConverter
19
21
  from nxtomomill.io.config.confighandler import (
20
22
  SETTABLE_PARAMETERS_UNITS,
21
23
  TomoHDF5ConfigHandler,
@@ -317,12 +319,49 @@ def main(argv):
317
319
  "Sometime relative link cannot be solved (GPFS vs NFS). "
318
320
  "In this case you might be forced to use absolute path.",
319
321
  )
322
+ parser.add_argument(
323
+ "--no-resolve-vds",
324
+ "--no-resolve-vds-links",
325
+ "--no_resolve_vds",
326
+ dest="resolve_vds",
327
+ action="store_false",
328
+ default=None,
329
+ help="Do not resolve external links and virtual datasets (VDS) down to "
330
+ "their final source when building the output NXtomo. Can speed up the "
331
+ "conversion for datasets with many nested links, at the cost of the "
332
+ "output file containing VDS pointing to other VDS instead of direct "
333
+ "links to the raw data. Defaults to the 'NXTOMOMILL_RESOLVE_VDS' "
334
+ "environment variable if set, else resolving is enabled.",
335
+ )
336
+ parser.add_argument(
337
+ "--profiling",
338
+ "--profile",
339
+ help="Profile the conversion with 'cProfile' and dump the statistics "
340
+ "to a file (see '--profiling-output-file').",
341
+ action="store_true",
342
+ default=False,
343
+ )
344
+ parser.add_argument(
345
+ "--profiling-output-file",
346
+ "--profiling_output_file",
347
+ default="profiling.out",
348
+ help="Output file to dump the 'cProfile' statistics to when "
349
+ "'--profiling' is provided. If this file already exists the "
350
+ "conversion won't be launched (default: 'profiling.out').",
351
+ )
320
352
  options = parser.parse_args(argv[1:])
321
353
  if options.request_input:
322
354
  callback_det_sel = _ask_for_selecting_detector
323
355
  else:
324
356
  callback_det_sel = None
325
357
 
358
+ if options.profiling and os.path.exists(options.profiling_output_file):
359
+ _logger.error(
360
+ f"Profiling output file already exists: '{options.profiling_output_file}'. "
361
+ "Remove it or provide another one with '--profiling-output-file'."
362
+ )
363
+ return 1
364
+
326
365
  try:
327
366
  configuration_handler = TomoHDF5ConfigHandler(options)
328
367
  except Exception:
@@ -335,9 +374,21 @@ def main(argv):
335
374
  configuration_handler.configuration.log_level
336
375
  )
337
376
  with tqdm(desc="h52nx", delay=1, bar_format="{l_bar}{bar}") as progress:
338
- from_h5_to_nx(
377
+ converter = _H5ToNxConverter(
339
378
  configuration=configuration_handler.configuration,
340
379
  progress=progress,
341
380
  input_callback=None,
342
381
  detector_sel_callback=callback_det_sel,
343
382
  )
383
+ if options.profiling:
384
+ profiler = cProfile.Profile()
385
+ profiler.enable()
386
+ try:
387
+ converter.convert()
388
+ finally:
389
+ profiler.disable()
390
+ profiler.dump_stats(options.profiling_output_file)
391
+ else:
392
+ converter.convert()
393
+ if converter.has_failed_acquisitions:
394
+ return 1
@@ -12,7 +12,9 @@ import copy
12
12
 
13
13
  import h5py
14
14
 
15
+ import nxtomomill
15
16
  from nxtomo.application.nxtomo import NXtomo
17
+ from nxtomo.io import HDF5File
16
18
  from nxtomomill.utils.h5pyutils import from_data_url_to_virtual_source
17
19
  from nxtomomill.utils.hdf5 import EntryReader
18
20
  from nxtomomill.utils.utils import embed_url # noqa F401
@@ -30,8 +32,9 @@ from silx.io.url import DataUrl
30
32
  from silx.io.utils import h5py_read_dataset
31
33
 
32
34
  from nxtomomill.models.h52nx.h52nx import H52nxModel
33
- from .utils._keys import keys_to_str_tuple
34
- from .utils._context import read_key, MissingValue
35
+ from nxtomomill.models.h52nx._context import _Key
36
+ from .utils._keys import keys_to_str_tuple, split_keys_by_priority
37
+ from .utils._context import read_key, MissingValue, InvalidValue
35
38
 
36
39
  _ureg = pint.get_application_registry()
37
40
  _logger = logging.getLogger(__name__)
@@ -300,7 +303,14 @@ class BaseAcquisition:
300
303
  file_path=output_file,
301
304
  data_path=data_path,
302
305
  use_abs_path_for_vds=self.configuration.use_abs_path_for_vds,
306
+ overwrite=self.configuration.overwrite,
303
307
  )
308
+ # record the nxtomomill version used to create this NXtomo as attributes
309
+ # on the NXtomo entry group itself
310
+ with HDF5File(output_file, mode="a") as h5f:
311
+ entry_grp = h5f[data_path]
312
+ entry_grp.attrs["creator"] = "nxtomomill"
313
+ entry_grp.attrs["creator_version"] = nxtomomill.__version__
304
314
  # check rotation angle
305
315
  if nx_tomo.sample.rotation_angle is not None:
306
316
  unique_angles = numpy.unique(
@@ -408,6 +418,12 @@ class BaseAcquisition:
408
418
  except MissingValue:
409
419
  # skip values containing '*DIS*'
410
420
  continue
421
+ except InvalidValue:
422
+ # dataset reports an 'ERR' value: skip it and try the next key
423
+ _logger.warning(
424
+ f"{info_retrieve} reports an 'ERR' value for {node.name} - {key}. This key will be skipped."
425
+ )
426
+ continue
411
427
  except KeyError as e:
412
428
  _logger.info(
413
429
  f"Cannot solve key {key}. Most likely one dataset is not there. Error is {e}"
@@ -613,6 +629,55 @@ class BaseAcquisition:
613
629
 
614
630
  return None
615
631
 
632
+ @staticmethod
633
+ def _get_values_from_nodes(
634
+ nodes: tuple[h5py.Group | None, ...],
635
+ n_frame: int | None,
636
+ keys: tuple[_Key, ...],
637
+ info_retrieve: str,
638
+ expected_unit: pint.Unit,
639
+ ) -> pint.Quantity | None:
640
+ """
641
+ Look for the values of 'keys' in 'nodes'.
642
+
643
+ Keys resolved from the bliss-tomo 'tomoconfig' are looked at first (on all nodes, arrays
644
+ first then scalars). The other keys (default keys) are only used as a fallback if none of
645
+ the 'tomoconfig' keys could be found.
646
+
647
+ :return: values as a pint.Quantity or None if no key could be found.
648
+ """
649
+ tomo_config_keys, fallback_keys = split_keys_by_priority(keys)
650
+
651
+ def look_for_keys(keys_to_check: tuple[str, ...]) -> pint.Quantity | None:
652
+ for node in nodes:
653
+ if node is None:
654
+ continue
655
+ try:
656
+ values, unit = BaseAcquisition._get_node_values_for_frame_array(
657
+ node=node,
658
+ n_frame=n_frame,
659
+ keys=keys_to_check,
660
+ info_retrieve=info_retrieve,
661
+ expected_unit=expected_unit,
662
+ )
663
+ except (ValueError, KeyError):
664
+ pass
665
+ else:
666
+ return values * unit
667
+ return None
668
+
669
+ if len(tomo_config_keys) > 0:
670
+ result = look_for_keys(tomo_config_keys)
671
+ if result is not None:
672
+ return result
673
+ _logger.warning(
674
+ f"Unable to find {info_retrieve} from the bliss-tomo 'tomoconfig' datasets {tomo_config_keys}. "
675
+ f"Will look at the fallback datasets {fallback_keys}."
676
+ )
677
+ if len(fallback_keys) > 0:
678
+ return look_for_keys(fallback_keys)
679
+ return None
680
+
616
681
  def _get_rotation_angle(self, root_node: h5py.Group, n_frame: int) -> pint.Quantity:
617
682
  """
618
683
  return the list of rotation angle for each frame.
@@ -623,23 +688,19 @@ class BaseAcquisition:
623
688
  if not isinstance(root_node, h5py.Group):
624
689
  raise TypeError("root_node is expected to be a h5py.Group")
625
690
 
626
- for grp in (
627
- self._get_positioners_node(root_node),
628
- root_node,
629
- self._get_measurement_node(root_node),
630
- ):
631
- try:
632
- angles, unit = self._get_node_values_for_frame_array(
633
- node=grp,
634
- n_frame=n_frame,
635
- keys=keys_to_str_tuple(self.configuration.rotation_angle_keys),
636
- info_retrieve="rotation angle",
637
- expected_unit=_ureg.degree,
638
- )
639
- except (ValueError, KeyError):
640
- pass
641
- else:
642
- return angles * unit
691
+ angles = self._get_values_from_nodes(
692
+ nodes=(
693
+ self._get_positioners_node(root_node),
694
+ root_node,
695
+ self._get_measurement_node(root_node),
696
+ ),
697
+ n_frame=n_frame,
698
+ keys=self.configuration.rotation_angle_keys,
699
+ info_retrieve="rotation angle",
700
+ expected_unit=_ureg.degree,
701
+ )
702
+ if angles is not None:
703
+ return angles
643
704
 
644
705
  mess = f"Unable to find rotation angle for {root_node.name}"
645
706
  if self.raise_error_if_issue:
@@ -651,19 +712,15 @@ class BaseAcquisition:
651
712
 
652
713
  def _get_sample_x(self, root_node, n_frame) -> pint.Quantity:
653
714
  """return the list of translation for each frame"""
654
- for grp in self._get_positioners_node(root_node), root_node:
655
- try:
656
- x_tr, unit = self._get_node_values_for_frame_array(
657
- node=grp,
658
- n_frame=n_frame,
659
- keys=keys_to_str_tuple(self.configuration.sample_x_keys),
660
- info_retrieve="sample x translation",
661
- expected_unit=_ureg.mm,
662
- )
663
- except (ValueError, KeyError):
664
- pass
665
- else:
666
- return x_tr * unit
715
+ translation = self._get_values_from_nodes(
716
+ nodes=(self._get_positioners_node(root_node), root_node),
717
+ n_frame=n_frame,
718
+ keys=self.configuration.sample_x_keys,
719
+ info_retrieve="sample x translation",
720
+ expected_unit=_ureg.mm,
721
+ )
722
+ if translation is not None:
723
+ return translation
667
724
 
668
725
  mess = f"Unable to find sample x for {root_node.name}"
669
726
  if self.raise_error_if_issue:
@@ -675,19 +732,15 @@ class BaseAcquisition:
675
732
 
676
733
  def _get_sample_y(self, root_node, n_frame) -> pint.Quantity:
677
734
  """return the list of translation for each frame"""
678
- for grp in self._get_positioners_node(root_node), root_node:
679
- try:
680
- y_tr, unit = self._get_node_values_for_frame_array(
681
- node=grp,
682
- n_frame=n_frame,
683
- keys=keys_to_str_tuple(self.configuration.sample_y_keys),
684
- info_retrieve="sample y translation",
685
- expected_unit=_ureg.mm,
686
- )
687
- except (ValueError, KeyError):
688
- pass
689
- else:
690
- return y_tr * unit
735
+ translation = self._get_values_from_nodes(
736
+ nodes=(self._get_positioners_node(root_node), root_node),
737
+ n_frame=n_frame,
738
+ keys=self.configuration.sample_y_keys,
739
+ info_retrieve="sample y translation",
740
+ expected_unit=_ureg.mm,
741
+ )
742
+ if translation is not None:
743
+ return translation
691
744
 
692
745
  mess = f"Unable to find sample y for {root_node.name}"
693
746
  if self.raise_error_if_issue:
@@ -697,21 +750,17 @@ class BaseAcquisition:
697
750
  _logger.warning(mess)
698
751
  return ([0] * n_frame) * _ureg.meter
699
752
 
700
- def _get_translation_x(self, root_node, n_frame) -> pint.quantity:
753
+ def _get_translation_x(self, root_node, n_frame) -> pint.Quantity | None:
701
754
  """return the list of translation for each frame"""
702
- for grp in self._get_positioners_node(root_node), root_node:
703
- try:
704
- x_tr, unit = self._get_node_values_for_frame_array(
705
- node=grp,
706
- n_frame=n_frame,
707
- keys=keys_to_str_tuple(self.configuration.translation_x_keys),
708
- info_retrieve="x base translation",
709
- expected_unit=_ureg.mm,
710
- )
711
- except (ValueError, KeyError):
712
- pass
713
- else:
714
- return x_tr * unit
755
+ translation = self._get_values_from_nodes(
756
+ nodes=(self._get_positioners_node(root_node), root_node),
757
+ n_frame=n_frame,
758
+ keys=self.configuration.translation_x_keys,
759
+ info_retrieve="x base translation",
760
+ expected_unit=_ureg.mm,
761
+ )
762
+ if translation is not None:
763
+ return translation
715
764
 
716
765
  mess = f"Unable to find translation x for {root_node.name}"
717
766
  if self.raise_error_if_issue:
@@ -721,21 +770,17 @@ class BaseAcquisition:
721
770
  _logger.warning(mess)
722
771
  return ([0] * n_frame) * _ureg.meter
723
772
 
724
- def _get_translation_y(self, root_node, n_frame) -> pint.quantity:
773
+ def _get_translation_y(self, root_node, n_frame) -> pint.Quantity | None:
725
774
  """return the list of translation for each frame"""
726
- for grp in self._get_positioners_node(root_node), root_node:
727
- try:
728
- y_tr, unit = self._get_node_values_for_frame_array(
729
- node=grp,
730
- n_frame=n_frame,
731
- keys=keys_to_str_tuple(self.configuration.translation_y_keys),
732
- info_retrieve="y base translation",
733
- expected_unit=_ureg.mm,
734
- )
735
- except (ValueError, KeyError):
736
- pass
737
- else:
738
- return y_tr * unit
775
+ translation = self._get_values_from_nodes(
776
+ nodes=(self._get_positioners_node(root_node), root_node),
777
+ n_frame=n_frame,
778
+ keys=self.configuration.translation_y_keys,
779
+ info_retrieve="y base translation",
780
+ expected_unit=_ureg.mm,
781
+ )
782
+ if translation is not None:
783
+ return translation
739
784
 
740
785
  mess = f"Unable to find translation y for {root_node.name}"
741
786
  if self.raise_error_if_issue:
@@ -748,20 +793,16 @@ class BaseAcquisition:
748
793
  @staticmethod
749
794
  def get_translation_z_frm(
750
795
  root_node, n_frame: int, configuration: H52nxModel
751
- ) -> pint.Quantity:
752
- for grp in BaseAcquisition._get_positioners_node(root_node), root_node:
753
- try:
754
- z_tr, unit = BaseAcquisition._get_node_values_for_frame_array(
755
- node=grp,
756
- n_frame=n_frame,
757
- keys=keys_to_str_tuple(configuration.translation_z_keys),
758
- info_retrieve="z translation",
759
- expected_unit=_ureg.mm,
760
- )
761
- except (ValueError, KeyError):
762
- pass
763
- else:
764
- return z_tr * unit
796
+ ) -> pint.Quantity | None:
797
+ translation = BaseAcquisition._get_values_from_nodes(
798
+ nodes=(BaseAcquisition._get_positioners_node(root_node), root_node),
799
+ n_frame=n_frame,
800
+ keys=configuration.translation_z_keys,
801
+ info_retrieve="z translation",
802
+ expected_unit=_ureg.mm,
803
+ )
804
+ if translation is not None:
805
+ return translation
765
806
 
766
807
  mess = f"Unable to find translation z on node {root_node.name}"
767
808
  if configuration.raises_error:
@@ -771,7 +812,7 @@ class BaseAcquisition:
771
812
  _logger.warning(mess)
772
813
  return ([0] * n_frame) * _ureg.meter
773
814
 
774
- def _get_translation_z(self, root_node, n_frame) -> pint.Quantity:
815
+ def _get_translation_z(self, root_node, n_frame) -> pint.Quantity | None:
775
816
  """return the list of translation z for each frame"""
776
817
  return self.get_translation_z_frm(
777
818
  root_node=root_node,
@@ -804,7 +845,7 @@ class BaseAcquisition:
804
845
  else:
805
846
  mess += "default value will be set. (0)"
806
847
  _logger.warning(mess)
807
- return 0 * _ureg.second
848
+ return numpy.zeros(n_frame) * _ureg.second
808
849
 
809
850
  def get_axis_scale_types(self):
810
851
  """
@@ -65,6 +65,10 @@ from .utils._keys import keys_to_str_tuple
65
65
  _ureg = pint.get_application_registry()
66
66
  _logger = logging.getLogger(__name__)
67
67
 
68
+ _MAX_VDS_SOURCES_TO_RESOLVE = 100
69
+ """If a camera dataset virtual source relies on that many (or more) distinct files, skip
70
+ resolving external links / virtual dataset sources for it."""
71
+
68
72
 
69
73
  __all__ = ["StandardAcquisition"]
70
74
 
@@ -346,20 +350,35 @@ class StandardAcquisition(BaseAcquisition):
346
350
  image_key_control = frame_type.to_image_key_control()
347
351
  self._image_key_control.extend([image_key_control.value] * n_frame)
348
352
 
349
- try:
350
- solved_camera_dataset_urls = resolve_link(
351
- dataset_url=camera_dataset_url,
352
- dataset=data_dataset,
353
- dataset_link=data_dataset_link,
354
- open_mode_for_reading=self.configuration.open_mode_for_reading,
355
- )
356
- except OSError as e:
357
- # This can happen if one fine cannot be found. In test for example where we are only using the master file
358
- # and low level bliss / tango .h5 files are not existing
353
+ n_vds_files = (
354
+ len({vs_info.file_name for vs_info in data_dataset.virtual_sources()})
355
+ if data_dataset.is_virtual
356
+ else 0
357
+ )
358
+ if n_vds_files >= _MAX_VDS_SOURCES_TO_RESOLVE:
359
359
  _logger.warning(
360
- "Fail to solve camera dataset links and VDS. Error is %s", e
360
+ f"{camera_dataset_url.path()} relies on {n_vds_files} files "
361
+ f"(>= {_MAX_VDS_SOURCES_TO_RESOLVE}). Skip external link / VDS "
362
+ "resolution to avoid opening too many files."
361
363
  )
362
364
  solved_camera_dataset_urls = (camera_dataset_url,)
365
+ elif self.configuration.resolve_vds:
366
+ try:
367
+ solved_camera_dataset_urls = resolve_link(
368
+ dataset_url=camera_dataset_url,
369
+ dataset=data_dataset,
370
+ dataset_link=data_dataset_link,
371
+ open_mode_for_reading=self.configuration.open_mode_for_reading,
372
+ )
373
+ except OSError as e:
374
+ # This can happen if one fine cannot be found. In test for example where we are only using the master file
375
+ # and low level bliss / tango .h5 files are not existing
376
+ _logger.warning(
377
+ "Fail to solve camera dataset links and VDS. Error is %s", e
378
+ )
379
+ solved_camera_dataset_urls = (camera_dataset_url,)
380
+ else:
381
+ solved_camera_dataset_urls = (camera_dataset_url,)
363
382
 
364
383
  n_total_frame_solved = 0
365
384
  for solved_camera_dataset_url in solved_camera_dataset_urls:
@@ -0,0 +1,188 @@
1
+ # coding: utf-8
2
+
3
+ import os
4
+ import tempfile
5
+
6
+ import logging
7
+
8
+ import h5py
9
+ import numpy
10
+ import pytest
11
+ from silx.io.url import DataUrl
12
+
13
+ from nxtomo.io import HDF5File
14
+ from nxtomomill.converter.hdf5.acquisition.baseacquisition import (
15
+ BaseAcquisition,
16
+ get_dataset_name_from_motor,
17
+ )
18
+ from nxtomomill.io.config import TomoHDF5Config
19
+ from nxtomomill.models.h52nx._context import _Key
20
+ from nxtomomill.tests.datasets import GitlabDataset
21
+ from nxtomomill import converter
22
+ from nxtomo.application.nxtomo import NXtomo
23
+ from nxtomo.nxobject.nxtransformations import get_lr_flip, get_ud_flip
24
+
25
+
26
+ def test_BaseAquisition():
27
+ """simple test of the BaseAcquisition class"""
28
+
29
+ with tempfile.TemporaryDirectory() as folder:
30
+ file_path = os.path.join(folder, "test.h5")
31
+ with HDF5File(file_path, mode="w") as h5f:
32
+ h5f["/data/toto/dataset"] = 12
33
+
34
+ url = DataUrl(file_path=file_path, data_path="/data/toto", scheme="silx")
35
+ std_acq = BaseAcquisition(
36
+ root_url=url,
37
+ configuration=TomoHDF5Config(),
38
+ detector_sel_callback=None,
39
+ start_index=0,
40
+ )
41
+ with std_acq.read_entry() as entry:
42
+ assert "dataset" in entry
43
+
44
+
45
+ def test_get_dataset_name_from_motor():
46
+ """test get_dataset_name_from_motor function"""
47
+ set_1 = ["rotation", "test1", "alias"]
48
+ assert get_dataset_name_from_motor(set_1, "rotation") == "test1"
49
+ assert get_dataset_name_from_motor(set_1, "my motor") is None
50
+
51
+ set_2 = ["rotation", "test1", "alias", "x translation", "m2", "test1"]
52
+ assert get_dataset_name_from_motor(set_2, "rotation") == "test1"
53
+ assert get_dataset_name_from_motor(set_2, "x translation") == "m2"
54
+
55
+ set_3 = ["rotation"]
56
+ with pytest.raises(ValueError):
57
+ get_dataset_name_from_motor(set_3, "rotation")
58
+
59
+
60
+ @pytest.mark.parametrize("raises_error", (True, False))
61
+ def test_translation_with_err_value(tmp_path, raises_error):
62
+ """
63
+ Test that translation datasets reporting an 'ERR' value are skipped:
64
+ the next valid key is used and, if none is valid, it behaves as if the
65
+ translation was not found (raises or default to 0).
66
+ """
67
+ file_path = os.path.join(tmp_path, "test.h5")
68
+ with HDF5File(file_path, mode="w") as h5f:
69
+ h5f["/entry/instrument/positioners/tx"] = "ERR"
70
+ h5f["/entry/instrument/positioners/ty"] = "ERR"
71
+ h5f["/entry/instrument/positioners/tz"] = "ERR"
72
+ h5f["/entry/instrument/positioners/sz"] = 2.0
73
+ h5f["/entry/instrument/positioners/sz"].attrs["units"] = "mm"
74
+
75
+ config = TomoHDF5Config()
76
+ config.raises_error = raises_error
77
+ config.translation_x_keys = ("tx",)
78
+ config.translation_y_keys = ("ty",)
79
+ config.translation_z_keys = ("tz", "sz")
80
+
81
+ url = DataUrl(file_path=file_path, data_path="/entry", scheme="silx")
82
+ std_acq = BaseAcquisition(
83
+ root_url=url,
84
+ configuration=config,
85
+ detector_sel_callback=None,
86
+ start_index=0,
87
+ )
88
+ with h5py.File(file_path, mode="r") as h5f:
89
+ root_node = h5f["/entry"]
90
+ # 'tz' reports 'ERR': fall back to 'sz'
91
+ z_tr = std_acq._get_translation_z(root_node=root_node, n_frame=1)
92
+ assert z_tr.to("mm").magnitude.tolist() == [2.0]
93
+
94
+ # no valid key: same behavior as a missing translation
95
+ if raises_error:
96
+ with pytest.raises(ValueError):
97
+ std_acq._get_translation_x(root_node=root_node, n_frame=1)
98
+ with pytest.raises(ValueError):
99
+ std_acq._get_translation_y(root_node=root_node, n_frame=1)
100
+ else:
101
+ x_tr = std_acq._get_translation_x(root_node=root_node, n_frame=1)
102
+ assert x_tr.magnitude.tolist() == [0]
103
+ y_tr = std_acq._get_translation_y(root_node=root_node, n_frame=1)
104
+ assert y_tr.magnitude.tolist() == [0]
105
+
106
+
107
+ def test_tomo_config_keys_priority(tmp_path, caplog):
108
+ """
109
+ Test that keys resolved from the bliss-tomo 'tomoconfig' are looked at first (array then scalar)
110
+ and that the default keys are only used as fallback.
111
+ """
112
+ n_frame = 3
113
+ file_path = os.path.join(tmp_path, "test.h5")
114
+ with HDF5File(file_path, mode="w") as h5f:
115
+ # tomoconfig motor is static (scalar) while a default key records an array
116
+ h5f["/entry/instrument/positioners/real_sx"] = 1.0
117
+ h5f["/entry/instrument/positioners/samx"] = numpy.array([5.0, 6.0, 7.0])
118
+ # tomoconfig motor only exists in root_node while a default key exists in positioners
119
+ h5f["/entry/real_sy"] = numpy.array([1.0, 2.0, 3.0])
120
+ h5f["/entry/instrument/positioners/samy"] = numpy.array([5.0, 6.0, 7.0])
121
+ # tomoconfig motor doesn't exist: fallback on the default key
122
+ h5f["/entry/instrument/positioners/sz"] = numpy.array([5.0, 6.0, 7.0])
123
+
124
+ config = TomoHDF5Config()
125
+ config.sample_x_keys = (
126
+ _Key(dataset="real_sx", contexts=set(), resolved_from_tomo_config=True),
127
+ _Key(dataset="samx", contexts=set()),
128
+ )
129
+ config.sample_y_keys = (
130
+ _Key(dataset="real_sy", contexts=set(), resolved_from_tomo_config=True),
131
+ _Key(dataset="samy", contexts=set()),
132
+ )
133
+ config.translation_z_keys = (
134
+ _Key(dataset="real_tz", contexts=set(), resolved_from_tomo_config=True),
135
+ _Key(dataset="sz", contexts=set()),
136
+ )
137
+
138
+ url = DataUrl(file_path=file_path, data_path="/entry", scheme="silx")
139
+ std_acq = BaseAcquisition(
140
+ root_url=url,
141
+ configuration=config,
142
+ detector_sel_callback=None,
143
+ start_index=0,
144
+ )
145
+ with h5py.File(file_path, mode="r") as h5f:
146
+ root_node = h5f["/entry"]
147
+ sample_x = std_acq._get_sample_x(root_node=root_node, n_frame=n_frame)
148
+ assert sample_x.magnitude.tolist() == [1.0] * n_frame
149
+ sample_y = std_acq._get_sample_y(root_node=root_node, n_frame=n_frame)
150
+ assert sample_y.magnitude.tolist() == [1.0, 2.0, 3.0]
151
+ with caplog.at_level(logging.WARNING):
152
+ z_tr = std_acq._get_translation_z(root_node=root_node, n_frame=n_frame)
153
+ assert z_tr.magnitude.tolist() == [5.0, 6.0, 7.0]
154
+ assert "Will look at the fallback datasets" in caplog.text
155
+
156
+
157
+ def test_frame_flips(tmp_path, monkeypatch):
158
+ # make sure the result is obtained by using the "instrument/{active_config_tomo_detector}/data_axes" path.
159
+ monkeypatch.setattr(
160
+ "nxtomomill.converter.hdf5.acquisition.baseacquisition.BaseAcquisition._FRAME_FLIP_PATHS",
161
+ ("instrument/{active_config_tomo_detector}/data_axes",),
162
+ )
163
+
164
+ scan_dir = GitlabDataset.get_dataset("h5_datasets")
165
+ if scan_dir is None:
166
+ raise FileNotFoundError("Unable to download h5_datasets not found")
167
+
168
+ configuration = TomoHDF5Config()
169
+ configuration.input_file = os.path.join(
170
+ scan_dir, "frame_flip/quartz_nscope_pct_basler.h5"
171
+ )
172
+ configuration.output_file = os.path.join(tmp_path, "quartz.nx")
173
+ configuration.single_file = True
174
+
175
+ result = converter.from_h5_to_nx(configuration=configuration)
176
+ assert os.path.exists(configuration.output_file)
177
+ assert len(result) == 1
178
+
179
+ nxtomo = NXtomo().load(*result[0])
180
+
181
+ lr_flips = get_lr_flip(nxtomo.instrument.detector.transformations)
182
+ assert len(lr_flips) == 1
183
+ lr_flip = lr_flips[0]
184
+ assert lr_flip is not None and lr_flip.transformation_values == 0
185
+ ud_flips = get_ud_flip(nxtomo.instrument.detector.transformations)
186
+ assert len(ud_flips) == 1
187
+ ud_flip = ud_flips[0]
188
+ assert ud_flip is not None and ud_flip.transformation_values == 0