Skip to content

Commit 2489ef2

Browse files
authored
Acquisition Configuration (#285)
* feat!: acquisition Config. This is a breaking change! * fmt: black * feat(test): use acquisition fixture for tests. * feat: docstrings * refactor!: use acq in display functions as well * fmt:black * fix: correct field name. * feat: move adc setup to acquisition level. * fix: example use the acquisition object * feat: example use acquisition. * fix: style * use correct units * fix: enum meta has evolved in python 3.12 * feat: use better default for grad raster time. * feat: more SI units and acq use * fix: don't modify API for write/read_trajectory * fmt * fix examples * update * fix: acq and display * feat: add context manager for the acquisition * update context manager * fmt: PEP604 type union fixes UP045 * [docs] update docstring for acquisition config.
1 parent 63f90a3 commit 2489ef2

File tree

11 files changed

+508
-320
lines changed

11 files changed

+508
-320
lines changed

examples/GPU/example_3d_trajectory_display.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,22 @@
1414
import os
1515
from mrinufft.trajectories.display3D import get_gridded_trajectory
1616
import mrinufft.trajectories.trajectory3D as mtt
17-
from mrinufft.trajectories.utils import Gammas
17+
from mrinufft.trajectories.utils import Gammas, Acquisition
1818
import matplotlib.pyplot as plt
1919
import numpy as np
2020

2121

2222
BACKEND = os.environ.get("MRINUFFT_BACKEND", "gpunufft")
2323

2424

25+
# %%
26+
# Acquisition parameters
27+
# ======================
28+
# Here we use acquisition defaults for the trajectory gridding.
29+
30+
acq = Acquisition.default
31+
32+
2533
# %%
2634
# Helper function to Displaying 3D Gridded Trajectories
2735
# =====================================================
@@ -57,9 +65,8 @@ def create_grid(grid_type, trajectories, traj_params, **kwargs):
5765
for i, (name, traj) in enumerate(trajectories.items()):
5866
grid = get_gridded_trajectory(
5967
traj,
60-
traj_params["img_size"],
68+
acq,
6169
grid_type=grid_type,
62-
traj_params=traj_params,
6370
backend=BACKEND,
6471
osf=2,
6572
**kwargs,

examples/operators/example_offresonance.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@
8585

8686
from mrinufft import initialize_2D_spiral
8787
from mrinufft.density import voronoi
88-
from mrinufft.trajectories.utils import DEFAULT_RASTER_TIME
88+
from mrinufft.trajectories.utils import Acquisition
8989

9090
samples = initialize_2D_spiral(Nc=48, Ns=600, nb_revolutions=10)
91-
t_read = np.arange(samples.shape[1]) * DEFAULT_RASTER_TIME * 1e-3
91+
t_read = np.arange(samples.shape[1]) * Acquisition.default.raster_time
9292
t_read = np.repeat(t_read[None, ...], samples.shape[0], axis=0)
9393
density = voronoi(samples)
9494

examples/trajectories/example_3D_trajectories.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
# Internal
3232
import mrinufft as mn
33-
from mrinufft import display_2D_trajectory, display_3D_trajectory
3433

3534
# %%
3635
# Script options

examples/trajectories/example_display_config.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from mrinufft import display_2D_trajectory, display_3D_trajectory, displayConfig
1717
from mrinufft.trajectories import conify, initialize_2D_spiral
18+
from mrinufft.trajectories.utils import Acquisition, Hardware
1819

1920
# %%
2021
# Script options
@@ -118,3 +119,33 @@ def show_traj(traj, name, values, **kwargs):
118119
["tab:blue", "tab:orange", "tab:red"],
119120
show_constraints=True,
120121
)
122+
123+
# You can also change the values of gmax and smax in order to see how the constraint
124+
# violations change.
125+
#
126+
acqs = [
127+
Acquisition(
128+
fov=(0.256, 0.256, 0.256),
129+
img_size=(256, 256, 256),
130+
hardware=Hardware(gmax=0.04, smax=50),
131+
), # limiting slew rate to 50 T/m/s
132+
Acquisition(
133+
fov=(0.256, 0.256, 0.256),
134+
img_size=(256, 256, 256),
135+
hardware=Hardware(gmax=0.04, smax=100),
136+
), # limiting slew rate to 100 T/m/s
137+
Acquisition(
138+
fov=(0.256, 0.256, 0.256),
139+
img_size=(256, 256, 256),
140+
hardware=Hardware(gmax=0.04, smax=200),
141+
),
142+
] # limiting slew rate to 200 T/m/s
143+
144+
# you can use Acquisition as a Context Manager, like display config.
145+
# Or pass it to the display function as well.
146+
for acq in acqs:
147+
with acq:
148+
display_3D_trajectory(traj, show_constraints=True)
149+
150+
# equivalent to
151+
# display_3D_trajectory(traj, show_constraints=True, acq=acq)

examples/trajectories/utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616
def show_trajectory(trajectory, one_shot, figure_size):
1717
if trajectory.shape[-1] == 2:
1818
ax = display_2D_trajectory(
19-
trajectory, size=figure_size, one_shot=one_shot % trajectory.shape[0]
19+
trajectory, figsize=figure_size, one_shot=one_shot % trajectory.shape[0]
2020
)
2121
ax.set_aspect("equal")
2222
plt.tight_layout()
2323
plt.show()
2424
else:
2525
ax = display_3D_trajectory(
2626
trajectory,
27-
size=figure_size,
27+
figsize=figure_size,
2828
one_shot=one_shot % trajectory.shape[0],
2929
per_plane=False,
3030
)
@@ -49,15 +49,15 @@ def show_trajectories(
4949
if dim == "3D" and traj.shape[-1] == 3:
5050
ax = display_3D_trajectory(
5151
traj,
52-
size=subfig_size,
52+
figsize=subfig_size,
5353
one_shot=one_shot % traj.shape[0],
5454
subfigure=subfig,
5555
per_plane=False,
5656
)
5757
else:
5858
ax = display_2D_trajectory(
5959
traj[..., axes],
60-
size=subfig_size,
60+
figsize=subfig_size,
6161
one_shot=one_shot % traj.shape[0],
6262
subfigure=subfig,
6363
)

src/mrinufft/io/nsp.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# type: ignore
12
"""Read/Write trajectory for Neurospin sequences."""
23

34
from __future__ import annotations
@@ -8,17 +9,19 @@
89
from datetime import datetime
910

1011
import numpy as np
11-
from typing import Optional
1212

1313
from mrinufft.trajectories.utils import (
14-
DEFAULT_GMAX,
15-
DEFAULT_RASTER_TIME,
16-
DEFAULT_SMAX,
17-
KMAX,
14+
Acquisition,
15+
Hardware,
1816
Gammas,
17+
SI,
1918
check_hardware_constraints,
2019
convert_gradients_to_slew_rates,
2120
convert_trajectory_to_gradients,
21+
DEFAULT_GMAX,
22+
DEFAULT_SMAX,
23+
KMAX,
24+
DEFAULT_RASTER_TIME,
2225
)
2326
from mrinufft.trajectories.tools import get_gradient_amplitudes_to_travel_for_set_time
2427

@@ -47,7 +50,7 @@ def write_gradients(
4750
Parameters
4851
----------
4952
gradients : np.ndarray
50-
Gradients. Shape (num_shots, num_samples_per_shot, dimension).
53+
Gradients. Shape (num_shots, num_samples_per_shot, dimension). in T/m.
5154
initial_positions : np.ndarray
5255
Initial positions. Shape (num_shots, dimension).
5356
grad_filename : str
@@ -211,7 +214,7 @@ def write_trajectory(
211214
img_size: tuple[int, ...],
212215
grad_filename: str,
213216
norm_factor: float = KMAX,
214-
gamma: float = Gammas.HYDROGEN,
217+
gamma: float = Gammas.HYDROGEN / 1e3,
215218
raster_time: float = DEFAULT_RASTER_TIME,
216219
check_constraints: bool = True,
217220
TE_pos: float = 0.5,
@@ -270,22 +273,20 @@ def write_trajectory(
270273
These are arguments passed to write_gradients function above.
271274
"""
272275
# Convert normalized trajectory to gradients
276+
acq = Acquisition(
277+
fov=FOV,
278+
img_size=img_size,
279+
gamma=gamma,
280+
norm_factor=norm_factor,
281+
hardware=Hardware(gmax=gmax, smax=smax, grad_raster_time=raster_time),
282+
)
273283
gradients, initial_positions, final_positions = convert_trajectory_to_gradients(
274284
trajectory,
275-
norm_factor=norm_factor,
276-
resolution=np.asarray(FOV) / np.asarray(img_size),
277-
raster_time=raster_time,
278-
gamma=gamma,
285+
acq=acq,
279286
get_final_positions=True,
280287
)
281288
Ns_to_skip_at_start = 0
282289
Ns_to_skip_at_end = 0
283-
scan_consts = {
284-
"gamma": gamma,
285-
"gmax": gmax,
286-
"smax": smax,
287-
"raster_time": raster_time,
288-
}
289290
if pregrad == "prephase":
290291
if version < 5.1:
291292
raise ValueError(
@@ -295,7 +296,7 @@ def write_trajectory(
295296
start_gradients = get_gradient_amplitudes_to_travel_for_set_time(
296297
kspace_end_loc=initial_positions,
297298
end_gradients=gradients[:, 0],
298-
**scan_consts,
299+
acq=acq,
299300
)
300301
initial_positions = np.zeros_like(initial_positions)
301302
gradients = np.hstack([start_gradients, gradients])
@@ -314,18 +315,17 @@ def write_trajectory(
314315
kspace_end_loc=edge_locations,
315316
start_gradients=gradients[:, -1],
316317
kspace_start_loc=final_positions,
317-
**scan_consts,
318+
acq=acq,
318319
)
319320
gradients = np.hstack([gradients, end_gradients])
320321
Ns_to_skip_at_end = end_gradients.shape[1]
321322
# Check constraints if requested
322323
if check_constraints:
323-
slewrates, _ = convert_gradients_to_slew_rates(gradients, raster_time)
324+
slewrates, _ = convert_gradients_to_slew_rates(gradients, acq)
324325
valid, maxG, maxS = check_hardware_constraints(
325326
gradients=gradients,
326327
slewrates=slewrates,
327-
gmax=gmax,
328-
smax=smax,
328+
acq=acq,
329329
)
330330
if not valid:
331331
warnings.warn(

0 commit comments

Comments
 (0)