thyra 4.3.4__tar.gz → 4.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. {thyra-4.3.4 → thyra-4.5.0}/PKG-INFO +90 -5
  2. {thyra-4.3.4 → thyra-4.5.0}/README.md +87 -3
  3. {thyra-4.3.4 → thyra-4.5.0}/pyproject.toml +7 -69
  4. {thyra-4.3.4 → thyra-4.5.0}/thyra/__init__.py +12 -3
  5. {thyra-4.3.4 → thyra-4.5.0}/thyra/__main__.py +157 -30
  6. {thyra-4.3.4 → thyra-4.5.0}/thyra/convert.py +80 -17
  7. thyra-4.5.0/thyra/converters/spatialdata/acquisition_frame.py +130 -0
  8. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/base_spatialdata_converter.py +165 -24
  9. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/csc_assembly.py +9 -110
  10. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/fused_passes.py +16 -3
  11. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/mobility_heatmap.py +225 -59
  12. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/mobility_table.py +149 -30
  13. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/msms_table.py +45 -14
  14. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/optical_image.py +43 -5
  15. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/root_attrs.py +69 -21
  16. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/sibling_tables.py +184 -51
  17. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/streaming_converter.py +46 -7
  18. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/uns_assembler.py +87 -17
  19. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/base_reader.py +113 -7
  20. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/mobility.py +90 -7
  21. thyra-4.5.0/thyra/mass_axis_plan.py +695 -0
  22. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/constants.py +70 -1
  23. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/bruker_extractor.py +9 -0
  24. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/imzml_extractor.py +73 -3
  25. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/imzml_header_extractor.py +13 -2
  26. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/mzpeak_extractor.py +120 -3
  27. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/waters_extractor.py +25 -0
  28. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/__init__.py +8 -4
  29. thyra-4.5.0/thyra/metadata/schema/axis_fingerprint.py +30 -0
  30. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/builder.py +51 -0
  31. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/cli.py +15 -3
  32. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/models.py +151 -17
  33. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/msi_metadata.linkml.yaml +28 -2
  34. thyra-4.3.4/thyra/metadata/schema/msi_metadata_schema_v0_10.json → thyra-4.5.0/thyra/metadata/schema/msi_metadata_schema_v0_14.json +111 -6
  35. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/validate.py +77 -5
  36. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/vocab.py +32 -0
  37. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/types.py +12 -0
  38. {thyra-4.3.4 → thyra-4.5.0}/thyra/preview.py +30 -0
  39. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/rapiflex/rapiflex_reader.py +95 -2
  40. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/solarix/solarix_reader.py +17 -4
  41. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/sdk_functions.py +2 -1
  42. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/timstof_reader.py +189 -39
  43. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/imzml/header.py +76 -21
  44. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/imzml/imzml_reader.py +130 -17
  45. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/mzpeak/chunk_decoding.py +108 -21
  46. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/mzpeak/mzpeak_reader.py +604 -35
  47. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/instrument.py +20 -0
  48. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/masslynx_lib.py +92 -0
  49. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/waters_reader.py +346 -5
  50. thyra-4.5.0/thyra/regions.py +93 -0
  51. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/__init__.py +2 -0
  52. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/axis_planner.py +84 -24
  53. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/binning.py +174 -0
  54. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/gaps.py +9 -7
  55. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/instrument_detectors.py +9 -2
  56. thyra-4.5.0/thyra/resampling/interpolation.py +460 -0
  57. thyra-4.5.0/thyra/resampling/memory_guard.py +127 -0
  58. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mobility_grid.py +47 -2
  59. thyra-4.5.0/thyra/resampling/plan.py +385 -0
  60. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/strategies/tic_preserving.py +23 -4
  61. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/tic.py +4 -1
  62. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/types.py +12 -3
  63. thyra-4.3.4/thyra/resampling/interpolation.py +0 -172
  64. {thyra-4.3.4 → thyra-4.5.0}/.gitignore +0 -0
  65. {thyra-4.3.4 → thyra-4.5.0}/LICENSE +0 -0
  66. {thyra-4.3.4 → thyra-4.5.0}/thyra/alignment/__init__.py +0 -0
  67. {thyra-4.3.4 → thyra-4.5.0}/thyra/alignment/affine.py +0 -0
  68. {thyra-4.3.4 → thyra-4.5.0}/thyra/alignment/teaching_points.py +0 -0
  69. {thyra-4.3.4 → thyra-4.5.0}/thyra/config.py +0 -0
  70. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/__init__.py +0 -0
  71. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/__init__.py +0 -0
  72. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/_chunking.py +0 -0
  73. {thyra-4.3.4 → thyra-4.5.0}/thyra/converters/spatialdata/converter.py +0 -0
  74. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/__init__.py +0 -0
  75. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/base_converter.py +0 -0
  76. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/base_extractor.py +0 -0
  77. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/conversion_state.py +0 -0
  78. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/drop_tally.py +0 -0
  79. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/frames.py +0 -0
  80. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/mass_axis.py +0 -0
  81. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/msms.py +0 -0
  82. {thyra-4.3.4 → thyra-4.5.0}/thyra/core/registry.py +0 -0
  83. {thyra-4.3.4 → thyra-4.5.0}/thyra/errors.py +0 -0
  84. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/__init__.py +0 -0
  85. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/document.py +0 -0
  86. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/__init__.py +0 -0
  87. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/phi_extractor.py +0 -0
  88. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/extractors/solarix_extractor.py +0 -0
  89. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/ontology/__init__.py +0 -0
  90. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/ontology/_ims.py +0 -0
  91. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/ontology/_ms.py +0 -0
  92. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/ontology/_uo.py +0 -0
  93. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/ontology/cache.py +0 -0
  94. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/ontology/dump_obo_files.py +0 -0
  95. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/personal_data.py +0 -0
  96. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/generate.py +0 -0
  97. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/metaspace.py +0 -0
  98. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/schema/store_io.py +0 -0
  99. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/uns_compat.py +0 -0
  100. {thyra-4.3.4 → thyra-4.5.0}/thyra/metadata/validator.py +0 -0
  101. {thyra-4.3.4 → thyra-4.5.0}/thyra/py.typed +0 -0
  102. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/__init__.py +0 -0
  103. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/__init__.py +0 -0
  104. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/base_bruker_reader.py +0 -0
  105. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/folder_structure.py +0 -0
  106. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/mis_parser.py +0 -0
  107. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/rapiflex/__init__.py +0 -0
  108. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/solarix/__init__.py +0 -0
  109. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/__init__.py +0 -0
  110. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/__init__.py +0 -0
  111. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/dll/LICENCE-BRUKER.txt +0 -0
  112. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/dll/README.md +0 -0
  113. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/dll/timsdata.dll +0 -0
  114. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/dll/timsdata.so +0 -0
  115. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/dll_manager.py +0 -0
  116. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/timstof/sdk/platform_detector.py +0 -0
  117. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/bruker/vendor_db.py +0 -0
  118. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/imzml/__init__.py +0 -0
  119. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/imzml/_pyimzml_compat.py +0 -0
  120. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/imzml/mobility_array.py +0 -0
  121. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/mzpeak/__init__.py +0 -0
  122. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/phi/__init__.py +0 -0
  123. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/phi/event_stream.py +0 -0
  124. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/phi/mass_axis.py +0 -0
  125. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/phi/phi_header.py +0 -0
  126. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/phi/phi_reader.py +0 -0
  127. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/__init__.py +0 -0
  128. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/imaging_grid.py +0 -0
  129. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/lib/MLReader.dll +0 -0
  130. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/lib/MassLynxRaw.dll +0 -0
  131. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/lib/libMLReader.so +0 -0
  132. {thyra-4.3.4 → thyra-4.5.0}/thyra/readers/waters/lib/libMassLynxRaw.so +0 -0
  133. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/common_axis.py +0 -0
  134. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/constants.py +0 -0
  135. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/data_characteristics.py +0 -0
  136. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/decision_tree.py +0 -0
  137. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/__init__.py +0 -0
  138. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/base_generator.py +0 -0
  139. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/fticr_generator.py +0 -0
  140. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/linear_generator.py +0 -0
  141. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/linear_tof_generator.py +0 -0
  142. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/orbitrap_generator.py +0 -0
  143. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/reflector_tof_generator.py +0 -0
  144. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/mass_axis/tof_generator.py +0 -0
  145. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/strategies/__init__.py +0 -0
  146. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/strategies/base.py +0 -0
  147. {thyra-4.3.4 → thyra-4.5.0}/thyra/resampling/strategies/nearest_neighbor.py +0 -0
  148. {thyra-4.3.4 → thyra-4.5.0}/thyra/tools/__init__.py +0 -0
  149. {thyra-4.3.4 → thyra-4.5.0}/thyra/tools/check_ontology.py +0 -0
  150. {thyra-4.3.4 → thyra-4.5.0}/thyra/tools/make_example_data.py +0 -0
  151. {thyra-4.3.4 → thyra-4.5.0}/thyra/utils/__init__.py +0 -0
  152. {thyra-4.3.4 → thyra-4.5.0}/thyra/utils/bruker_exceptions.py +0 -0
  153. {thyra-4.3.4 → thyra-4.5.0}/thyra/utils/imzml_coordinate_base.py +0 -0
  154. {thyra-4.3.4 → thyra-4.5.0}/thyra/utils/logging_config.py +0 -0
  155. {thyra-4.3.4 → thyra-4.5.0}/thyra/utils/pyimzml_direct.py +0 -0
  156. {thyra-4.3.4 → thyra-4.5.0}/thyra/utils/windows_paths.py +0 -0
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: thyra
3
- Version: 4.3.4
3
+ Version: 4.5.0
4
4
  Summary: A modern Python library for converting Mass Spectrometry Imaging (MSI) data into SpatialData/Zarr format - your portal to spatial omics
5
5
  Project-URL: Homepage, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra
6
6
  Project-URL: Repository, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra
7
7
  Project-URL: Documentation, https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/
8
+ Project-URL: Paper, https://doi.org/10.1021/jasms.6c00169
8
9
  Project-URL: Bug Tracker, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/issues
9
10
  Project-URL: Discussions, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/discussions
10
11
  Project-URL: Changelog, https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/blob/main/CHANGELOG.md
@@ -13,7 +14,7 @@ Author-email: Theodoros Visvikis <t.visvikis@maastrichtuniversity.nl>
13
14
  Maintainer-email: Theodoros Visvikis <t.visvikis@maastrichtuniversity.nl>
14
15
  License-Expression: MIT
15
16
  License-File: LICENSE
16
- Keywords: bioinformatics,bruker,data-conversion,imaging,imzml,mass-spectrometry,msi,omics,scientific-computing,spatial-omics,spatialdata,zarr
17
+ Keywords: bioinformatics,bruker,data-conversion,imaging,imzml,ion-mobility,mass-spectrometry,msi,msms,omics,pasef,scientific-computing,spatial-omics,spatialdata,tims,timstof,zarr
17
18
  Classifier: Development Status :: 4 - Beta
18
19
  Classifier: Environment :: Console
19
20
  Classifier: Intended Audience :: Science/Research
@@ -66,8 +67,9 @@ Description-Content-Type: text/markdown
66
67
  **Thyra converts imaging mass spectrometry data into one open format.** It
67
68
  reads the files your instrument writes (Bruker, Waters, PHI, or imzML from any
68
69
  vendor) and saves them in [SpatialData](https://spatialdata.scverse.org/),
69
- which analysis and viewing tools can read. You do not need to know how to
70
- program.
70
+ which analysis and viewing tools can read. It keeps ion mobility (TIMS 1/K0)
71
+ and MS/MS (PASEF, iprm-PASEF) data, not only the spectra. You do not need to
72
+ know how to program.
71
73
 
72
74
  **[Documentation](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra)** | [Install](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/install/) | [Your first conversion](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/getting-started/) | [Tutorial](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/tutorial/) | [Technical reference](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/technical-reference/)
73
75
 
@@ -105,6 +107,10 @@ also converts the published example dataset
105
107
  | Any instrument that exports imzML | the `.imzML` file, with its `.ibd` file next to it |
106
108
  | mzPeak (experimental) | the `.mzpeak` file |
107
109
 
110
+ imzML with a mobility array is read too. In continuous mode, as TIMSImaging
111
+ writes it, the mobility is kept as its own table. In processed mode, as
112
+ TIMSCONVERT writes it, add `--mobility-grid` for that table.
113
+
108
114
  Thyra reads timsTOF and Waters data on Windows and Linux; everything else
109
115
  also works on macOS. Shimadzu support is in development: until then, export
110
116
  imzML from IMAGEREVEAL MS.
@@ -120,12 +126,57 @@ One folder ending in `.zarr`, holding:
120
126
  - the spectrum of every pixel, on one shared m/z axis
121
127
  - an image of the total ion current (TIC)
122
128
  - the optical image of the slide, when the acquisition has one
123
- - ion mobility and MS/MS data from Bruker timsTOF, when the acquisition has them
129
+ - ion mobility and MS/MS data, when the acquisition has them (below)
124
130
  - a validated description of the acquisition, ready for METASPACE
125
131
 
126
132
  Thyra handles datasets larger than your computer's memory, slides with
127
133
  several regions, and 3D data.
128
134
 
135
+ ## Ion mobility and MS/MS
136
+
137
+ Thyra keeps the extra dimensions of Bruker timsTOF data instead of dropping
138
+ them.
139
+
140
+ - **By default** each spectrum is added up over mobility, so every tool can
141
+ read the main table. The table also carries a mass-mobility heatmap of the
142
+ whole image, `uns["mobility_heatmap"]`.
143
+ - **`{table}_mobility`** has one column for each (m/z, 1/K0) pair. It is
144
+ written by itself from continuous imzML with a mobility array, and from
145
+ Bruker TDF and processed imzML with `--mobility-grid`. `var["ccs"]`
146
+ holds each pair's collision cross section (CCS) in square angstroms, for
147
+ an assumed charge of 1; `uns["ccs"]` records the assumptions.
148
+ - **`{table}_msms`** splits PASEF and iprm-PASEF MS/MS by precursor: one
149
+ block of fragment columns per precursor, so every fragment has its own
150
+ image.
151
+
152
+ Two ions at one m/z, told apart by mobility:
153
+
154
+ ```python
155
+ import numpy as np
156
+ import spatialdata as sd
157
+
158
+ sdata = sd.read_zarr("output/sample.zarr")
159
+ mob = sdata.tables["msi_dataset_z0_mobility"]
160
+ mz, k0 = mob.var["mz"].to_numpy(), mob.var["mobility"].to_numpy()
161
+ near = np.abs(mz - 600.25) < 0.01
162
+ low = np.asarray(mob.X[:, near & (k0 < 1.3)].sum(axis=1)).ravel() # one value per pixel
163
+ high = np.asarray(mob.X[:, near & (k0 >= 1.3)].sum(axis=1)).ravel()
164
+ ```
165
+
166
+ Tools should recognise these tables by their columns (`"mobility" in
167
+ var.columns`, `"precursor_mz" in var.columns`), not by their names. The
168
+ [Ion mobility and MS/MS guide](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/ion-mobility-and-msms/)
169
+ walks through both, and the output format describes the
170
+ [mobility](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/output-format/#ion-mobility)
171
+ and [MS/MS](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/output-format/#demultiplexed-msms-table)
172
+ tables in full.
173
+ [For tool developers](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/for-tool-developers/)
174
+ lists the rules a tool can rely on, and what may change.
175
+
176
+ Waters Synapt runs with ion mobility on keep their drift times; older
177
+ Synapt G1 mobility runs are refused. CCS is computed from 1/K0 only, never
178
+ from a drift time.
179
+
129
180
  ## From Python
130
181
 
131
182
  ```python
@@ -172,6 +223,30 @@ MIT -- see [LICENSE](LICENSE).
172
223
 
173
224
  ## Citation
174
225
 
226
+ If you use Thyra, please cite the paper:
227
+
228
+ > Visvikis, T.; Vierdag, W.-M.; Marconato, L.; Birmpili, A.; Heeren, R. M. A.;
229
+ > Cuypers, E. Native, Memory-Bounded Conversion of Mass Spectrometry Imaging
230
+ > Data into the SpatialData Ecosystem. *J. Am. Soc. Mass Spectrom.* **2026**,
231
+ > *37* (10), 2379-2387.
232
+ > [doi:10.1021/jasms.6c00169](https://doi.org/10.1021/jasms.6c00169)
233
+
234
+ ```bibtex
235
+ @article{visvikis2026thyra,
236
+ title = {Native, Memory-Bounded Conversion of Mass Spectrometry Imaging Data into the {SpatialData} Ecosystem},
237
+ author = {Visvikis, Theodoros and Vierdag, Wouter-Michiel and Marconato, Luca and Birmpili, Angeliki and Heeren, Ron M. A. and Cuypers, Eva},
238
+ journal = {Journal of the American Society for Mass Spectrometry},
239
+ year = {2026},
240
+ volume = {37},
241
+ number = {10},
242
+ pages = {2379--2387},
243
+ doi = {10.1021/jasms.6c00169}
244
+ }
245
+ ```
246
+
247
+ The same entry is in [CITATION.cff](CITATION.cff), which GitHub's "Cite this
248
+ repository" button reads. To cite the software itself:
249
+
175
250
  ```bibtex
176
251
  @software{thyra2024,
177
252
  title = {Thyra: Modern Mass Spectrometry Imaging Data Conversion},
@@ -181,6 +256,16 @@ MIT -- see [LICENSE](LICENSE).
181
256
  }
182
257
  ```
183
258
 
259
+ ### Papers
260
+
261
+ | Paper | Published | DOI |
262
+ |---|---|---|
263
+ | Native, Memory-Bounded Conversion of Mass Spectrometry Imaging Data into the SpatialData Ecosystem (*J. Am. Soc. Mass Spectrom.*) | 2026-09 | [10.1021/jasms.6c00169](https://doi.org/10.1021/jasms.6c00169) |
264
+ | Thyra: Bridging Mass Spectrometry Imaging and SpatialData for Unified Multi-Modal Analysis (bioRxiv preprint) | 2026-01 | [10.64898/2026.01.26.700792](https://doi.org/10.64898/2026.01.26.700792) |
265
+
266
+ Ion mobility and MS/MS support came after both papers. If you use them,
267
+ please also name the Thyra version you used.
268
+
184
269
  ## Acknowledgments
185
270
 
186
271
  - Built with [SpatialData](https://spatialdata.scverse.org/) ecosystem
@@ -13,8 +13,9 @@
13
13
  **Thyra converts imaging mass spectrometry data into one open format.** It
14
14
  reads the files your instrument writes (Bruker, Waters, PHI, or imzML from any
15
15
  vendor) and saves them in [SpatialData](https://spatialdata.scverse.org/),
16
- which analysis and viewing tools can read. You do not need to know how to
17
- program.
16
+ which analysis and viewing tools can read. It keeps ion mobility (TIMS 1/K0)
17
+ and MS/MS (PASEF, iprm-PASEF) data, not only the spectra. You do not need to
18
+ know how to program.
18
19
 
19
20
  **[Documentation](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra)** | [Install](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/install/) | [Your first conversion](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/getting-started/) | [Tutorial](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/tutorial/) | [Technical reference](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/technical-reference/)
20
21
 
@@ -52,6 +53,10 @@ also converts the published example dataset
52
53
  | Any instrument that exports imzML | the `.imzML` file, with its `.ibd` file next to it |
53
54
  | mzPeak (experimental) | the `.mzpeak` file |
54
55
 
56
+ imzML with a mobility array is read too. In continuous mode, as TIMSImaging
57
+ writes it, the mobility is kept as its own table. In processed mode, as
58
+ TIMSCONVERT writes it, add `--mobility-grid` for that table.
59
+
55
60
  Thyra reads timsTOF and Waters data on Windows and Linux; everything else
56
61
  also works on macOS. Shimadzu support is in development: until then, export
57
62
  imzML from IMAGEREVEAL MS.
@@ -67,12 +72,57 @@ One folder ending in `.zarr`, holding:
67
72
  - the spectrum of every pixel, on one shared m/z axis
68
73
  - an image of the total ion current (TIC)
69
74
  - the optical image of the slide, when the acquisition has one
70
- - ion mobility and MS/MS data from Bruker timsTOF, when the acquisition has them
75
+ - ion mobility and MS/MS data, when the acquisition has them (below)
71
76
  - a validated description of the acquisition, ready for METASPACE
72
77
 
73
78
  Thyra handles datasets larger than your computer's memory, slides with
74
79
  several regions, and 3D data.
75
80
 
81
+ ## Ion mobility and MS/MS
82
+
83
+ Thyra keeps the extra dimensions of Bruker timsTOF data instead of dropping
84
+ them.
85
+
86
+ - **By default** each spectrum is added up over mobility, so every tool can
87
+ read the main table. The table also carries a mass-mobility heatmap of the
88
+ whole image, `uns["mobility_heatmap"]`.
89
+ - **`{table}_mobility`** has one column for each (m/z, 1/K0) pair. It is
90
+ written by itself from continuous imzML with a mobility array, and from
91
+ Bruker TDF and processed imzML with `--mobility-grid`. `var["ccs"]`
92
+ holds each pair's collision cross section (CCS) in square angstroms, for
93
+ an assumed charge of 1; `uns["ccs"]` records the assumptions.
94
+ - **`{table}_msms`** splits PASEF and iprm-PASEF MS/MS by precursor: one
95
+ block of fragment columns per precursor, so every fragment has its own
96
+ image.
97
+
98
+ Two ions at one m/z, told apart by mobility:
99
+
100
+ ```python
101
+ import numpy as np
102
+ import spatialdata as sd
103
+
104
+ sdata = sd.read_zarr("output/sample.zarr")
105
+ mob = sdata.tables["msi_dataset_z0_mobility"]
106
+ mz, k0 = mob.var["mz"].to_numpy(), mob.var["mobility"].to_numpy()
107
+ near = np.abs(mz - 600.25) < 0.01
108
+ low = np.asarray(mob.X[:, near & (k0 < 1.3)].sum(axis=1)).ravel() # one value per pixel
109
+ high = np.asarray(mob.X[:, near & (k0 >= 1.3)].sum(axis=1)).ravel()
110
+ ```
111
+
112
+ Tools should recognise these tables by their columns (`"mobility" in
113
+ var.columns`, `"precursor_mz" in var.columns`), not by their names. The
114
+ [Ion mobility and MS/MS guide](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/ion-mobility-and-msms/)
115
+ walks through both, and the output format describes the
116
+ [mobility](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/output-format/#ion-mobility)
117
+ and [MS/MS](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/output-format/#demultiplexed-msms-table)
118
+ tables in full.
119
+ [For tool developers](https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/for-tool-developers/)
120
+ lists the rules a tool can rely on, and what may change.
121
+
122
+ Waters Synapt runs with ion mobility on keep their drift times; older
123
+ Synapt G1 mobility runs are refused. CCS is computed from 1/K0 only, never
124
+ from a drift time.
125
+
76
126
  ## From Python
77
127
 
78
128
  ```python
@@ -119,6 +169,30 @@ MIT -- see [LICENSE](LICENSE).
119
169
 
120
170
  ## Citation
121
171
 
172
+ If you use Thyra, please cite the paper:
173
+
174
+ > Visvikis, T.; Vierdag, W.-M.; Marconato, L.; Birmpili, A.; Heeren, R. M. A.;
175
+ > Cuypers, E. Native, Memory-Bounded Conversion of Mass Spectrometry Imaging
176
+ > Data into the SpatialData Ecosystem. *J. Am. Soc. Mass Spectrom.* **2026**,
177
+ > *37* (10), 2379-2387.
178
+ > [doi:10.1021/jasms.6c00169](https://doi.org/10.1021/jasms.6c00169)
179
+
180
+ ```bibtex
181
+ @article{visvikis2026thyra,
182
+ title = {Native, Memory-Bounded Conversion of Mass Spectrometry Imaging Data into the {SpatialData} Ecosystem},
183
+ author = {Visvikis, Theodoros and Vierdag, Wouter-Michiel and Marconato, Luca and Birmpili, Angeliki and Heeren, Ron M. A. and Cuypers, Eva},
184
+ journal = {Journal of the American Society for Mass Spectrometry},
185
+ year = {2026},
186
+ volume = {37},
187
+ number = {10},
188
+ pages = {2379--2387},
189
+ doi = {10.1021/jasms.6c00169}
190
+ }
191
+ ```
192
+
193
+ The same entry is in [CITATION.cff](CITATION.cff), which GitHub's "Cite this
194
+ repository" button reads. To cite the software itself:
195
+
122
196
  ```bibtex
123
197
  @software{thyra2024,
124
198
  title = {Thyra: Modern Mass Spectrometry Imaging Data Conversion},
@@ -128,6 +202,16 @@ MIT -- see [LICENSE](LICENSE).
128
202
  }
129
203
  ```
130
204
 
205
+ ### Papers
206
+
207
+ | Paper | Published | DOI |
208
+ |---|---|---|
209
+ | Native, Memory-Bounded Conversion of Mass Spectrometry Imaging Data into the SpatialData Ecosystem (*J. Am. Soc. Mass Spectrom.*) | 2026-09 | [10.1021/jasms.6c00169](https://doi.org/10.1021/jasms.6c00169) |
210
+ | Thyra: Bridging Mass Spectrometry Imaging and SpatialData for Unified Multi-Modal Analysis (bioRxiv preprint) | 2026-01 | [10.64898/2026.01.26.700792](https://doi.org/10.64898/2026.01.26.700792) |
211
+
212
+ Ion mobility and MS/MS support came after both papers. If you use them,
213
+ please also name the Thyra version you used.
214
+
131
215
  ## Acknowledgments
132
216
 
133
217
  - Built with [SpatialData](https://spatialdata.scverse.org/) ecosystem
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thyra"
7
- version = "4.3.4"
7
+ version = "4.5.0"
8
8
  description = "A modern Python library for converting Mass Spectrometry Imaging (MSI) data into SpatialData/Zarr format - your portal to spatial omics"
9
9
  authors = [{ name = "Theodoros Visvikis", email = "t.visvikis@maastrichtuniversity.nl" }]
10
10
  maintainers = [{ name = "Theodoros Visvikis", email = "t.visvikis@maastrichtuniversity.nl" }]
@@ -22,6 +22,11 @@ keywords = [
22
22
  "msi",
23
23
  "imzml",
24
24
  "bruker",
25
+ "timstof",
26
+ "ion-mobility",
27
+ "tims",
28
+ "msms",
29
+ "pasef",
25
30
  "spatial-omics",
26
31
  "data-conversion",
27
32
  "scientific-computing",
@@ -243,6 +248,7 @@ dependencies = [
243
248
  Homepage = "https://github.com/M4i-Imaging-Mass-Spectrometry/thyra"
244
249
  Repository = "https://github.com/M4i-Imaging-Mass-Spectrometry/thyra"
245
250
  Documentation = "https://M4i-Imaging-Mass-Spectrometry.github.io/thyra/"
251
+ Paper = "https://doi.org/10.1021/jasms.6c00169"
246
252
  "Bug Tracker" = "https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/issues"
247
253
  Discussions = "https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/discussions"
248
254
  Changelog = "https://github.com/M4i-Imaging-Mass-Spectrometry/thyra/blob/main/CHANGELOG.md"
@@ -310,13 +316,6 @@ dev = [
310
316
  "flake8>=6.0.0",
311
317
  "mypy>=1.0.0",
312
318
  "pydocstyle>=6.3.0",
313
- # This once capped `click <8.2`, and the note here said that dev-only cap
314
- # was what held the lockfile's click at 8.1.8. Both halves are stale:
315
- # 10.6.1, the locked version, requires `click>=8.1.0,<9.0.0`, and uv.lock
316
- # resolves click 8.4.2. So the lockfile no longer exercises the floor of
317
- # the `>=8.1, <9` range [project] publishes -- see the click comment above
318
- # for which versions were verified by hand instead.
319
- "python-semantic-release",
320
319
  "python-dotenv>=1.1.1,<2",
321
320
  "pre-commit>=3.0.0",
322
321
  "bandit[toml]>=1.7.0",
@@ -355,67 +354,6 @@ filterwarnings = [
355
354
  "ignore::UserWarning:spatialdata.models.models:1053"
356
355
  ]
357
356
 
358
- # =============================================================
359
- # === SEMANTIC RELEASE CONFIGURATION ===
360
- # =============================================================
361
- [tool.semantic_release]
362
- version_toml = ["pyproject.toml:project.version"]
363
- version_variables = [
364
- "thyra/__init__.py:__version__",
365
- ]
366
- # `uv lock` runs here rather than as a workflow step because the build
367
- # command is the only hook that executes AFTER semantic-release stamps the
368
- # new version into pyproject.toml and BEFORE it makes the release commit.
369
- # Without it, the lockfile's own `[[package]] name = "thyra"` entry keeps
370
- # the previous version. The release commit lands on main without a test run
371
- # (a GITHUB_TOKEN push starts no workflows), so nothing notices until the
372
- # first branch cut after the release fails every CI job at
373
- # `uv sync --locked` with "The lockfile at `uv.lock` needs to be updated" --
374
- # see the one-off repair on PR #177 after v3.5.0. Regenerating the lockfile
375
- # is only half the fix: `assets` below is what carries it into the release
376
- # commit. uv reaches this command's PATH via the same SHA-pinned setup-uv
377
- # step the test workflow uses (see release.yml).
378
- build_command = "uv lock && pip install build && python -m build"
379
- # Extra files to include in the release commit beyond the version-stamped
380
- # ones above.
381
- assets = ["uv.lock"]
382
- # There is deliberately no `upload_to_vcs_release` here. It used to sit at this
383
- # level and had never had any effect, for three independent reasons: it is a
384
- # field of PublishConfig, so it belongs under [tool.semantic_release.publish] and
385
- # is silently dropped here (RawConfig sets no `extra` policy, so pydantic ignores
386
- # unknown keys); its default is already true; and it is acted on only by
387
- # `semantic-release publish`, which calls hvcs_client.upload_dists, while
388
- # release.yml only ever runs `semantic-release version`, which never does.
389
- # Verified against python-semantic-release 10.6.2. The visible consequence was
390
- # that the releases for v3.23.0-v3.23.2 each carry exactly one asset, uv.lock,
391
- # and no wheel or sdist.
392
- #
393
- # The dist this build_command produces is not lost, though. release.yml uploads
394
- # it as a workflow artifact and the publish job takes it from there, which is
395
- # what makes one build per release. If you want it on the GitHub Release as well,
396
- # that is an upload step in release.yml, not this key.
397
-
398
- [tool.semantic_release.branches.main]
399
- match = "main"
400
- prerelease = false
401
-
402
- [tool.semantic_release.changelog]
403
- template_dir = "templates"
404
- changelog_file = "CHANGELOG.md"
405
- exclude_commit_patterns = [
406
- "chore*",
407
- "ci*",
408
- ]
409
-
410
- [tool.semantic_release.commit_parser_options]
411
- allowed_tags = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test"]
412
- minor_tags = ["feat"]
413
- patch_tags = ["fix", "perf"]
414
-
415
- [tool.semantic_release.remote_settings]
416
- name = "origin"
417
- token = { env = "GITHUB_TOKEN" }
418
-
419
357
  # =============================================================
420
358
  # === CODE QUALITY TOOL CONFIGURATIONS ===
421
359
  # =============================================================
@@ -21,8 +21,8 @@ The public names below resolve on first attribute access (:pep:`562`), so
21
21
  still cost what they always did -- paid by the caller who asks for them.
22
22
 
23
23
  ``__version__`` stays an ordinary module attribute rather than joining
24
- them. It is what ``python-semantic-release`` rewrites at release
25
- (``version_variables`` in ``pyproject.toml``), and
24
+ them. It is what release-please rewrites in the release pull request
25
+ (the ``python`` release type in ``release-please-config.json``), and
26
26
  ``thyra.metadata.schema.builder`` reads it through ``from thyra import
27
27
  __version__`` while building a document -- which is only affordable
28
28
  because reaching this module is now free.
@@ -51,8 +51,11 @@ from typing import TYPE_CHECKING, Any, List
51
51
  if TYPE_CHECKING: # pragma: no cover - for type checkers, not at runtime
52
52
  from .convert import convert_msi
53
53
  from .converters.spatialdata.converter import SpatialDataConverter
54
+ from .mass_axis_plan import plan_mass_axis
54
55
  from .metadata.document import read_metadata_document
55
56
  from .preview import MsiPreview, preview_msi
57
+ from .regions import list_regions
58
+ from .resampling.plan import MassAxisPlan
56
59
 
57
60
  # Suppress remaining dependency warnings
58
61
  warnings.filterwarnings("ignore", category=FutureWarning, module="dask")
@@ -67,13 +70,16 @@ warnings.filterwarnings(
67
70
  category=FutureWarning,
68
71
  )
69
72
 
70
- __version__ = "4.3.4"
73
+ __version__ = "4.5.0"
71
74
 
72
75
  #: Public name -> the module that defines it, imported on first access.
73
76
  _LAZY_ATTRS = {
77
+ "MassAxisPlan": ".resampling.plan",
74
78
  "MsiPreview": ".preview",
75
79
  "SpatialDataConverter": ".converters.spatialdata.converter",
76
80
  "convert_msi": ".convert",
81
+ "list_regions": ".regions",
82
+ "plan_mass_axis": ".mass_axis_plan",
77
83
  "preview_msi": ".preview",
78
84
  "read_metadata_document": ".metadata.document",
79
85
  }
@@ -88,7 +94,10 @@ __all__ = [
88
94
  "__version__",
89
95
  "convert_msi",
90
96
  "preview_msi",
97
+ "list_regions",
98
+ "plan_mass_axis",
91
99
  "read_metadata_document",
100
+ "MassAxisPlan",
92
101
  "MsiPreview",
93
102
  "SpatialDataConverter",
94
103
  ]