MRArbGrad 4.3.0__tar.gz → 4.4.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 (41) hide show
  1. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0/MRArbGrad.egg-info}/PKG-INFO +1 -1
  2. {mrarbgrad-4.3.0/MRArbGrad.egg-info → mrarbgrad-4.4.0}/PKG-INFO +1 -1
  3. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/Function.py +184 -48
  4. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/Utility.py +108 -27
  5. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/MrTraj.h +2 -2
  6. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/pyproject.toml +1 -1
  7. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/LICENSE +0 -0
  8. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/MANIFEST.in +0 -0
  9. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/MRArbGrad.egg-info/SOURCES.txt +0 -0
  10. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/MRArbGrad.egg-info/dependency_links.txt +0 -0
  11. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/MRArbGrad.egg-info/requires.txt +0 -0
  12. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/MRArbGrad.egg-info/top_level.txt +0 -0
  13. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/README.md +0 -0
  14. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/example/example_BuiltInTraj.py +0 -0
  15. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/example/example_ExternalFunction2D.py +0 -0
  16. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/example/example_ExternalSamples2D.py +0 -0
  17. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/example/example_ExternalSamples3D.py +0 -0
  18. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/__init__.py +0 -0
  19. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/mag/Mag.cpp +0 -0
  20. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/mag/Mag.h +0 -0
  21. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/main.cpp +0 -0
  22. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/Cones.h +0 -0
  23. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/MrTraj_2D.h +0 -0
  24. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/Rosette.h +0 -0
  25. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/Seiffert.h +0 -0
  26. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/Shell3d.h +0 -0
  27. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/Spiral.h +0 -0
  28. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/TrajFunc.h +0 -0
  29. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/VDSpiral.h +0 -0
  30. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/traj/Yarnball.h +0 -0
  31. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/utility/Intp.h +0 -0
  32. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/utility/LinIntp.h +0 -0
  33. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/utility/SplineIntp.h +0 -0
  34. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/utility/global.cpp +0 -0
  35. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/utility/global.h +0 -0
  36. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/utility/v3.cpp +0 -0
  37. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/ext/utility/v3.h +0 -0
  38. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/trajfunc/__init__.py +0 -0
  39. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/mrarbgrad_src/trajfunc/main.py +0 -0
  40. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/setup.cfg +0 -0
  41. {mrarbgrad-4.3.0 → mrarbgrad-4.4.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MRArbGrad
3
- Version: 4.3.0
3
+ Version: 4.4.0
4
4
  Summary: Gradient waveform design tool for arbitrary k-space trajectories.
5
5
  Author-email: Ryan <ryan_shanghaitech@proton.me>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MRArbGrad
3
- Version: 4.3.0
3
+ Version: 4.4.0
4
4
  Summary: Gradient waveform design tool for arbitrary k-space trajectories.
5
5
  Author-email: Ryan <ryan_shanghaitech@proton.me>
6
6
  License-Expression: MIT
@@ -23,10 +23,24 @@ def calGrad4ExFunc\
23
23
  p0:float64 = 0e0,
24
24
  p1:float64 = 1e0,
25
25
  ) -> tuple[NDArray, NDArray]:
26
- '''
27
- :return: gradient waveform, corresponding parameter
28
- :rtype: tuple[NDArray, NDArray]
29
- '''
26
+ """
27
+ Generate gradient waveforms for external functions.
28
+
29
+ Args:
30
+ fov (float64, optional): Field of view, unit: `m`.
31
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
32
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
33
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
34
+ dt (float64, optional): Time step, unit: `s`.
35
+ getK (Callable | None, optional): Trajectory function.
36
+ getDkDp (Callable | None, optional): 1st order derivative to trajectory function.
37
+ getD2kDp2 (Callable | None, optional): 2nd order derivative to trajectory function.
38
+ p0 (float64, optional): Lower bound of the trajectory parameter.
39
+ p1 (float64, optional): Upper bound of the trajectory parameter.
40
+
41
+ Returns:
42
+ tuple[NDArray, NDArray]: Gradient waveform (in `Hz/pix`); Corresponding parameter
43
+ """
30
44
  return ext.calGrad4ExFunc\
31
45
  (
32
46
  float64(fov),
@@ -55,10 +69,20 @@ def calGrad4ExSamp\
55
69
 
56
70
  arrK: NDArray = np.empty((0,3)),
57
71
  ) -> tuple[NDArray, NDArray]:
58
- '''
59
- :return: gradient waveform, corresponding parameter
60
- :rtype: tuple[NDArray, NDArray]
61
- '''
72
+ """
73
+ Generate gradient waveforms for external trajectory samples.
74
+
75
+ Args:
76
+ fov (float64, optional): Field of view, unit: `m`.
77
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
78
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
79
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
80
+ dt (float64, optional): Time step, unit: `s`.
81
+ arrK (NDArray, optional): External trajectory samples, shape: `[nK,nAx]`
82
+
83
+ Returns:
84
+ tuple[NDArray, NDArray]: Gradient waveform (in `Hz/pix`); Corresponding parameter
85
+ """
62
86
  return ext.calGrad4ExSamp\
63
87
  (
64
88
  float64(fov),
@@ -83,10 +107,21 @@ def getG_Spiral\
83
107
  nSlice: int64 = 1,
84
108
  kRhoPhi: float64 = 0.5 / (4 * pi)
85
109
  ) -> tuple[list[NDArray], list[NDArray]]:
86
- '''
87
- :return: list of trajectory start, list of gradient waveforms
88
- :rtype: tuple[list[NDArray], list[NDArray]]
89
- '''
110
+ """
111
+ Generate gradient waveforms for a standard spiral trajectory.
112
+
113
+ Args:
114
+ fov (float64, optional): Field of view, unit: `m`.
115
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
116
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
117
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
118
+ dt (float64, optional): Time step, unit: `s`.
119
+ nSlice (int64, optional): Number of slices.
120
+ kRhoPhi (float64, optional): Shape parameter.
121
+
122
+ Returns:
123
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
124
+ """
90
125
  return ext.getG_Spiral\
91
126
  (
92
127
  float64(fov),
@@ -113,10 +148,22 @@ def getG_VDSpiral\
113
148
  kRhoPhi0: float64 = 0.5 / (8 * pi),
114
149
  kRhoPhi1: float64 = 0.5 / (2 * pi),
115
150
  ) -> tuple[list[NDArray], list[NDArray]]:
116
- '''
117
- :return: list of trajectory start, list of gradient waveforms
118
- :rtype: tuple[list[NDArray], list[NDArray]]
119
- '''
151
+ """
152
+ Generate gradient waveforms for a variable density (VD) spiral trajectory.
153
+
154
+ Args:
155
+ fov (float64, optional): Field of view, unit: `m`.
156
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
157
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
158
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
159
+ dt (float64, optional): Time step, unit: `s`.
160
+ nSlice (int64, optional): Number of slices.
161
+ kRhoPhi0 (float64, optional): Inner shape parameter.
162
+ kRhoPhi1 (float64, optional): Outer shape parameter.
163
+
164
+ Returns:
165
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
166
+ """
120
167
  return ext.getG_VDSpiral\
121
168
  (
122
169
  float64(fov),
@@ -143,10 +190,21 @@ def getG_VDSpiral_RT\
143
190
  kRhoPhi0: float64 = 0.5 / (8 * pi),
144
191
  kRhoPhi1: float64 = 0.5 / (2 * pi),
145
192
  ) -> tuple[list[NDArray], list[NDArray]]:
146
- '''
147
- :return: list of trajectory start, list of gradient waveforms
148
- :rtype: tuple[list[NDArray], list[NDArray]]
149
- '''
193
+ """
194
+ Generate gradient waveforms for a real-time variable density spiral trajectory.
195
+
196
+ Args:
197
+ fov (float64, optional): Field of view, unit: `m`.
198
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
199
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
200
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
201
+ dt (float64, optional): Time step, unit: `s`.
202
+ kRhoPhi0 (float64, optional): Inner shape parameter.
203
+ kRhoPhi1 (float64, optional): Outer shape parameter.
204
+
205
+ Returns:
206
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
207
+ """
150
208
  return ext.getG_VDSpiral_RT\
151
209
  (
152
210
  float64(fov),
@@ -174,10 +232,23 @@ def getG_Rosette\
174
232
  om2: float64 = 3*pi,
175
233
  tMax: float64 = 1e0,
176
234
  ) -> tuple[list[NDArray], list[NDArray]]:
177
- '''
178
- :return: list of trajectory start, list of gradient waveforms
179
- :rtype: tuple[list[NDArray], list[NDArray]]
180
- '''
235
+ """
236
+ Generate gradient waveforms for a rosette trajectory.
237
+
238
+ Args:
239
+ fov (float64, optional): Field of view, unit: `m`.
240
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
241
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
242
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
243
+ dt (float64, optional): Time step, unit: `s`.
244
+ nSlice (int64, optional): Number of slices.
245
+ om1 (float64, optional): Shape parameter.
246
+ om2 (float64, optional): Shape parameter.
247
+ tMax (float64, optional): Shape parameter.
248
+
249
+ Returns:
250
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
251
+ """
181
252
  return ext.getG_Rosette\
182
253
  (
183
254
  float64(fov),
@@ -208,10 +279,24 @@ def getG_Rosette_Trad\
208
279
  tMax: float64 = 1e0,
209
280
  tAcq: float64 = 2.523e-3,
210
281
  ) -> tuple[list[NDArray], list[NDArray]]:
211
- '''
212
- :return: list of trajectory start, list of gradient waveforms
213
- :rtype: tuple[list[NDArray], list[NDArray]]
214
- '''
282
+ """
283
+ Generate gradient waveforms for a traditional rosette trajectory.
284
+
285
+ Args:
286
+ fov (float64, optional): Field of view, unit: `m`.
287
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
288
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
289
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
290
+ dt (float64, optional): Time step, unit: `s`.
291
+ nSlice (int64, optional): Number of slices.
292
+ om1 (float64, optional): Shape parameter.
293
+ om2 (float64, optional): Shape parameter.
294
+ tMax (float64, optional): Shape parameter.
295
+ tAcq (float64, optional): Acquisition duration.
296
+
297
+ Returns:
298
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
299
+ """
215
300
  return ext.getG_Rosette_Trad\
216
301
  (
217
302
  float64(fov),
@@ -239,10 +324,20 @@ def getG_Shell3d\
239
324
 
240
325
  kRhoTht: float64 = 0.5 / (2 * pi),
241
326
  ) -> tuple[list[NDArray], list[NDArray]]:
242
- '''
243
- :return: list of trajectory start, list of gradient waveforms
244
- :rtype: tuple[list[NDArray], list[NDArray]]
245
- '''
327
+ """
328
+ Generate gradient waveforms for a 3D shell trajectory.
329
+
330
+ Args:
331
+ fov (float64, optional): Field of view, unit: `m`.
332
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
333
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
334
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
335
+ dt (float64, optional): Time step, unit: `s`.
336
+ kRhoTht (float64, optional): Shape parameter.
337
+
338
+ Returns:
339
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
340
+ """
246
341
  return ext.getG_Shell3d\
247
342
  (
248
343
  float64(fov),
@@ -266,10 +361,20 @@ def getG_Yarnball\
266
361
 
267
362
  kRhoPhi: float64 = 0.5 / (2 * pi),
268
363
  ) -> tuple[list[NDArray], list[NDArray]]:
269
- '''
270
- :return: list of trajectory start, list of gradient waveforms
271
- :rtype: tuple[list[NDArray], list[NDArray]]
272
- '''
364
+ """
365
+ Generate gradient waveforms for a yarnball trajectory.
366
+
367
+ Args:
368
+ fov (float64, optional): Field of view, unit: `m`.
369
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
370
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
371
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
372
+ dt (float64, optional): Time step, unit: `s`.
373
+ kRhoPhi (float64, optional): Shape parameter.
374
+
375
+ Returns:
376
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
377
+ """
273
378
  return ext.getG_Yarnball\
274
379
  (
275
380
  float64(fov),
@@ -293,10 +398,20 @@ def getG_Yarnball_RT\
293
398
 
294
399
  kRhoPhi: float64 = 0.5 / (2 * pi)
295
400
  ) -> tuple[list[NDArray], list[NDArray]]:
296
- '''
297
- :return: list of trajectory start, list of gradient waveforms
298
- :rtype: tuple[list[NDArray], list[NDArray]]
299
- '''
401
+ """
402
+ Generate gradient waveforms for a real-time yarnball trajectory.
403
+
404
+ Args:
405
+ fov (float64, optional): Field of view, unit: `m`.
406
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
407
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
408
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
409
+ dt (float64, optional): Time step, unit: `s`.
410
+ kRhoPhi (float64, optional): Shape parameter.
411
+
412
+ Returns:
413
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
414
+ """
300
415
  return ext.getG_Yarnball_RT\
301
416
  (
302
417
  float64(fov),
@@ -321,10 +436,21 @@ def getG_Seiffert\
321
436
  m: float64 = 0.07,
322
437
  uMax: float64 = 20.0,
323
438
  ) -> tuple[list[NDArray], list[NDArray]]:
324
- '''
325
- :return: list of trajectory start, list of gradient waveforms
326
- :rtype: tuple[list[NDArray], list[NDArray]]
327
- '''
439
+ """
440
+ Generate gradient waveforms for a Seiffert spiral trajectory.
441
+
442
+ Args:
443
+ fov (float64, optional): Field of view, unit: `m`.
444
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
445
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
446
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
447
+ dt (float64, optional): Time step, unit: `s`.
448
+ m (float64, optional): Shape parameter.
449
+ uMax (float64, optional): Shape parameter.
450
+
451
+ Returns:
452
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
453
+ """
328
454
  return ext.getG_Seiffert\
329
455
  (
330
456
  float64(fov),
@@ -349,10 +475,20 @@ def getG_Cones\
349
475
 
350
476
  kRhoPhi: float64 = 0.5 / (4 * pi),
351
477
  ) -> tuple[list[NDArray], list[NDArray]]:
352
- '''
353
- :return: list of trajectory start, list of gradient waveforms
354
- :rtype: tuple[list[NDArray], list[NDArray]]
355
- '''
478
+ """
479
+ Generate gradient waveforms for a 3D cones trajectory.
480
+
481
+ Args:
482
+ fov (float64, optional): Field of view, unit: `m`.
483
+ nPix (int64, optional): Number of pixel, unit: `a.u.`.
484
+ sLim (float64, optional): l2 Slew limit, unit: `Hz/pix/s`.
485
+ gLim (float64, optional): l2 Gradient limit, unit: `Hz/pix`.
486
+ dt (float64, optional): Time step, unit: `s`.
487
+ kRhoPhi (float64, optional): Shape parameter.
488
+
489
+ Returns:
490
+ tuple[list[NDArray], list[NDArray]]: List of trajectory start points (in `/pix`); List of gradient waveforms (in `Hz/pix`)
491
+ """
356
492
  return ext.getG_Cones\
357
493
  (
358
494
  float64(fov),
@@ -1,6 +1,7 @@
1
1
  from numpy import *
2
2
  from numpy.linalg import norm
3
3
  from numpy.typing import *
4
+ from itertools import product
4
5
 
5
6
  goldrat = (1+sqrt(5))/2
6
7
  goldang = (2*pi)/(1+goldrat)
@@ -8,6 +9,86 @@ goldang = (2*pi)/(1+goldrat)
8
9
  def getGoldrat()->float: return goldrat
9
10
  def getGoldang()->float: return goldang
10
11
 
12
+ def unitcvt(x:float, fov:float, nPix:int, dir:int, gamma:float=42.5756e6) -> float:
13
+ """
14
+ This package uses `Hz/pix` for gradient unit, and `Hz/pix/s` for slew unit, and `pix` for length unit. This unit system has several good properties for theory derivation. You may need this helper function for unit conversion in case you are not familiar with this unit system.
15
+
16
+ You may need `dir=1,2` to convert common units (like `mT/m` for gradient and `T/m/s` for slew) to units supported by this package (like `Hz/pix` for gradient and `Hz/pix/s` for slew), before you call any gradient generation functions, or `dir=-1` when converting gradients generated by this package in `Hz/pix` back to `mT/m`.
17
+
18
+ Args:
19
+ x (float): Input value.
20
+ fov (float): Field of view.
21
+ nPix (int): Number of pixels.
22
+ dir (int):
23
+ `1`: from `mT/m` to `Hz/pix`.
24
+ `2`: from `T/m/s` to `Hz/pix/s`.
25
+ `-1`: from `Hz/pix` to `mT/m`.
26
+ `-2`: from `Hz/pix/s` to `T/m/s`.
27
+ gamma (float): Gyromagnetic ratio `Hz/T`.
28
+
29
+ Returns:
30
+ float: Converted value.
31
+ """
32
+ if dir==1:
33
+ return x * (1e-3*gamma*fov/nPix)
34
+ if dir==2:
35
+ return x * (gamma*fov/nPix)
36
+ if dir==-1:
37
+ return x / (1e-3*gamma*fov/nPix)
38
+ if dir==-2:
39
+ return x / (gamma*fov/nPix)
40
+ raise RuntimeError("dir")
41
+
42
+ def getK_Cartesian(nPix:int, nAx:int) -> list[NDArray]:
43
+ """
44
+ Generates a Cartesian sampling pattern.
45
+
46
+ Args:
47
+ nPix (int): Number of pixels.
48
+ nAx (int): Number of dimensions.
49
+
50
+ Returns:
51
+ list[NDArray]: List of trajectories in `[nK,nAx]`.
52
+ """
53
+ arrK1D = linspace(-0.5, 0.5, nPix, endpoint=0)
54
+ lstArrK:list[NDArray] = []
55
+
56
+ for arrKyKz in product(arrK1D, repeat=nAx-1):
57
+ arrK = empty((nPix,nAx), dtype=float32)
58
+ arrK[:,0] = arrK1D
59
+ arrK[:,1:] = arrKyKz
60
+ lstArrK.append(arrK)
61
+
62
+ return lstArrK
63
+
64
+
65
+ def getK_Radial(nPix:int, nAx:int, nSpoke:int, enGoldAng:bool) -> list:
66
+ """
67
+ Generates a Radial sampling pattern.
68
+
69
+ Args:
70
+ nPix (int): Number of pixels.
71
+ nAx (int): Number of dimensions.
72
+ nSpoke (int): Number of radial spokes.
73
+ enGoldAng (bool): If True, uses the golden angle for spoke spacing.
74
+
75
+ Returns:
76
+ list: A list of numpy arrays, each of shape [nPix, nAx], representing radial spokes.
77
+ """
78
+ if nAx!=2: raise NotImplementedError("nAx!=2")
79
+ lstArrK = []
80
+ arrK1D = linspace(-0.5, 0.5, nPix, endpoint=0)
81
+
82
+ for iSpoke in range(nSpoke):
83
+ if enGoldAng: tht = iSpoke * goldang
84
+ else: tht = iSpoke * (pi / nSpoke)
85
+ arrK = empty((nPix,nAx), dtype=float32)
86
+ arrK[:,0] = arrK1D * cos(tht)
87
+ arrK[:,1] = arrK1D * sin(tht)
88
+ lstArrK.append(arrK)
89
+
90
+ return lstArrK
91
+
11
92
  def gradClip(lstArrGrad:list[NDArray]|NDArray, dt:float, sLim:float, gLim:float) -> list[NDArray]|NDArray:
12
93
  """
13
94
  Clip the slewrate and gradient amlitude of a list of gradient waveforms
@@ -64,70 +145,70 @@ def genPermTab(n:int) -> list[int]:
64
145
  lstIdx.append(i*inc%n)
65
146
  return lstIdx
66
147
 
67
- def cvtGrad2Traj(arrG:NDArray, dtGrad:int|float, dtADC:int|float, nShift:int|float=1.0) -> tuple[NDArray, NDArray]:
148
+ def cvtGrad2Traj(arrGrad:NDArray, dtGrad:int|float, dtAdc:int|float, nShift:int|float=1.0) -> tuple[NDArray, NDArray]:
68
149
  """
69
150
  # description:
70
151
  interpolate gradient waveform and calculate trajectory
71
152
 
72
153
  # parameter
73
- `arrG`: array of gradient waveform
74
- `dtGrad`, `dtADC`: temporal resolution of gradient system and ADC
154
+ `arrGrad`: array of gradient waveform
155
+ `dtGrad`, `dtAdc`: temporal resolution of gradient system and ADC
75
156
 
76
157
  # return:
77
158
  interpolated trajectory and gradient
78
159
  """
79
- dtShift = nShift*dtADC
80
- nGrad, nDim = arrG.shape
81
- nADC = int(dtGrad/dtADC)*(nGrad-1)
82
- arrG_Resamp = zeros([nADC,nDim], dtype=float64)
83
- for iDim in range(nDim):
84
- arrG_Resamp[:,iDim] = interp(dtADC*arange(nADC)+dtShift, dtGrad*arange(nGrad), arrG[:,iDim])
85
- arrDk = zeros_like(arrG_Resamp)
86
- arrDk[0,:] = (arrG[0,:] + arrG_Resamp[0,:])*dtShift/2
87
- arrDk[1:,:] = (arrG_Resamp[:-1] + arrG_Resamp[1:])*dtADC/2
160
+ dtShift = nShift*dtAdc
161
+ nGrad, nAx = arrGrad.shape
162
+ nAdc = int(dtGrad/dtAdc)*(nGrad-1)
163
+ arrGrad_Resamp = zeros([nAdc,nAx], dtype=float64)
164
+ for iDim in range(nAx):
165
+ arrGrad_Resamp[:,iDim] = interp(dtAdc*arange(nAdc)+dtShift, dtGrad*arange(nGrad), arrGrad[:,iDim])
166
+ arrDk = zeros_like(arrGrad_Resamp)
167
+ arrDk[0,:] = (arrGrad[0,:] + arrGrad_Resamp[0,:])*dtShift/2
168
+ arrDk[1:,:] = (arrGrad_Resamp[:-1] + arrGrad_Resamp[1:])*dtAdc/2
88
169
  arrK = cumsum(arrDk,axis=0)
89
- return arrK, arrG_Resamp
170
+ return arrK, arrGrad_Resamp
90
171
 
91
- def delGrad(arrG:NDArray, tau:int|float) -> NDArray:
172
+ def delGrad(arrGrad:NDArray, tau:int|float) -> NDArray:
92
173
  """
93
174
  # description:
94
175
  delay the input gradient waveform by time constant tau
95
176
 
96
177
  # parameter
97
- `arrG`: array of single gradient waveform
178
+ `arrGrad`: array of single gradient waveform
98
179
  `tau`: time constant in RL circuit transfer function
99
180
 
100
181
  # return:
101
182
  delayed gradient waveform
102
183
  """
103
- assert arrG.ndim == 2, "only single gradient waveform is supported."
104
- if tau == 0: return arrG.copy() # avoid divided-by-0 later
105
- nPt, nAx = arrG.shape
184
+ assert arrGrad.ndim == 2, "only single gradient waveform is supported."
185
+ if tau == 0: return arrGrad.copy() # avoid divided-by-0 later
186
+ nPt, nAx = arrGrad.shape
106
187
 
107
188
  # perform oversample to get better impluse response profile
108
189
  ov = clip(10/tau, 1, 1e3).astype(int64) # the smaller the ov, the bigger oversampling is needed
109
- arrG_ov = zeros([nPt*ov,nAx], dtype=arrG.dtype)
190
+ arrGrad_ov = zeros([nPt*ov,nAx], dtype=arrGrad.dtype)
110
191
  for iAx in range(nAx):
111
- arrG_ov[:,iAx] = interp(linspace(0,nPt,nPt*ov,0), linspace(0,nPt,nPt,0), arrG[:,iAx]) # oversample
192
+ arrGrad_ov[:,iAx] = interp(linspace(0,nPt,nPt*ov,0), linspace(0,nPt,nPt,0), arrGrad[:,iAx]) # oversample
112
193
  nPt *= ov
113
194
  tau *= ov
114
195
 
115
196
  # derive impluse response of RL circuit
116
- arrG_Pad = zeros_like(arrG_ov)
117
- arrG_Pad[:nPt//2,:] = arrG_ov[-1:,:]
118
- arrG_Pad[nPt//2:,:] = arrG_ov[:1,:]
119
- arrG_ov = concatenate([arrG_ov, arrG_Pad], axis=0)
197
+ arrGrad_Pad = zeros_like(arrGrad_ov)
198
+ arrGrad_Pad[:nPt//2,:] = arrGrad_ov[-1:,:]
199
+ arrGrad_Pad[nPt//2:,:] = arrGrad_ov[:1,:]
200
+ arrGrad_ov = concatenate([arrGrad_ov, arrGrad_Pad], axis=0)
120
201
  arrT = linspace(0,2*nPt,2*nPt,0) + 0.5
121
202
  arrImpResRL = (1/tau)*exp(-arrT/tau)
122
203
  if abs(arrImpResRL.sum() - 1) > 1e-2: raise ValueError(f"arrImpResRL.sum() = {arrImpResRL.sum():.2f} (supposed to be 1) (tau too small or too large)")
123
204
 
124
205
  # perform convolution between input waveform and impulse response
125
- arrG_ov = fft.ifft(fft.fft(arrG_ov,axis=0)*fft.fft(arrImpResRL)[:,newaxis], axis=0).real
206
+ arrGrad_ov = fft.ifft(fft.fft(arrGrad_ov,axis=0)*fft.fft(arrImpResRL)[:,newaxis], axis=0).real
126
207
 
127
208
  # de-oversample
128
- arrG = arrG_ov[:nPt:ov,:]
209
+ arrGrad = arrGrad_ov[:nPt:ov,:]
129
210
 
130
- return arrG
211
+ return arrGrad
131
212
 
132
213
  def rotate(arr:NDArray, ang:float64, axis:int64) -> NDArray:
133
214
  if axis==0: # x
@@ -13,7 +13,7 @@
13
13
  bool gMrTraj_enMtg = false; // whether to use Lustig's minTimeGrad solver
14
14
  f64 gMrTraj_g0Norm = 0e0; // initial gradient amplitude
15
15
  f64 gMrTraj_g1Norm = 0e0; // final gradient amplitude
16
- i64 gMrTraj_nGradSampRsv = 10000; // buffer size of mag solver
16
+ i64 gMrTraj_nGradSampRsv = 10000; // buffer size of MAG solver
17
17
  i64 gMrTraj_nTrajSampRsv = 10000; // num. of samp. when doing Traj. Rep.
18
18
 
19
19
  /*
@@ -204,7 +204,7 @@ protected:
204
204
  minRotang = std::min(minRotang, rotang);
205
205
  }
206
206
 
207
- // ensure the rot. Num. is a integer
207
+ // ensure the rot. Num. is an integer
208
208
  return (i64)std::ceil(2e0*M_PI/minRotang);
209
209
  }
210
210
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "MRArbGrad"
7
- version = "4.3.0"
7
+ version = "4.4.0"
8
8
  dependencies = ["numpy"]
9
9
 
10
10
  description = "Gradient waveform design tool for arbitrary k-space trajectories."
File without changes
File without changes
File without changes
File without changes
File without changes