mrzerocore 0.2.12__tar.gz → 0.3.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 (101) hide show
  1. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/.gitignore +0 -1
  2. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/CHANGELOG.md +10 -0
  3. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/Cargo.lock +91 -69
  4. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/Cargo.toml +3 -3
  5. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/PKG-INFO +9 -9
  6. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api/phantom.md +2 -0
  7. MRzeroCore-0.3.0/documentation/playground/generated/seqs/FLASH.ipynb +1 -0
  8. MRzeroCore-0.3.0/documentation/playground/quantified_brain.npz +0 -0
  9. MRzeroCore-0.3.0/documentation/playground/templates/generate.py +82 -0
  10. MRzeroCore-0.3.0/documentation/playground/templates/seqs.ipynb +119 -0
  11. MRzeroCore-0.3.0/documentation/playground/templates/template_A.ipynb +396 -0
  12. MRzeroCore-0.3.0/documentation/playground_mr0/flash.ipynb +292 -0
  13. MRzeroCore-0.3.0/documentation/playground_mr0/mr0_DWI_GRE_2D_seq.ipynb +417 -0
  14. MRzeroCore-0.3.0/documentation/playground_mr0/mr0_bSSFP_2D_seq.ipynb +1 -0
  15. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/pyproject.toml +1 -1
  16. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/__init__.py +8 -5
  17. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/phantom/brainweb/__init__.py +5 -5
  18. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/phantom/custom_voxel_phantom.py +4 -3
  19. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/phantom/sim_data.py +11 -2
  20. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/phantom/voxel_grid_phantom.py +63 -41
  21. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/sequence.py +1 -1
  22. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/simulation/isochromat_sim.py +37 -13
  23. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/simulation/main_pass.py +68 -18
  24. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/simulation/pre_pass.py +10 -1
  25. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/util.py +30 -2
  26. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/src/lib.rs +4 -0
  27. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/src/pre_pass.rs +8 -2
  28. MRzeroCore-0.2.12/documentation/playground_mr0/flash.ipynb +0 -187
  29. MRzeroCore-0.2.12/documentation/playground_mr0/mr0_DWI_GRE_2D_seq.ipynb +0 -1
  30. MRzeroCore-0.2.12/documentation/playground_mr0/mr0_bSSFP_2D_seq.ipynb +0 -1
  31. MRzeroCore-0.2.12/documentation/resources/logo.blend +0 -0
  32. MRzeroCore-0.2.12/documentation/resources/logo.png +0 -0
  33. MRzeroCore-0.2.12/documentation/resources/studio_small_09_2k.hdr +0 -0
  34. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/.github/workflows/pypi_publish.yml +0 -0
  35. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/.readthedocs.yaml +0 -0
  36. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/EULA.txt +0 -0
  37. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/LICENSE +0 -0
  38. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/README.md +0 -0
  39. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/_config.yml +0 -0
  40. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/_toc.yml +0 -0
  41. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api/reco.md +0 -0
  42. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api/sequence.md +0 -0
  43. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api/simulation/isochromat_sim.md +0 -0
  44. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api/simulation/pdg_sim.md +0 -0
  45. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api/simulation.md +0 -0
  46. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api/util.md +0 -0
  47. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/api.md +0 -0
  48. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/intro.md +0 -0
  49. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/logo.png +0 -0
  50. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/phantom_generation.md +0 -0
  51. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/AdjDataUser2gB0_transversal_0.08moving_average.mat +0 -0
  52. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/flash_DWI.ipynb +0 -0
  53. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr00_FLASH_2D_ernstAngle_opt.ipynb +0 -0
  54. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_CS_cartesian_seq.ipynb +0 -0
  55. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_CS_radial_seq.ipynb +0 -0
  56. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_DREAM_STE_seq.ipynb +0 -0
  57. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_DREAM_STID_seq.ipynb +0 -0
  58. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_EPI_2D_seq.ipynb +0 -0
  59. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_FID_seq.ipynb +0 -0
  60. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_FLASH_2D_seq.ipynb +0 -0
  61. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_GRE_to_FLASH.ipynb +0 -0
  62. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_RARE_2D_seq.ipynb +0 -0
  63. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_SE_CPMG_seq.ipynb +0 -0
  64. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_STE_3pulses_5echoes_seq.ipynb +0 -0
  65. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_burst_TSE.ipynb +0 -0
  66. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_Fit_T1.ipynb +0 -0
  67. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_voxelNN_T1.ipynb +0 -0
  68. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_pypulseq_exmpls_seq.ipynb +0 -0
  69. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/mr0_upload_seq.ipynb +0 -0
  70. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/numerical_brain_cropped.mat +0 -0
  71. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/overview.md +0 -0
  72. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/ptx_phantom.p +0 -0
  73. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/pulseq_flash.ipynb +0 -0
  74. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/pulseq_rf_shim.ipynb +0 -0
  75. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/pulseq_sim_pTx.ipynb +0 -0
  76. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/seqs/flash pTx CP.seq +0 -0
  77. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/seqs/flash pTx EP.seq +0 -0
  78. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/seqs/flash pTx QM.seq +0 -0
  79. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/playground_mr0/subject05.npz +0 -0
  80. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/documentation/requirements.txt +0 -0
  81. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/phantom/brainweb/brainweb_data.json +0 -0
  82. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/phantom/brainweb/brainweb_data_sources.txt +0 -0
  83. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/phantom/brainweb/output/.gitkeep +0 -0
  84. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/exporter.py +0 -0
  85. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/exporter_v2.py +0 -0
  86. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/helpers.py +0 -0
  87. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_exporter.py +0 -0
  88. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/__init__.py +0 -0
  89. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/adc.py +0 -0
  90. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/helpers.py +0 -0
  91. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulse.py +0 -0
  92. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/__init__.py +0 -0
  93. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/adc.py +0 -0
  94. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/block.py +0 -0
  95. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/definitons.py +0 -0
  96. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/gradient.py +0 -0
  97. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/helpers.py +0 -0
  98. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/rf.py +0 -0
  99. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/trap.py +0 -0
  100. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/pulseq/pulseq_loader/spoiler.py +0 -0
  101. {MRzeroCore-0.2.12 → MRzeroCore-0.3.0}/python/MRzeroCore/reconstruction.py +0 -0
@@ -3,7 +3,6 @@
3
3
  /documentation/_build
4
4
  out/
5
5
  *.seq
6
- *.npz
7
6
  *.env
8
7
 
9
8
  *.pdf
@@ -1,3 +1,13 @@
1
+ - 0.3.0
2
+ - Bugfixes (seq.get_contrast(), pTx shim phase, pytorch deprecation warnings, > 360° pulses, pulseq plot, missing 2pi in diffusion, phantom plot titles)
3
+ - WIP rigid phantom motion simulation - not yet documented and subject to change
4
+ - Added option to return transversal magnetization from simulation
5
+ - New imshow() function in util.py for consistent plotting
6
+ - Some fixes to playground sequences - bigger overhaul coming soon
7
+ - Allow loading B0 / B1 in new VoxelGridPhantom.load() function
8
+ - Introduced quantified brain
9
+ - Performance improvement - only calculate signal for measured ADC samples
10
+ - Added latent_signal_unormalized to prepass graph for vizualisation
1
11
  - 0.2.12
2
12
  - Fixed Brainweb phantom loading and generation
3
13
  - Switched pre-pass kt precision from f32 to f64
@@ -4,15 +4,15 @@ version = 3
4
4
 
5
5
  [[package]]
6
6
  name = "autocfg"
7
- version = "1.1.0"
7
+ version = "1.3.0"
8
8
  source = "registry+https://github.com/rust-lang/crates.io-index"
9
- checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
9
+ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
10
10
 
11
11
  [[package]]
12
12
  name = "bitflags"
13
- version = "1.3.2"
13
+ version = "2.5.0"
14
14
  source = "registry+https://github.com/rust-lang/crates.io-index"
15
- checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
15
+ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
16
16
 
17
17
  [[package]]
18
18
  name = "cfg-if"
@@ -20,23 +20,29 @@ version = "1.0.0"
20
20
  source = "registry+https://github.com/rust-lang/crates.io-index"
21
21
  checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
22
22
 
23
+ [[package]]
24
+ name = "heck"
25
+ version = "0.4.1"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
28
+
23
29
  [[package]]
24
30
  name = "indoc"
25
- version = "1.0.8"
31
+ version = "2.0.5"
26
32
  source = "registry+https://github.com/rust-lang/crates.io-index"
27
- checksum = "da2d6f23ffea9d7e76c53eee25dfb67bcd8fde7f1198b0855350698c9f07c780"
33
+ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
28
34
 
29
35
  [[package]]
30
36
  name = "libc"
31
- version = "0.2.139"
37
+ version = "0.2.155"
32
38
  source = "registry+https://github.com/rust-lang/crates.io-index"
33
- checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
39
+ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
34
40
 
35
41
  [[package]]
36
42
  name = "lock_api"
37
- version = "0.4.9"
43
+ version = "0.4.12"
38
44
  source = "registry+https://github.com/rust-lang/crates.io-index"
39
- checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
45
+ checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
40
46
  dependencies = [
41
47
  "autocfg",
42
48
  "scopeguard",
@@ -44,16 +50,16 @@ dependencies = [
44
50
 
45
51
  [[package]]
46
52
  name = "memoffset"
47
- version = "0.6.5"
53
+ version = "0.9.1"
48
54
  source = "registry+https://github.com/rust-lang/crates.io-index"
49
- checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
55
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
50
56
  dependencies = [
51
57
  "autocfg",
52
58
  ]
53
59
 
54
60
  [[package]]
55
61
  name = "mrzero_core"
56
- version = "0.2.12"
62
+ version = "0.3.0"
57
63
  dependencies = [
58
64
  "num-complex",
59
65
  "pyo3",
@@ -61,33 +67,33 @@ dependencies = [
61
67
 
62
68
  [[package]]
63
69
  name = "num-complex"
64
- version = "0.4.2"
70
+ version = "0.4.6"
65
71
  source = "registry+https://github.com/rust-lang/crates.io-index"
66
- checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
72
+ checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
67
73
  dependencies = [
68
74
  "num-traits",
69
75
  ]
70
76
 
71
77
  [[package]]
72
78
  name = "num-traits"
73
- version = "0.2.15"
79
+ version = "0.2.19"
74
80
  source = "registry+https://github.com/rust-lang/crates.io-index"
75
- checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
81
+ checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
76
82
  dependencies = [
77
83
  "autocfg",
78
84
  ]
79
85
 
80
86
  [[package]]
81
87
  name = "once_cell"
82
- version = "1.17.0"
88
+ version = "1.19.0"
83
89
  source = "registry+https://github.com/rust-lang/crates.io-index"
84
- checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
90
+ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
85
91
 
86
92
  [[package]]
87
93
  name = "parking_lot"
88
- version = "0.12.1"
94
+ version = "0.12.2"
89
95
  source = "registry+https://github.com/rust-lang/crates.io-index"
90
- checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
96
+ checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
91
97
  dependencies = [
92
98
  "lock_api",
93
99
  "parking_lot_core",
@@ -95,31 +101,37 @@ dependencies = [
95
101
 
96
102
  [[package]]
97
103
  name = "parking_lot_core"
98
- version = "0.9.6"
104
+ version = "0.9.10"
99
105
  source = "registry+https://github.com/rust-lang/crates.io-index"
100
- checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
106
+ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
101
107
  dependencies = [
102
108
  "cfg-if",
103
109
  "libc",
104
110
  "redox_syscall",
105
111
  "smallvec",
106
- "windows-sys",
112
+ "windows-targets",
107
113
  ]
108
114
 
115
+ [[package]]
116
+ name = "portable-atomic"
117
+ version = "1.6.0"
118
+ source = "registry+https://github.com/rust-lang/crates.io-index"
119
+ checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
120
+
109
121
  [[package]]
110
122
  name = "proc-macro2"
111
- version = "1.0.49"
123
+ version = "1.0.83"
112
124
  source = "registry+https://github.com/rust-lang/crates.io-index"
113
- checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
125
+ checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
114
126
  dependencies = [
115
127
  "unicode-ident",
116
128
  ]
117
129
 
118
130
  [[package]]
119
131
  name = "pyo3"
120
- version = "0.17.3"
132
+ version = "0.21.2"
121
133
  source = "registry+https://github.com/rust-lang/crates.io-index"
122
- checksum = "268be0c73583c183f2b14052337465768c07726936a260f480f0857cb95ba543"
134
+ checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8"
123
135
  dependencies = [
124
136
  "cfg-if",
125
137
  "indoc",
@@ -127,6 +139,7 @@ dependencies = [
127
139
  "memoffset",
128
140
  "num-complex",
129
141
  "parking_lot",
142
+ "portable-atomic",
130
143
  "pyo3-build-config",
131
144
  "pyo3-ffi",
132
145
  "pyo3-macros",
@@ -135,9 +148,9 @@ dependencies = [
135
148
 
136
149
  [[package]]
137
150
  name = "pyo3-build-config"
138
- version = "0.17.3"
151
+ version = "0.21.2"
139
152
  source = "registry+https://github.com/rust-lang/crates.io-index"
140
- checksum = "28fcd1e73f06ec85bf3280c48c67e731d8290ad3d730f8be9dc07946923005c8"
153
+ checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50"
141
154
  dependencies = [
142
155
  "once_cell",
143
156
  "target-lexicon",
@@ -145,9 +158,9 @@ dependencies = [
145
158
 
146
159
  [[package]]
147
160
  name = "pyo3-ffi"
148
- version = "0.17.3"
161
+ version = "0.21.2"
149
162
  source = "registry+https://github.com/rust-lang/crates.io-index"
150
- checksum = "0f6cb136e222e49115b3c51c32792886defbfb0adead26a688142b346a0b9ffc"
163
+ checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403"
151
164
  dependencies = [
152
165
  "libc",
153
166
  "pyo3-build-config",
@@ -155,9 +168,9 @@ dependencies = [
155
168
 
156
169
  [[package]]
157
170
  name = "pyo3-macros"
158
- version = "0.17.3"
171
+ version = "0.21.2"
159
172
  source = "registry+https://github.com/rust-lang/crates.io-index"
160
- checksum = "94144a1266e236b1c932682136dc35a9dee8d3589728f68130c7c3861ef96b28"
173
+ checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c"
161
174
  dependencies = [
162
175
  "proc-macro2",
163
176
  "pyo3-macros-backend",
@@ -167,50 +180,52 @@ dependencies = [
167
180
 
168
181
  [[package]]
169
182
  name = "pyo3-macros-backend"
170
- version = "0.17.3"
183
+ version = "0.21.2"
171
184
  source = "registry+https://github.com/rust-lang/crates.io-index"
172
- checksum = "c8df9be978a2d2f0cdebabb03206ed73b11314701a5bfe71b0d753b81997777f"
185
+ checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c"
173
186
  dependencies = [
187
+ "heck",
174
188
  "proc-macro2",
189
+ "pyo3-build-config",
175
190
  "quote",
176
191
  "syn",
177
192
  ]
178
193
 
179
194
  [[package]]
180
195
  name = "quote"
181
- version = "1.0.23"
196
+ version = "1.0.36"
182
197
  source = "registry+https://github.com/rust-lang/crates.io-index"
183
- checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
198
+ checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
184
199
  dependencies = [
185
200
  "proc-macro2",
186
201
  ]
187
202
 
188
203
  [[package]]
189
204
  name = "redox_syscall"
190
- version = "0.2.16"
205
+ version = "0.5.1"
191
206
  source = "registry+https://github.com/rust-lang/crates.io-index"
192
- checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
207
+ checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
193
208
  dependencies = [
194
209
  "bitflags",
195
210
  ]
196
211
 
197
212
  [[package]]
198
213
  name = "scopeguard"
199
- version = "1.1.0"
214
+ version = "1.2.0"
200
215
  source = "registry+https://github.com/rust-lang/crates.io-index"
201
- checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
216
+ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
202
217
 
203
218
  [[package]]
204
219
  name = "smallvec"
205
- version = "1.10.0"
220
+ version = "1.13.2"
206
221
  source = "registry+https://github.com/rust-lang/crates.io-index"
207
- checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
222
+ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
208
223
 
209
224
  [[package]]
210
225
  name = "syn"
211
- version = "1.0.107"
226
+ version = "2.0.65"
212
227
  source = "registry+https://github.com/rust-lang/crates.io-index"
213
- checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
228
+ checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
214
229
  dependencies = [
215
230
  "proc-macro2",
216
231
  "quote",
@@ -219,31 +234,32 @@ dependencies = [
219
234
 
220
235
  [[package]]
221
236
  name = "target-lexicon"
222
- version = "0.12.5"
237
+ version = "0.12.14"
223
238
  source = "registry+https://github.com/rust-lang/crates.io-index"
224
- checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d"
239
+ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
225
240
 
226
241
  [[package]]
227
242
  name = "unicode-ident"
228
- version = "1.0.6"
243
+ version = "1.0.12"
229
244
  source = "registry+https://github.com/rust-lang/crates.io-index"
230
- checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
245
+ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
231
246
 
232
247
  [[package]]
233
248
  name = "unindent"
234
- version = "0.1.11"
249
+ version = "0.2.3"
235
250
  source = "registry+https://github.com/rust-lang/crates.io-index"
236
- checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
251
+ checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
237
252
 
238
253
  [[package]]
239
- name = "windows-sys"
240
- version = "0.42.0"
254
+ name = "windows-targets"
255
+ version = "0.52.5"
241
256
  source = "registry+https://github.com/rust-lang/crates.io-index"
242
- checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
257
+ checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
243
258
  dependencies = [
244
259
  "windows_aarch64_gnullvm",
245
260
  "windows_aarch64_msvc",
246
261
  "windows_i686_gnu",
262
+ "windows_i686_gnullvm",
247
263
  "windows_i686_msvc",
248
264
  "windows_x86_64_gnu",
249
265
  "windows_x86_64_gnullvm",
@@ -252,42 +268,48 @@ dependencies = [
252
268
 
253
269
  [[package]]
254
270
  name = "windows_aarch64_gnullvm"
255
- version = "0.42.1"
271
+ version = "0.52.5"
256
272
  source = "registry+https://github.com/rust-lang/crates.io-index"
257
- checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
273
+ checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
258
274
 
259
275
  [[package]]
260
276
  name = "windows_aarch64_msvc"
261
- version = "0.42.1"
277
+ version = "0.52.5"
262
278
  source = "registry+https://github.com/rust-lang/crates.io-index"
263
- checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
279
+ checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
264
280
 
265
281
  [[package]]
266
282
  name = "windows_i686_gnu"
267
- version = "0.42.1"
283
+ version = "0.52.5"
284
+ source = "registry+https://github.com/rust-lang/crates.io-index"
285
+ checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
286
+
287
+ [[package]]
288
+ name = "windows_i686_gnullvm"
289
+ version = "0.52.5"
268
290
  source = "registry+https://github.com/rust-lang/crates.io-index"
269
- checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
291
+ checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
270
292
 
271
293
  [[package]]
272
294
  name = "windows_i686_msvc"
273
- version = "0.42.1"
295
+ version = "0.52.5"
274
296
  source = "registry+https://github.com/rust-lang/crates.io-index"
275
- checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
297
+ checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
276
298
 
277
299
  [[package]]
278
300
  name = "windows_x86_64_gnu"
279
- version = "0.42.1"
301
+ version = "0.52.5"
280
302
  source = "registry+https://github.com/rust-lang/crates.io-index"
281
- checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
303
+ checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
282
304
 
283
305
  [[package]]
284
306
  name = "windows_x86_64_gnullvm"
285
- version = "0.42.1"
307
+ version = "0.52.5"
286
308
  source = "registry+https://github.com/rust-lang/crates.io-index"
287
- checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
309
+ checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
288
310
 
289
311
  [[package]]
290
312
  name = "windows_x86_64_msvc"
291
- version = "0.42.1"
313
+ version = "0.52.5"
292
314
  source = "registry+https://github.com/rust-lang/crates.io-index"
293
- checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
315
+ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "mrzero_core"
3
- version = "0.2.12"
3
+ version = "0.3.0"
4
4
  edition = "2021"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -8,5 +8,5 @@ edition = "2021"
8
8
  crate-type = ["cdylib"]
9
9
 
10
10
  [dependencies]
11
- pyo3 = { version = "0.17.3", features = ["abi3-py37", "extension-module", "num-complex"] }
12
- num-complex = "0.4.2"
11
+ pyo3 = { version = "0.21.2", features = ["abi3-py37", "extension-module", "num-complex"] }
12
+ num-complex = "0.4.6"
@@ -1,26 +1,26 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: MRzeroCore
3
- Version: 0.2.12
3
+ Version: 0.3.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
7
7
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3
8
- Requires-Dist: torch>=1.12
8
+ Requires-Dist: torch >=1.12
9
9
  Requires-Dist: pypulseq
10
- Requires-Dist: matplotlib>=3.5
11
- Requires-Dist: scipy>=1.7
12
- Requires-Dist: requests>=2.20
10
+ Requires-Dist: matplotlib >=3.5
11
+ Requires-Dist: scipy >=1.7
12
+ Requires-Dist: requests >=2.20
13
13
  Requires-Dist: scikit-image
14
14
  Requires-Dist: torchkbnufft
15
- Requires-Dist: pydisseqt>=0.1.4
15
+ Requires-Dist: pydisseqt >=0.1.4
16
16
  License-File: LICENSE
17
17
  Summary: Core functionality of MRzero
18
18
  Author-email: Jonathan Endres <jonathan.endres@uk-erlangen.de>
19
19
  Requires-Python: >=3.9
20
20
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
21
- Project-URL: MRzero-Paper, https://arxiv.org/abs/2002.04265
22
- Project-URL: Documentation, https://mrzero-core.readthedocs.io/
23
21
  Project-URL: Repository, https://github.com/MRsources/MRzero-Core
22
+ Project-URL: Documentation, https://mrzero-core.readthedocs.io/
23
+ Project-URL: MRzero-Paper, https://arxiv.org/abs/2002.04265
24
24
 
25
25
  [![Documentation Status](https://readthedocs.org/projects/mrzero-core/badge/?version=latest)](https://mrzero-core.readthedocs.io/en/latest/?badge=latest)
26
26
 
@@ -59,6 +59,8 @@ When converting to {class}`SimData`, voxels are sinc-shaped by default in order
59
59
 
60
60
  Analytical phantom for experimentation. There is no resolution or grid, voxels can be placed anywhere and have any size or shape. Useful for testing reconstruction, sub-voxel positioning etc.
61
61
 
62
+ The `size` of this phantom is computed from the extends of the voxel positions.
63
+
62
64
  :::{note}
63
65
  Because of how the voxel k-space responce is implemented currently, all voxels are limited to have the same shape and size. This limitation could be lifted in the future, if required.
64
66
  :::
@@ -0,0 +1 @@
1
+ {"cells": [{"cell_type": "markdown", "metadata": {"id": "ZUXedN4MhMJK"}, "source": ["![logo_40.webp](data:image/webp;base64,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)"]}, {"cell_type": "markdown", "metadata": {"id": "8TMFbWzzYav1"}, "source": ["# **FID: free induction decay**\n", "In this script a sequence and a phantom are set-up to simulate the measurement of the free induction decay signal.\n", "\n", "References:\n", "1. https://mriquestions.com/free-induction-decay.html, last access: 16-05-2024\n", "\n"]}, {"cell_type": "markdown", "metadata": {"id": "qePtXYPgYWrd"}, "source": ["## 1. Library installation and download of phantom"]}, {"cell_type": "code", "execution_count": null, "metadata": {"execution": {"iopub.execute_input": "2024-05-20T16:07:40.095400Z", "iopub.status.busy": "2024-05-20T16:07:40.094980Z", "iopub.status.idle": "2024-05-20T16:08:56.887833Z", "shell.execute_reply": "2024-05-20T16:08:56.887087Z"}, "id": "TuPWGS5NYM-b", "tags": ["hide-cell"]}, "outputs": [], "source": ["!pip install pypulseq==1.3.1.post1 &> /dev/null\n", "!pip install MRzeroCore &> /dev/null\n", "!wget https://github.com/MRsources/MRzero-Core/raw/main/documentation/playground_mr0/numerical_brain_cropped.mat &> /dev/null"]}, {"cell_type": "markdown", "metadata": {"id": "pdWiq2pyYM-e"}, "source": ["## 2. Library import and setup"]}, {"cell_type": "code", "execution_count": null, "metadata": {"execution": {"iopub.execute_input": "2024-05-20T16:08:56.890416Z", "iopub.status.busy": "2024-05-20T16:08:56.890228Z", "iopub.status.idle": "2024-05-20T16:08:58.479155Z", "shell.execute_reply": "2024-05-20T16:08:58.478693Z"}, "id": "raWci0gpZ-w1"}, "outputs": [], "source": ["import numpy as np\n", "np.int = int\n", "np.float = float\n", "np.complex = complex\n", "\n", "import MRzeroCore as mr0\n", "import pypulseq as pp\n", "import matplotlib.pyplot as plt"]}, {"cell_type": "markdown", "metadata": {"id": "pFNMbQ2UZn5x"}, "source": ["## 3. Acquisition configuration"]}, {"cell_type": "code", "execution_count": null, "metadata": {"execution": {"iopub.execute_input": "2024-05-20T16:08:58.481993Z", "iopub.status.busy": "2024-05-20T16:08:58.481591Z", "iopub.status.idle": "2024-05-20T16:08:58.485048Z", "shell.execute_reply": "2024-05-20T16:08:58.484632Z"}, "id": "pxSmmjAPZuIl"}, "outputs": [], "source": ["# Phantom resolution\n", "sz = [64, 64]\n", "\n", "# Field of view, imaging volume and resolution\n", "fov = 200e-3\n", "slice_thickness = 8e-3\n", "Nread = 64\n", "Nphase = 64\n", "\n", "# Sequence's name\n", "experiment_id = 'flash'\n", "save = True\n", "\n", "# Scanner limits\n", "system = pp.Opts(\n", " max_grad=28, grad_unit='mT/m',\n", " max_slew=150, slew_unit='T/m/s',\n", " rf_ringdown_time=20e-6, rf_dead_time=100e-6,\n", " adc_dead_time=20e-6,\n", " grad_raster_time=50e-6\n", ")"]}, {"cell_type": "markdown", "metadata": {"id": "AbmZsLMCarQO"}, "source": ["## 4. Phantom loading and configuration"]}, {"cell_type": "code", "execution_count": null, "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 880}, "execution": {"iopub.execute_input": "2024-05-20T16:08:58.486935Z", "iopub.status.busy": "2024-05-20T16:08:58.486613Z", "iopub.status.idle": "2024-05-20T16:08:59.413395Z", "shell.execute_reply": "2024-05-20T16:08:59.412838Z"}, "executionInfo": {"elapsed": 3631, "status": "ok", "timestamp": 1715863433984, "user": {"displayName": "magda duarte", "userId": "04023483935404706158"}, "user_tz": -120}, "id": "mMH_gkosat5M", "outputId": "c10d24e3-480b-4e98-825f-b0e7cd6a72f0"}, "outputs": [], "source": ["# Load phantom data\n", "obj_p = mr0.VoxelGridPhantom.load('../../quantified_brain.npz')\n", "obj_p = obj_p.interpolate(sz[0], sz[1], 1)\n", "\n", "# Manipulate loaded data\n", "obj_p.D *= 0\n", "obj_p.plot()"]}, {"cell_type": "markdown", "metadata": {"id": "6VlHLvSnZU03"}, "source": ["## 5. Sequence building"]}, {"cell_type": "code", "execution_count": null, "metadata": {"colab": {"base_uri": "https://localhost:8080/"}, "execution": {"iopub.execute_input": "2024-05-20T16:08:59.415735Z", "iopub.status.busy": "2024-05-20T16:08:59.415254Z", "iopub.status.idle": "2024-05-20T16:08:59.423067Z", "shell.execute_reply": "2024-05-20T16:08:59.422600Z"}, "executionInfo": {"elapsed": 255, "status": "ok", "timestamp": 1715864381418, "user": {"displayName": "magda duarte", "userId": "04023483935404706158"}, "user_tz": -120}, "id": "UAbo_q1WGPoi", "outputId": "2f9cb6bf-8a73-49ed-9ef3-771f29cc4078"}, "outputs": [], "source": ["def flash_2D(fov=200e-3, slice_thickness=8e-3,\n", " n_read=64, n_phase=64,\n", " system=system,\n", " flip_angle=10, phase_cycling=84):\n", " \"\"\"Linear, cartesian 2D FLASH with TR = 26 ms + 50 us * n_phase\"\"\"\n", " rf, gz, gzr = pp.make_sinc_pulse(\n", " flip_angle=flip_angle * np.pi / 180, duration=1e-3,\n", " slice_thickness=slice_thickness, apodization=0.5, time_bw_product=4,\n", " return_gz=True, system=system\n", " )\n", "\n", " adc_dur = n_phase * 50e-6\n", " gx = pp.make_trapezoid(channel='x', flat_area=n_read / fov, flat_time=adc_dur, system=system)\n", " adc = pp.make_adc(num_samples=n_read, duration=adc_dur, delay=gx.rise_time, system=system)\n", " gx_pre = pp.make_trapezoid(channel='x', area=-0.5 * gx.area, duration=5e-3, system=system)\n", " gx_spoil = pp.make_trapezoid(channel='x', area=1.5 * gx.area, duration=2e-3, system=system)\n", "\n", " seq = pp.Sequence(system)\n", " for i in range(n_phase):\n", " phase = 0.5 * phase_cycling * (2 + i + i**2)\n", " rf.phase_offset = (phase % 360) * np.pi / 180\n", " adc.phase_offset = rf.phase_offset\n", "\n", " phenc = (i - n_phase // 2) / fov\n", "\n", " seq.add_block(rf, gz)\n", " seq.add_block(gzr, pp.make_delay(5e-3))\n", " gp = pp.make_trapezoid(channel='y', area=phenc, duration=5e-3, system=system)\n", " seq.add_block(gx_pre, gp)\n", " seq.add_block(adc, gx)\n", " gp = pp.make_trapezoid(channel='y', area=-phenc, duration=5e-3, system=system)\n", " seq.add_block(gx_spoil, gp)\n", " seq.add_block(pp.make_delay(10e-3))\n", "\n", " seq.set_definition('FOV', [fov, fov, slice_thickness])\n", " return seq\n", "seq = flash_2D(fov, slice_thickness, Nread, Nphase, system)\n", "seq.set_definition('Name', experiment_id)\n", "seq.write(experiment_id + '.seq')\n", "\n", "# Verify sequence timing\n", "ok, error_report = seq.check_timing()\n", "if ok:\n", " print('Timing check passed successfully')\n", "else:\n", " print('Timing check failed. Error listing follows:')\n", " [print(e) for e in error_report]"]}, {"cell_type": "markdown", "metadata": {"id": "PyupRv7waNWL"}, "source": ["## 6. Simulation"]}, {"cell_type": "markdown", "metadata": {"id": "nzJLNmnSe64H"}, "source": ["### 6.1 Update phantom parameters for simulation"]}, {"cell_type": "code", "execution_count": null, "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 880}, "execution": {"iopub.execute_input": "2024-05-20T16:08:59.425063Z", "iopub.status.busy": "2024-05-20T16:08:59.424896Z", "iopub.status.idle": "2024-05-20T16:09:00.397313Z", "shell.execute_reply": "2024-05-20T16:09:00.396794Z"}, "executionInfo": {"elapsed": 2543, "status": "ok", "timestamp": 1715864292107, "user": {"displayName": "magda duarte", "userId": "04023483935404706158"}, "user_tz": -120}, "id": "JMnqlRlujZHf", "outputId": "f2928d48-fc1b-4bfa-b8ba-d16c015da10a"}, "outputs": [], "source": ["#@markdown The B0 inhomogeneity brings you from the rotating frame FID at dB0=0, closer to the lab frame FID at dB0=B0.\n", "#@markdown Try dB0=0 and dB0=500 for a test.\n", "dB0 = 0 #@param {title:'dB0',type:\"slider\", min:0, max:500, step:10}\n", "\n", "# Reload phantom\n", "obj_p = mr0.VoxelGridPhantom.load('../../quantified_brain.npz')\n", "obj_p = obj_p.interpolate(sz[0], sz[1], 1)\n", "\n", "# Manipulate loaded data\n", "obj_p.D *= 0\n", "obj_p.B0 += dB0\n", "obj_p.plot()\n", "# Convert phantom into simulation data\n", "obj_p = obj_p.build()"]}, {"cell_type": "markdown", "metadata": {"id": "Z04m1ZNHfAf2"}, "source": ["### 6.2 Read seq file and simulate"]}, {"cell_type": "code", "execution_count": null, "metadata": {"colab": {"base_uri": "https://localhost:8080/", "height": 843}, "execution": {"iopub.execute_input": "2024-05-20T16:09:00.399569Z", "iopub.status.busy": "2024-05-20T16:09:00.399221Z", "iopub.status.idle": "2024-05-20T16:09:01.662556Z", "shell.execute_reply": "2024-05-20T16:09:01.662062Z"}, "executionInfo": {"elapsed": 2781, "status": "ok", "timestamp": 1715864297525, "user": {"displayName": "magda duarte", "userId": "04023483935404706158"}, "user_tz": -120}, "id": "Xr3zWNwUeoaG", "outputId": "2cce98bd-588e-4c05-e42f-5af22a846767"}, "outputs": [], "source": ["# Read in the sequence\n", "seq0 = mr0.Sequence.import_file(experiment_id + '.seq')\n", "\n", "# Simulate the sequence\n", "graph = mr0.compute_graph(seq0, obj_p, 200, 1e-3)\n", "signal = mr0.execute_graph(graph, seq0, obj_p, print_progress=False)\n", "\n", "# Plot sequence with SIGNAL\n", "sp_adc, t_adc = mr0.util.pulseq_plot(seq=seq,signal=signal.numpy())"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## 7. Save results"]}, {"cell_type": "code", "execution_count": null, "metadata": {"execution": {"iopub.execute_input": "2024-05-20T16:09:01.664813Z", "iopub.status.busy": "2024-05-20T16:09:01.664633Z", "iopub.status.idle": "2024-05-20T16:09:01.696830Z", "shell.execute_reply": "2024-05-20T16:09:01.696440Z"}}, "outputs": [], "source": ["if save:\n", " dir = \"../results/\"\n", " np.savetxt(dir + experiment_id + \"_signal.txt\", signal)\n", " plt.savefig(dir + experiment_id + \"_signal.png\")"]}], "metadata": {"colab": {"provenance": [{"file_id": "https://github.com/MRsources/MRzero-Core/blob/main/documentation/playground_mr0/mr0_FID_seq.ipynb", "timestamp": 1715862582835}, {"file_id": "1GhBrEuUSoKzI0K6QBDdmo5YR3pTpLQ38", "timestamp": 1678617699445}]}, "kernelspec": {"display_name": "Python 3", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9"}}, "nbformat": 4, "nbformat_minor": 0}
@@ -0,0 +1,82 @@
1
+ import json
2
+ import os
3
+ from copy import deepcopy
4
+
5
+
6
+ def create_nb(source, template, output_path: str):
7
+ snippets = {}
8
+ snip = []
9
+ for line in source:
10
+ if line.startswith("# !!!"):
11
+ name = line[5:].strip()
12
+ if name in snippets:
13
+ print(f"[!] Snippet {name} defined more than once")
14
+ snip = []
15
+ snippets[name] = snip
16
+ else:
17
+ snip.append(line)
18
+ for snippet in snippets.values():
19
+ while len(snippet) > 0 and len(snippet[-1].strip()) == 0:
20
+ snippet.pop()
21
+
22
+ used_snippets = set()
23
+ output = deepcopy(template)
24
+
25
+ for cell in output["cells"]:
26
+ if cell["cell_type"] == "code":
27
+ template_source = cell["source"]
28
+ cell["source"] = []
29
+
30
+ for line in template_source:
31
+ if line.startswith("# !!!"):
32
+ snippet_name = line[5:].strip()
33
+ if snippet_name not in snippets:
34
+ print(f"[!] Snippet {snippet_name} requested but not provided")
35
+ else:
36
+ if snippet_name in used_snippets:
37
+ print(f"[!] Snippet {snippet_name} used more than once")
38
+ else:
39
+ print(f"Used snippet {snippet_name}")
40
+
41
+ cell["source"] += snippets[snippet_name]
42
+ used_snippets.add(snippet_name)
43
+ else:
44
+ cell["source"].append(line)
45
+
46
+ for snippet_name in snippets:
47
+ if snippet_name not in used_snippets:
48
+ print(f"[!] Snippet {snippet_name} provided but not requested")
49
+
50
+ with open(output_path, "w") as output_file:
51
+ json.dump(output, output_file)
52
+
53
+
54
+ cwd = os.path.dirname(os.path.realpath(__file__))
55
+ files = os.listdir(cwd)
56
+ templates = [f for f in files if f.startswith("template")]
57
+ instatiators = [f for f in files if f not in templates and f.endswith(".ipynb")]
58
+
59
+ print(f"Generating notebooks based on {instatiators}")
60
+ print(f"using the templates {templates}")
61
+
62
+ template_src = {}
63
+ for nb_name in templates:
64
+ with open(os.path.join(cwd, nb_name)) as nb:
65
+ template_src[nb_name] = json.load(nb)
66
+
67
+ for nb_name in instatiators:
68
+ with open(os.path.join(cwd, nb_name)) as nb:
69
+ cells = json.load(nb)["cells"]
70
+ for cell in cells:
71
+ if cell["cell_type"] == "code":
72
+ source = cell["source"]
73
+ if source[0].startswith("# ***"):
74
+ template, _, output = source[0][6:].partition("->")
75
+ output = output.strip()
76
+ outdir = os.path.join(os.path.dirname(cwd), "generated", nb_name.partition(".")[0])
77
+ os.makedirs(outdir, exist_ok=True)
78
+ output = os.path.join(outdir, output)
79
+
80
+ print(f"> Creating notebook: {nb_name} {template.strip()} -> {output.strip()}")
81
+ template = template_src[template.strip()]
82
+ create_nb(source, template, output)