mrzerocore 0.2.10__tar.gz → 0.2.12__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 (93) hide show
  1. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/CHANGELOG.md +6 -0
  2. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/Cargo.lock +1 -1
  3. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/Cargo.toml +1 -1
  4. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/PKG-INFO +2 -2
  5. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/_toc.yml +1 -0
  6. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/phantom/brainweb/__init__.py +1 -1
  7. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/phantom/brainweb/brainweb_data.json +1 -1
  8. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/phantom/voxel_grid_phantom.py +4 -5
  9. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/simulation/main_pass.py +21 -8
  10. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/src/lib.rs +1 -1
  11. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/src/pre_pass.rs +33 -17
  12. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/.github/workflows/pypi_publish.yml +0 -0
  13. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/.gitignore +0 -0
  14. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/.readthedocs.yaml +0 -0
  15. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/EULA.txt +0 -0
  16. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/LICENSE +0 -0
  17. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/README.md +0 -0
  18. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/_config.yml +0 -0
  19. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api/phantom.md +0 -0
  20. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api/reco.md +0 -0
  21. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api/sequence.md +0 -0
  22. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api/simulation/isochromat_sim.md +0 -0
  23. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api/simulation/pdg_sim.md +0 -0
  24. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api/simulation.md +0 -0
  25. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api/util.md +0 -0
  26. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/api.md +0 -0
  27. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/intro.md +0 -0
  28. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/logo.png +0 -0
  29. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/phantom_generation.md +0 -0
  30. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/AdjDataUser2gB0_transversal_0.08moving_average.mat +0 -0
  31. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/flash.ipynb +0 -0
  32. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/flash_DWI.ipynb +0 -0
  33. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr00_FLASH_2D_ernstAngle_opt.ipynb +0 -0
  34. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_CS_cartesian_seq.ipynb +0 -0
  35. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_CS_radial_seq.ipynb +0 -0
  36. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_DREAM_STE_seq.ipynb +0 -0
  37. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_DREAM_STID_seq.ipynb +0 -0
  38. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_DWI_GRE_2D_seq.ipynb +0 -0
  39. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_EPI_2D_seq.ipynb +0 -0
  40. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_FID_seq.ipynb +0 -0
  41. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_FLASH_2D_seq.ipynb +0 -0
  42. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_GRE_to_FLASH.ipynb +0 -0
  43. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_RARE_2D_seq.ipynb +0 -0
  44. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_SE_CPMG_seq.ipynb +0 -0
  45. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_STE_3pulses_5echoes_seq.ipynb +0 -0
  46. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_bSSFP_2D_seq.ipynb +0 -0
  47. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_burst_TSE.ipynb +0 -0
  48. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_Fit_T1.ipynb +0 -0
  49. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_opt_FLASH_2D_IR_voxelNN_T1.ipynb +0 -0
  50. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_pypulseq_exmpls_seq.ipynb +0 -0
  51. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/mr0_upload_seq.ipynb +0 -0
  52. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/numerical_brain_cropped.mat +0 -0
  53. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/overview.md +0 -0
  54. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/ptx_phantom.p +0 -0
  55. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/pulseq_flash.ipynb +0 -0
  56. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/pulseq_rf_shim.ipynb +0 -0
  57. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/pulseq_sim_pTx.ipynb +0 -0
  58. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/seqs/flash pTx CP.seq +0 -0
  59. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/seqs/flash pTx EP.seq +0 -0
  60. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/seqs/flash pTx QM.seq +0 -0
  61. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/playground_mr0/subject05.npz +0 -0
  62. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/requirements.txt +0 -0
  63. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/resources/logo.blend +0 -0
  64. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/resources/logo.png +0 -0
  65. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/documentation/resources/studio_small_09_2k.hdr +0 -0
  66. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/pyproject.toml +0 -0
  67. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/__init__.py +0 -0
  68. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/phantom/brainweb/brainweb_data_sources.txt +0 -0
  69. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/phantom/brainweb/output/.gitkeep +0 -0
  70. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/phantom/custom_voxel_phantom.py +0 -0
  71. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/phantom/sim_data.py +0 -0
  72. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/exporter.py +0 -0
  73. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/exporter_v2.py +0 -0
  74. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/helpers.py +0 -0
  75. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_exporter.py +0 -0
  76. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/__init__.py +0 -0
  77. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/adc.py +0 -0
  78. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/helpers.py +0 -0
  79. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulse.py +0 -0
  80. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/__init__.py +0 -0
  81. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/adc.py +0 -0
  82. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/block.py +0 -0
  83. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/definitons.py +0 -0
  84. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/gradient.py +0 -0
  85. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/helpers.py +0 -0
  86. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/rf.py +0 -0
  87. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/pulseq_file/trap.py +0 -0
  88. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/pulseq/pulseq_loader/spoiler.py +0 -0
  89. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/reconstruction.py +0 -0
  90. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/sequence.py +0 -0
  91. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/simulation/isochromat_sim.py +0 -0
  92. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/simulation/pre_pass.py +0 -0
  93. {MRzeroCore-0.2.10 → MRzeroCore-0.2.12}/python/MRzeroCore/util.py +0 -0
@@ -1,3 +1,9 @@
1
+ - 0.2.12
2
+ - Fixed Brainweb phantom loading and generation
3
+ - Switched pre-pass kt precision from f32 to f64
4
+ - Made pre-pass state merging relative to sequence gradient moment and event time sizes
5
+ - 0.2.11
6
+ - (re-)introduced return_mag_z parameter in pdg simulation
1
7
  - 0.2.10
2
8
  - change default FOV in all notebooks to phantom size (200 mm)
3
9
  - Use Open in Colab badges in the playground instead of links
@@ -53,7 +53,7 @@ dependencies = [
53
53
 
54
54
  [[package]]
55
55
  name = "mrzero_core"
56
- version = "0.2.10"
56
+ version = "0.2.12"
57
57
  dependencies = [
58
58
  "num-complex",
59
59
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "mrzero_core"
3
- version = "0.2.10"
3
+ version = "0.2.12"
4
4
  edition = "2021"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: MRzeroCore
3
- Version: 0.2.10
3
+ Version: 0.2.12
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -18,9 +18,9 @@ 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
21
22
  Project-URL: Documentation, https://mrzero-core.readthedocs.io/
22
23
  Project-URL: Repository, https://github.com/MRsources/MRzero-Core
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
 
@@ -42,5 +42,6 @@ chapters:
42
42
  - file: playground_mr0/mr00_FLASH_2D_ernstAngle_opt.ipynb
43
43
 
44
44
  - file: playground_mr0/flash
45
+ - file: playground_mr0/flash_DWI
45
46
  - file: playground_mr0/pulseq_flash
46
47
  - file: playground_mr0/pulseq_sim_pTx
@@ -151,7 +151,7 @@ def generate_brainweb_phantoms(
151
151
  print("Adding tissue to phantom", end="", flush=True)
152
152
  maps["PD_map"] += tissue_data["PD"] * tissue_map * noise()
153
153
  maps["T1_map"] += tissue_data["T1"] * tissue_map * noise()
154
- maps["T2_map"] += tissue_data["T2"] * noise()
154
+ maps["T2_map"] += tissue_data["T2"] * tissue_map * noise()
155
155
  maps["T2dash_map"] += tissue_data["T2'"] * tissue_map * noise()
156
156
  maps["D_map"] += tissue_data["D"] * tissue_map * noise()
157
157
  print(" - done")
@@ -33,7 +33,7 @@
33
33
  "gm": {
34
34
  "PD": 0.8,
35
35
  "T1": 1.56,
36
- "T2": 0.0083,
36
+ "T2": 0.083,
37
37
  "T2'": 0.32,
38
38
  "D": 0.83
39
39
  },
@@ -157,6 +157,10 @@ class VoxelGridPhantom:
157
157
  T2dash = torch.tensor(data['T2dash_map'])
158
158
  PD = torch.tensor(data['PD_map'])
159
159
  D = torch.tensor(data['D_map'])
160
+ try:
161
+ size = torch.tensor(data['FOV'])
162
+ except KeyError:
163
+ size = torch.tensor([0.192, 0.192, 0.192])
160
164
 
161
165
  # Generate a somewhat plausible B0 and B1 map.
162
166
  # Visually fitted to look similar to the numerical_brain_cropped
@@ -174,11 +178,6 @@ class VoxelGridPhantom:
174
178
  B0 -= (B0 * weight).sum()
175
179
  B1 /= (B1 * weight).sum()
176
180
 
177
- try:
178
- size = torch.tensor(data['FOV'])
179
- except KeyError:
180
- size = torch.tensor([0.192, 0.192, 0.192])
181
-
182
181
  return cls(
183
182
  PD, T1, T2, T2dash, D, B0, B1[None, ...],
184
183
  torch.ones(1, *PD.shape), size,
@@ -18,12 +18,9 @@ def execute_graph(graph: Graph,
18
18
  min_emitted_signal: float = 1e-2,
19
19
  min_latent_signal: float = 1e-2,
20
20
  print_progress: bool = True,
21
- ) -> torch.Tensor:
22
- """Calculate the signal of the sequence by computing the graph.
23
-
24
- This function can optionally return the + or z magnetisation and the
25
- encoding of all states, if requested. The encoding consists of the signal
26
- of a distribution and its k-t space trajectory.
21
+ return_mag_z: int | bool | None = None,
22
+ ) -> torch.Tensor | list:
23
+ """Calculate the signal of the sequence by executing the phase graph.
27
24
 
28
25
  Parameters
29
26
  ----------
@@ -40,11 +37,18 @@ def execute_graph(graph: Graph,
40
37
  Should be <= than min_emitted_signal.
41
38
  print_progress : bool
42
39
  If true, the current repetition is printed while simulating.
40
+ return_mag_z : int or bool, optional
41
+ If set, returns the longitudinal magnetisation of either the given
42
+ repetition (int) or all repetitions (``True``).
43
+
43
44
 
44
45
  Returns
45
46
  -------
46
47
  signal : torch.Tensor
47
48
  The simulated signal of the sequence.
49
+ mag_z : torch.Tensor | list[torch.Tensor]
50
+ The longitudinal magnetisation of the specified or all repetition(s).
51
+
48
52
  """
49
53
  if seq.normalized_grads:
50
54
  grad_scale = 1 / data.size
@@ -67,6 +71,7 @@ def execute_graph(graph: Graph,
67
71
  # Calculate kt_vec ourselves for autograd
68
72
  graph[0][0].kt_vec = torch.zeros(4, device=data.device)
69
73
 
74
+ mag_z = []
70
75
  for i, (dists, rep) in enumerate(zip(graph[1:], seq)):
71
76
  if print_progress:
72
77
  print(f"\rCalculating repetition {i+1} / {len(seq)}", end='')
@@ -129,7 +134,8 @@ def execute_graph(graph: Graph,
129
134
  # Use the same adc phase for all coils
130
135
  adc_rot = torch.exp(1j * rep.adc_phase).unsqueeze(1)
131
136
 
132
-
137
+ mag_z_rep = []
138
+ mag_z.append(mag_z_rep)
133
139
  for dist in dists:
134
140
  # Create a list only containing ancestors that were simulated
135
141
  ancestors = list(filter(
@@ -142,6 +148,9 @@ def execute_graph(graph: Graph,
142
148
  continue # skip dists for which no ancestors were simulated
143
149
 
144
150
  dist.mag = sum([calc_mag(ancestor) for ancestor in ancestors])
151
+ if dist.dist_type in ['z0', 'z'] and return_mag_z in [i, True]:
152
+ mag_z_rep.append(dist.mag)
153
+
145
154
  # The pre_pass already calculates kt_vec, but that does not
146
155
  # work with autograd -> we need to calculate it with torch
147
156
  if dist.dist_type == 'z0':
@@ -224,6 +233,10 @@ def execute_graph(graph: Graph,
224
233
  print(" - done")
225
234
 
226
235
  # Only return measured samples
227
- return torch.cat([
236
+ measured = torch.cat([
228
237
  sig[rep.adc_usage > 0, :] for sig, rep in zip(signal, seq)
229
238
  ])
239
+ if return_mag_z is not None:
240
+ return measured, mag_z
241
+ else:
242
+ return measured
@@ -29,7 +29,7 @@ struct PyDistribution {
29
29
  #[pyo3(get)]
30
30
  emitted_signal: f32,
31
31
  #[pyo3(get)]
32
- prepass_kt_vec: [f32; 4],
32
+ prepass_kt_vec: [f64; 4],
33
33
  }
34
34
 
35
35
  #[pymethods]
@@ -1,7 +1,6 @@
1
- use num_complex::Complex32;
1
+ use num_complex::{Complex32, ComplexFloat};
2
2
  use std::cell::RefCell;
3
3
  use std::collections::HashMap;
4
- use std::f32::consts::TAU;
5
4
  use std::iter;
6
5
  use std::rc::Rc;
7
6
 
@@ -42,7 +41,7 @@ pub struct Distribution {
42
41
  pub regrown_mag: f32, // Don't propagate to ancestors if its the own mag
43
42
  pub signal: f32,
44
43
  pub emitted_signal: f32, // relative to the strongest dist in the repetition
45
- pub kt_vec: [f32; 4],
44
+ pub kt_vec: [f64; 4],
46
45
  pub dist_type: DistType,
47
46
  pub ancestors: Vec<Edge>,
48
47
  /// latent_signal metric: if you want to measure states with a minimum signal of x,
@@ -53,17 +52,17 @@ pub struct Distribution {
53
52
  impl Distribution {
54
53
  fn measure(&mut self, t2dash: f32, nyquist: [f32; 3]) {
55
54
  let sigmoid = |x: f32| 1.0 / (1.0 + (-x).exp());
56
- let tmp = |nyq: f32, k: f32| sigmoid((nyq - k.abs() + 0.5) * 100.0);
55
+ let tmp = |nyq: f32, k: f64| sigmoid((nyq - k.abs() as f32 + 0.5) * 100.0);
57
56
  let dephasing = tmp(nyquist[0], self.kt_vec[0])
58
57
  * tmp(nyquist[1], self.kt_vec[1])
59
58
  * tmp(nyquist[2], self.kt_vec[2]);
60
59
 
61
- let norm = |x: f32, y: f32, z: f32| (x * x + y * y + z * z).sqrt();
60
+ let norm = |x: f64, y: f64, z: f64| (x * x + y * y + z * z).sqrt() as f32;
62
61
  let k_len = norm(self.kt_vec[0], self.kt_vec[1], self.kt_vec[2]);
63
62
 
64
63
  // Empirical signal drop-off is roughly estimated by x^(-2.2)
65
64
  self.signal += self.mag.norm()
66
- * (-(self.kt_vec[3] / t2dash).abs()).exp()
65
+ * (-(self.kt_vec[3] as f32 / t2dash).abs()).exp()
67
66
  * (1.0 + k_len).powf(-2.2)
68
67
  * dephasing;
69
68
  }
@@ -90,6 +89,20 @@ pub fn comp_graph(
90
89
  grad_scale: [f32; 3], // scale gradients to SI if they are normalized
91
90
  avg_b1_trig: &[[f32; 3]],
92
91
  ) -> Vec<Vec<RcDist>> {
92
+ // Make precision of state merging dependent on the units used in the seq.
93
+ // For this, we use a fraction of the smallest used step.
94
+ let min_kt_step = [
95
+ seq.iter().flat_map(|r| r.gradm_event.iter().map(|g| g[0].abs() as f64)).filter(|x| *x > 1e-3).min_by(f64::total_cmp),
96
+ seq.iter().flat_map(|r| r.gradm_event.iter().map(|g| g[1].abs() as f64)).filter(|x| *x > 1e-3).min_by(f64::total_cmp),
97
+ seq.iter().flat_map(|r| r.gradm_event.iter().map(|g| g[2].abs() as f64)).filter(|x| *x > 1e-3).min_by(f64::total_cmp),
98
+ seq.iter().flat_map(|r| r.event_time.iter().map(|t| *t as f64)).filter(|x| *x > 1e-6).min_by(f64::total_cmp)
99
+ ];
100
+ let inv_kt_grid = [
101
+ 1.0 / (0.1 * min_kt_step[0].unwrap_or(1.0)).clamp(1e-6, 1.0),
102
+ 1.0 / (0.1 * min_kt_step[1].unwrap_or(1.0)).clamp(1e-6, 1.0),
103
+ 1.0 / (0.1 * min_kt_step[2].unwrap_or(1.0)).clamp(1e-6, 1.0),
104
+ 1.0 / (0.1 * min_kt_step[3].unwrap_or(1e-3)).clamp(1e-9, 1e-3),
105
+ ];
93
106
  let mut graph: Vec<Vec<RcDist>> = Vec::new();
94
107
 
95
108
  let mut dists_p = DistVec::new();
@@ -112,6 +125,7 @@ pub fn comp_graph(
112
125
  max_dist_count,
113
126
  min_dist_mag,
114
127
  avg_b1_trig,
128
+ inv_kt_grid
115
129
  );
116
130
  dists_p = _dists_p;
117
131
  dists_z = _dists_z;
@@ -136,10 +150,10 @@ pub fn comp_graph(
136
150
  .zip(&rep.adc_mask)
137
151
  {
138
152
  let k1 = dist.kt_vec;
139
- dist.kt_vec[0] += gradm[0] * grad_scale[0];
140
- dist.kt_vec[1] += gradm[1] * grad_scale[1];
141
- dist.kt_vec[2] += gradm[2] * grad_scale[2];
142
- dist.kt_vec[3] += dt;
153
+ dist.kt_vec[0] += gradm[0] as f64 * grad_scale[0] as f64;
154
+ dist.kt_vec[1] += gradm[1] as f64 * grad_scale[1] as f64;
155
+ dist.kt_vec[2] += gradm[2] as f64 * grad_scale[2] as f64;
156
+ dist.kt_vec[3] += *dt as f64;
143
157
  let k2 = dist.kt_vec;
144
158
 
145
159
  // Integrating (dt omitted) over k²(t) = ((1-x)*k1 + x*k2)^2
@@ -148,7 +162,8 @@ pub fn comp_graph(
148
162
  * dt
149
163
  * ((k1[0] * k1[0] + k1[0] * k2[0] + k2[0] * k2[0])
150
164
  + (k1[1] * k1[1] + k1[1] * k2[1] + k2[1] * k2[1])
151
- + (k1[2] * k1[2] + k1[2] * k2[2] + k2[2] * k2[2]));
165
+ + (k1[2] * k1[2] + k1[2] * k2[2] + k2[2] * k2[2]))
166
+ as f32;
152
167
 
153
168
  dist.mag *= r2 * (-b * d).exp();
154
169
  if *adc {
@@ -165,7 +180,7 @@ pub fn comp_graph(
165
180
  let sqr = |x| x * x;
166
181
  let k2 = sqr(dist.kt_vec[0]) + sqr(dist.kt_vec[1]) + sqr(dist.kt_vec[2]);
167
182
 
168
- dist.mag *= r1 * (-d * rep_time * k2).exp();
183
+ dist.mag *= r1 * (-d * rep_time * k2 as f32).exp();
169
184
  }
170
185
  // Z0 has no diffusion because it's not dephased
171
186
  dist_z0.borrow_mut().mag *= r1;
@@ -183,22 +198,23 @@ fn apply_pulse(
183
198
  max_dist_count: usize,
184
199
  min_dist_mag: f32,
185
200
  avg_b1_trig: &[[f32; 3]],
201
+ inv_kt_grid: [f64; 4],
186
202
  ) -> (DistVec, DistVec, RcDist) {
187
203
  let rot_mat = RotMat::new(rep.pulse_angle, rep.pulse_phase, avg_b1_trig);
188
204
 
189
205
  let mut dist_dict_p: DistMap = HashMap::new();
190
206
  let mut dist_dict_z: DistMap = HashMap::new();
191
207
 
192
- let mut add_dist = |kt_vec: [f32; 4],
208
+ let mut add_dist = |kt_vec: [f64; 4],
193
209
  mag: Complex32,
194
210
  rot_mat_factor: Complex32,
195
211
  relation: DistRelation,
196
212
  ancestor: &RcDist| {
197
213
  let key = [
198
- (kt_vec[0] * 1e3).round() as i32,
199
- (kt_vec[1] * 1e3).round() as i32,
200
- (kt_vec[2] * 1e3).round() as i32,
201
- (kt_vec[3] * 1e6).round() as i32,
214
+ (kt_vec[0] * inv_kt_grid[0]).round() as i32,
215
+ (kt_vec[1] * inv_kt_grid[1]).round() as i32,
216
+ (kt_vec[2] * inv_kt_grid[2]).round() as i32,
217
+ (kt_vec[3] * inv_kt_grid[3]).round() as i32,
202
218
  ];
203
219
  let dist_type = match relation {
204
220
  DistRelation::PP | DistRelation::MP | DistRelation::ZP => DistType::P,
File without changes
File without changes
File without changes
File without changes
File without changes