pyMapVBVD 0.6.1__tar.gz → 0.7.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 (51) hide show
  1. pymapvbvd-0.7.0/.gitattributes +3 -0
  2. pymapvbvd-0.7.0/.github/workflows/publish.yml +17 -0
  3. pymapvbvd-0.7.0/.github/workflows/push_pr.yml +49 -0
  4. pymapvbvd-0.7.0/.gitignore +11 -0
  5. pymapvbvd-0.7.0/.zenodo.json +17 -0
  6. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/CHANGELOG.md +6 -0
  7. pymapvbvd-0.7.0/Demo.ipynb +309 -0
  8. pymapvbvd-0.7.0/MANIFEST.in +2 -0
  9. {pymapvbvd-0.6.1/pyMapVBVD.egg-info → pymapvbvd-0.7.0}/PKG-INFO +25 -10
  10. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/README.md +11 -0
  11. pymapvbvd-0.7.0/build_process.md +9 -0
  12. pymapvbvd-0.7.0/mapvbvd/__init__.py +8 -0
  13. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/mapvbvd/twix_map_obj.py +7 -2
  14. pymapvbvd-0.7.0/pixi.lock +2581 -0
  15. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0/pyMapVBVD.egg-info}/PKG-INFO +25 -10
  16. pymapvbvd-0.7.0/pyMapVBVD.egg-info/SOURCES.txt +40 -0
  17. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/pyMapVBVD.egg-info/requires.txt +6 -0
  18. pymapvbvd-0.7.0/pyMapVBVD.egg-info/scm_file_list.json +37 -0
  19. pymapvbvd-0.7.0/pyMapVBVD.egg-info/scm_version.json +8 -0
  20. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/pyMapVBVD.egg-info/top_level.txt +1 -0
  21. pymapvbvd-0.7.0/pyproject.toml +73 -0
  22. pymapvbvd-0.7.0/setup.cfg +4 -0
  23. pymapvbvd-0.7.0/submission.md +6 -0
  24. pymapvbvd-0.7.0/tests/__init__.py +4 -0
  25. pymapvbvd-0.7.0/tests/test_data/meas_MID00255_FID12798_GRE_surf.dat +3 -0
  26. pymapvbvd-0.7.0/tests/test_data/meas_MID00255_FID12798_GRE_surf.mat +3 -0
  27. pymapvbvd-0.7.0/tests/test_data/meas_MID00265_FID12808_FMRI.dat +3 -0
  28. pymapvbvd-0.7.0/tests/test_data/meas_MID00265_FID12808_FMRI.mat +3 -0
  29. pymapvbvd-0.7.0/tests/test_data/meas_MID00305_FID74175_VOI_slaser_wref1.dat +3 -0
  30. pymapvbvd-0.7.0/tests/test_data/meas_MID00305_FID74175_VOI_slaser_wref1.mat +3 -0
  31. pymapvbvd-0.7.0/tests/test_data/meas_MID111_sLaser_broken_FID4873.dat +3 -0
  32. pymapvbvd-0.7.0/tests/test_data/meas_MID311_STEAM_wref1_FID115674.dat +3 -0
  33. pymapvbvd-0.7.0/tests/test_data/meas_MID311_STEAM_wref1_FID115674.mat +3 -0
  34. pymapvbvd-0.6.1/MANIFEST.in +0 -5
  35. pymapvbvd-0.6.1/mapvbvd/__init__.py +0 -4
  36. pymapvbvd-0.6.1/mapvbvd/_version.py +0 -21
  37. pymapvbvd-0.6.1/pyMapVBVD.egg-info/SOURCES.txt +0 -24
  38. pymapvbvd-0.6.1/pyproject.toml +0 -3
  39. pymapvbvd-0.6.1/requirements.yml +0 -7
  40. pymapvbvd-0.6.1/setup.cfg +0 -25
  41. pymapvbvd-0.6.1/setup.py +0 -40
  42. pymapvbvd-0.6.1/versioneer.py +0 -2205
  43. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/LICENSE +0 -0
  44. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/mapvbvd/_attrdict.py +0 -0
  45. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/mapvbvd/mapVBVD.py +0 -0
  46. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/mapvbvd/read_twix_hdr.py +0 -0
  47. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/pyMapVBVD.egg-info/dependency_links.txt +0 -0
  48. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/tests/test_flags.py +0 -0
  49. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/tests/test_read.py +0 -0
  50. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/tests/test_slicing.py +0 -0
  51. {pymapvbvd-0.6.1 → pymapvbvd-0.7.0}/tests/test_svs.py +0 -0
@@ -0,0 +1,3 @@
1
+ mapVBVD/_version.py export-subst
2
+ *.dat filter=lfs diff=lfs merge=lfs -text
3
+ *.mat filter=lfs diff=lfs merge=lfs -text
@@ -0,0 +1,17 @@
1
+ name: Publish to PyPI.org
2
+ on:
3
+ release:
4
+ types: [published]
5
+ jobs:
6
+ pypi:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - name: Checkout
10
+ uses: actions/checkout@v4
11
+ with:
12
+ fetch-depth: 0
13
+ - run: python3 -m pip install --upgrade build && python3 -m build
14
+ - name: Publish package
15
+ uses: pypa/gh-action-pypi-publish@release/v1
16
+ with:
17
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,49 @@
1
+ name: Run code tests on push and pull requests
2
+ on:
3
+ push:
4
+ pull_request:
5
+ workflow_dispatch:
6
+
7
+ jobs:
8
+ push-tests:
9
+ if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
10
+ name: Pixi (py314, ubuntu-latest)
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+ with:
15
+ submodules: true
16
+ lfs: true
17
+ - uses: prefix-dev/setup-pixi@v0.9.4
18
+ with:
19
+ manifest-path: pyproject.toml
20
+ environments: py314
21
+ cache: false
22
+ activate-environment: false
23
+ - name: Lint
24
+ run: pixi run lint
25
+ - name: Run pytest
26
+ run: pixi run -e py314 test
27
+
28
+ pr-tests:
29
+ if: github.event_name == 'pull_request'
30
+ name: Pixi (${{ matrix.environment }}, ${{ matrix.os }})
31
+ runs-on: ${{ matrix.os }}
32
+ strategy:
33
+ fail-fast: true
34
+ matrix:
35
+ os: ["ubuntu-latest"]
36
+ environment: ["py313", "py314"]
37
+ steps:
38
+ - uses: actions/checkout@v6
39
+ with:
40
+ submodules: true
41
+ lfs: true
42
+ - uses: prefix-dev/setup-pixi@v0.9.4
43
+ with:
44
+ manifest-path: pyproject.toml
45
+ environments: ${{ matrix.environment }}
46
+ cache: false
47
+ activate-environment: false
48
+ - name: Run pytest
49
+ run: pixi run -e ${{ matrix.environment }} test
@@ -0,0 +1,11 @@
1
+ .ipynb_checkpoints
2
+ *.ipynb
3
+ *.code-workspace
4
+ build
5
+ dist
6
+ *.egg-info
7
+ __pycache__
8
+ .DS_Store
9
+ .idea/*
10
+ .eggs/
11
+ .pixi/
@@ -0,0 +1,17 @@
1
+ {
2
+ "description": "<p>pyMapVBVD is a python port of the Matlab mapVBVD tool for reading Siemens raw data 'twix' (.dat) files.</p>\n\n<p>pyMapVBVD is developed at the Wellcome Centre for Integrative Neuroimaging (FMRIB), at the University of Oxford. It is hosted at&nbsp;<a href=\"https://github.com/wtclarke/pymapvbvd\">github.com/wtclarke/pymapvbvd</a>.</p>",
3
+ "license": "MIT",
4
+ "title": "pyMapVBVD",
5
+ "keywords": [ "MRS" ],
6
+ "creators": [
7
+ {
8
+ "orcid": "0000-0001-7159-7025",
9
+ "affiliation": "University of Oxford",
10
+ "name": "William T Clarke"
11
+ }
12
+ ],
13
+ "access_right": "open",
14
+ "grants": [
15
+ { "id": "10.13039/100004440::203139"}
16
+ ]
17
+ }
@@ -1,5 +1,11 @@
1
1
  This document contains the pymapvbvd release history in reverse chronological order.
2
2
 
3
+ 0.7.0 (Thursday 1st October 2026)
4
+ ----------------------------------
5
+ - Move to `pixi` local build
6
+ - Remove extra deepcopy
7
+ - Fix issues with overflow of uint16 for very large acquisition - Thanks to @chaithyagr
8
+
3
9
  0.6.1 (Thursday 31st October 2024)
4
10
  ----------------------------------
5
11
  - Testing on Python 3.13
@@ -0,0 +1,309 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# pyMapVBVD Demo"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "#### Imports"
15
+ ]
16
+ },
17
+ {
18
+ "cell_type": "code",
19
+ "execution_count": 1,
20
+ "metadata": {
21
+ "tags": []
22
+ },
23
+ "outputs": [],
24
+ "source": [
25
+ "%load_ext autoreload\n",
26
+ "%autoreload 2\n",
27
+ "from mapvbvd import mapVBVD as mapVBVD\n",
28
+ "import numpy as np"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "markdown",
33
+ "metadata": {},
34
+ "source": [
35
+ "#### Load VB file"
36
+ ]
37
+ },
38
+ {
39
+ "cell_type": "code",
40
+ "execution_count": 2,
41
+ "metadata": {
42
+ "tags": []
43
+ },
44
+ "outputs": [
45
+ {
46
+ "name": "stderr",
47
+ "output_type": "stream",
48
+ "text": [
49
+ "Scan 1/1, read all mdhs: 71%|███████▏ | 2.51M/3.52M [00:00<00:00, 979MB/s]"
50
+ ]
51
+ },
52
+ {
53
+ "name": "stdout",
54
+ "output_type": "stream",
55
+ "text": [
56
+ "pymapVBVD version 0.3.2+44.g33ddc7b.dirty\n",
57
+ "Software version: VB\n"
58
+ ]
59
+ },
60
+ {
61
+ "name": "stderr",
62
+ "output_type": "stream",
63
+ "text": [
64
+ "\n"
65
+ ]
66
+ }
67
+ ],
68
+ "source": [
69
+ "vbfile = 'tests/test_data/meas_MID311_STEAM_wref1_FID115674.dat'\n",
70
+ "twixObj = mapVBVD(vbfile)"
71
+ ]
72
+ },
73
+ {
74
+ "cell_type": "markdown",
75
+ "metadata": {},
76
+ "source": [
77
+ "#### Show file contents"
78
+ ]
79
+ },
80
+ {
81
+ "cell_type": "code",
82
+ "execution_count": 3,
83
+ "metadata": {
84
+ "tags": []
85
+ },
86
+ "outputs": [
87
+ {
88
+ "name": "stdout",
89
+ "output_type": "stream",
90
+ "text": [
91
+ "MDH: image\n",
92
+ "***twix_map_obj***\n",
93
+ "File: tests/test_data/meas_MID311_STEAM_wref1_FID115674.dat\n",
94
+ "Software: vb\n",
95
+ "Number of acquisitions read 2\n",
96
+ "Data size is [4096, 32, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1,\n",
97
+ " 1, 1]\n",
98
+ "Squeezed data size is [2048,32,2] (['Col', 'Cha', 'Rep'])\n",
99
+ "NCol = 4096\n",
100
+ "NCha = 32\n",
101
+ "NLin = 1\n",
102
+ "NAve = 1\n",
103
+ "NSli = 1\n",
104
+ "NPar = 1\n",
105
+ "NEco = 1\n",
106
+ "NPhs = 1\n",
107
+ "NRep = 2\n",
108
+ "NSet = 1\n",
109
+ "NSeg = 1\n",
110
+ "NIda = 1\n",
111
+ "NIdb = 1\n",
112
+ "NIdc = 1\n",
113
+ "NIdd = 1\n",
114
+ "NIde = 1\n",
115
+ "\n",
116
+ "\n",
117
+ "MDH: phasecor\n",
118
+ "***twix_map_obj***\n",
119
+ "File: tests/test_data/meas_MID311_STEAM_wref1_FID115674.dat\n",
120
+ "Software: vb\n",
121
+ "Number of acquisitions read 1\n",
122
+ "Data size is [4096, 32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n",
123
+ " 1, 1]\n",
124
+ "Squeezed data size is [2048,32] (['Col', 'Cha'])\n",
125
+ "NCol = 4096\n",
126
+ "NCha = 32\n",
127
+ "NLin = 1\n",
128
+ "NAve = 1\n",
129
+ "NSli = 1\n",
130
+ "NPar = 1\n",
131
+ "NEco = 1\n",
132
+ "NPhs = 1\n",
133
+ "NRep = 1\n",
134
+ "NSet = 1\n",
135
+ "NSeg = 1\n",
136
+ "NIda = 1\n",
137
+ "NIdb = 1\n",
138
+ "NIdc = 1\n",
139
+ "NIdd = 1\n",
140
+ "NIde = 1\n",
141
+ "\n",
142
+ "\n"
143
+ ]
144
+ }
145
+ ],
146
+ "source": [
147
+ "mdh_flags = twixObj.MDH_flags()\n",
148
+ "for mdh in mdh_flags:\n",
149
+ " print(f'MDH: {mdh}')\n",
150
+ " print(twixObj[mdh])\n",
151
+ " print('\\n')"
152
+ ]
153
+ },
154
+ {
155
+ "cell_type": "markdown",
156
+ "metadata": {},
157
+ "source": [
158
+ "#### View data"
159
+ ]
160
+ },
161
+ {
162
+ "cell_type": "code",
163
+ "execution_count": 4,
164
+ "metadata": {
165
+ "tags": []
166
+ },
167
+ "outputs": [
168
+ {
169
+ "name": "stderr",
170
+ "output_type": "stream",
171
+ "text": [
172
+ " \r"
173
+ ]
174
+ },
175
+ {
176
+ "name": "stdout",
177
+ "output_type": "stream",
178
+ "text": [
179
+ "(2048, 32, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1)\n",
180
+ "(2048, 32, 2)\n",
181
+ "(2000, 8)\n",
182
+ "(2048, 32, 2)\n"
183
+ ]
184
+ },
185
+ {
186
+ "data": {
187
+ "image/png": "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\n",
188
+ "text/plain": [
189
+ "<Figure size 432x288 with 1 Axes>"
190
+ ]
191
+ },
192
+ "metadata": {
193
+ "needs_background": "light"
194
+ },
195
+ "output_type": "display_data"
196
+ }
197
+ ],
198
+ "source": [
199
+ "# You can retrieve all of the data\n",
200
+ "twixObj.image.squeeze = False\n",
201
+ "out = twixObj.image['']\n",
202
+ "print(out.shape)\n",
203
+ "\n",
204
+ "# Which can be squeezed\n",
205
+ "twixObj.image.squeeze = True\n",
206
+ "out = twixObj.image['']\n",
207
+ "print(out.shape)\n",
208
+ "\n",
209
+ "# Or you can select a subset of the data\n",
210
+ "twixObj.image.squeeze = True\n",
211
+ "out = twixObj.image[0:2000,0:32:4,1]\n",
212
+ "print(out.shape)\n",
213
+ "\n",
214
+ "# Or you can retrieve the unsorted data (not very interesting in this example)\n",
215
+ "out_unsorted = twixObj.image.unsorted()\n",
216
+ "print(out_unsorted.shape)\n",
217
+ "\n",
218
+ "%matplotlib inline\n",
219
+ "import matplotlib.pyplot as plt\n",
220
+ "plt.plot(np.real(out))\n",
221
+ "plt.show()"
222
+ ]
223
+ },
224
+ {
225
+ "cell_type": "markdown",
226
+ "metadata": {},
227
+ "source": [
228
+ "#### View header information"
229
+ ]
230
+ },
231
+ {
232
+ "cell_type": "code",
233
+ "execution_count": 5,
234
+ "metadata": {},
235
+ "outputs": [
236
+ {
237
+ "name": "stdout",
238
+ "output_type": "stream",
239
+ "text": [
240
+ "MeasYaps:\n",
241
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'tNucleus'): \"1H\"\n",
242
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'lFrequency'): 297219948.0\n",
243
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'flReferenceAmplitude'): 225.0\n",
244
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'flAmplitudeCorrection'): 1.0\n",
245
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'flAmplitudeCorrection'): 1.0\n",
246
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'lRFPAIndex'): -1.0\n",
247
+ "Phoenix:\n",
248
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'tNucleus'): \"1H\"\n",
249
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'lFrequency'): 297219948.0\n",
250
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'bFrequencyValid'): 1.0\n",
251
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'flReferenceAmplitude'): 225.0\n",
252
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'bReferenceAmplitudeValid'): 1.0\n",
253
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'flAmplitudeCorrection'): 1.0\n",
254
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'bAmplitudeCorrectionValid'): 1.0\n",
255
+ "\t('sTXSPEC', 'asNucleusInfo', '0', 'bRFPAIndexValid'): 1.0\n",
256
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'bFrequencyValid'): 1.0\n",
257
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'bReferenceAmplitudeValid'): 1.0\n",
258
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'flAmplitudeCorrection'): 1.0\n",
259
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'bAmplitudeCorrectionValid'): 1.0\n",
260
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'lRFPAIndex'): -1.0\n",
261
+ "\t('sTXSPEC', 'asNucleusInfo', '1', 'bRFPAIndexValid'): 1.0\n"
262
+ ]
263
+ }
264
+ ],
265
+ "source": [
266
+ "found_keys = twixObj.search_header_for_keys(('TXSPEC', 'asNucleusInfo'),top_lvl=['MeasYaps','Phoenix'])"
267
+ ]
268
+ },
269
+ {
270
+ "cell_type": "code",
271
+ "execution_count": 6,
272
+ "metadata": {},
273
+ "outputs": [
274
+ {
275
+ "name": "stdout",
276
+ "output_type": "stream",
277
+ "text": [
278
+ "['\"1H\"']\n"
279
+ ]
280
+ }
281
+ ],
282
+ "source": [
283
+ "value = twixObj.search_header_for_val('MeasYaps',('sTXSPEC', 'asNucleusInfo', '0', 'tNucleus'))\n",
284
+ "print(value)"
285
+ ]
286
+ }
287
+ ],
288
+ "metadata": {
289
+ "kernelspec": {
290
+ "display_name": "Python 3",
291
+ "language": "python",
292
+ "name": "python3"
293
+ },
294
+ "language_info": {
295
+ "codemirror_mode": {
296
+ "name": "ipython",
297
+ "version": 3
298
+ },
299
+ "file_extension": ".py",
300
+ "mimetype": "text/x-python",
301
+ "name": "python",
302
+ "nbconvert_exporter": "python",
303
+ "pygments_lexer": "ipython3",
304
+ "version": "3.7.7"
305
+ }
306
+ },
307
+ "nbformat": 4,
308
+ "nbformat_minor": 4
309
+ }
@@ -0,0 +1,2 @@
1
+ include LICENSE
2
+ include CHANGELOG.md
@@ -1,18 +1,16 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pyMapVBVD
3
- Version: 0.6.1
3
+ Version: 0.7.0
4
4
  Summary: Python twix file reader
5
- Home-page: https://github.com/wtclarke/pymapvbvd
6
- Author: Will Clarke
7
- Author-email: william.clarke@ndcn.ox.ac.uk
5
+ Author-email: Will Clarke <william.clarke@ndcn.ox.ac.uk>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/wtclarke/pymapvbvd
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
9
  Classifier: Programming Language :: Python :: 3.12
13
- Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
14
12
  Classifier: Operating System :: OS Independent
15
- Requires-Python: >=3.9
13
+ Requires-Python: >3.11
16
14
  Description-Content-Type: text/markdown
17
15
  License-File: LICENSE
18
16
  Requires-Dist: numpy>=1.26
@@ -23,6 +21,12 @@ Requires-Dist: h5py
23
21
  Requires-Dist: six
24
22
  Provides-Extra: tests
25
23
  Requires-Dist: pytest; extra == "tests"
24
+ Provides-Extra: dev
25
+ Requires-Dist: build; extra == "dev"
26
+ Requires-Dist: flake8; extra == "dev"
27
+ Requires-Dist: flake8-pyproject; extra == "dev"
28
+ Requires-Dist: pytest; extra == "dev"
29
+ Dynamic: license-file
26
30
 
27
31
  # pyMapVBVD
28
32
 
@@ -38,6 +42,17 @@ Python port of the Matlab mapVBVD tool for reading Siemens raw data 'twix' (.dat
38
42
  or
39
43
  `pip install pymapvbvd`
40
44
 
45
+ ## Development
46
+ Install [Pixi](https://pixi.sh), then run `pixi install` to create the development environment.
47
+
48
+ ```bash
49
+ pixi run lint
50
+ pixi run test
51
+ pixi run build
52
+ ```
53
+
54
+ Run the test suite with a specific supported development Python version using `pixi run -e py313 test` or `pixi run -e py314 test`.
55
+
41
56
  ## Use
42
57
 
43
58
  I have attempted to replicate the syntax of the original matlab code, but there are a few differences due to differing variable types.
@@ -12,6 +12,17 @@ Python port of the Matlab mapVBVD tool for reading Siemens raw data 'twix' (.dat
12
12
  or
13
13
  `pip install pymapvbvd`
14
14
 
15
+ ## Development
16
+ Install [Pixi](https://pixi.sh), then run `pixi install` to create the development environment.
17
+
18
+ ```bash
19
+ pixi run lint
20
+ pixi run test
21
+ pixi run build
22
+ ```
23
+
24
+ Run the test suite with a specific supported development Python version using `pixi run -e py313 test` or `pixi run -e py314 test`.
25
+
15
26
  ## Use
16
27
 
17
28
  I have attempted to replicate the syntax of the original matlab code, but there are a few differences due to differing variable types.
@@ -0,0 +1,9 @@
1
+ # Manual build process
2
+ 1. Commit, push and run CI tests
3
+ 2. git tag -m VX.X.X X.X.X
4
+ 3. git push github master --tags
5
+ 4. git clean -fdxn
6
+ 5. git clean -fdx
7
+ 6. python setup.py sdist
8
+ 7. python setup.py bdist_wheel
9
+ 8. python -m twine upload dist/*
@@ -0,0 +1,8 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ from mapvbvd.mapVBVD import mapVBVD
4
+
5
+ try:
6
+ __version__ = version("pyMapVBVD")
7
+ except PackageNotFoundError:
8
+ __version__ = "0+unknown"
@@ -427,7 +427,12 @@ class twix_map_obj:
427
427
  self.NIdc = np.max(self.Idc) + 1
428
428
  self.NIdd = np.max(self.Idd) + 1
429
429
  self.NIde = np.max(self.Ide) + 1
430
-
430
+ if int(self.NLin) == 1<<16:
431
+ if len(self.Lin) > self.NLin // self.NRep:
432
+ logging.warning('NLin is at maximum value for uint16, this may cause issues with indexing, attempting fix')
433
+ self.NLin = len(self.Lin) // self.NRep
434
+ self.Lin = np.arange(self.NLin)
435
+
431
436
  # ok, let us assume for now that all NCol and NCha entries are
432
437
  # the same for all mdhs:
433
438
 
@@ -792,7 +797,7 @@ class twix_map_obj:
792
797
  raw = raw.reshape(readShape)
793
798
  isBrokenRead = True # remember it and bail out later
794
799
 
795
- block[:, :, blockCtr, None] = copy.deepcopy(raw).reshape(np.append(readShape, 1))
800
+ block[:, :, blockCtr, None] = raw.reshape(np.append(readShape, 1))
796
801
  # fast serial storage in a cache array - this is probably all very dependent on whether I've got things
797
802
  # contiguous in memory. I highly doubt that I have on this first pass. WTC
798
803
  blockCtr += 1