Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/mrseq/preparations/t2_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np
import pypulseq as pp

from mrseq.utils import round_to_raster
from mrseq.utils import sys_defaults


Expand Down Expand Up @@ -169,6 +170,7 @@ def add_t2_prep(
- system.rf_dead_time # dead time of 180° pulse in refocusing block
- duration_180 / 2 # half duration of 180° pulse in refocusing block
)
tau1 = round_to_raster(tau1, system.block_duration_raster)

if tau1 < 0:
raise ValueError(f'Desired echo time ({echo_time * 1000:.2f} ms) is too short to create the T2 prep block.')
Expand All @@ -190,6 +192,7 @@ def add_t2_prep(
- time_since_refocusing # time since refocusing in 1st refocusing block
- (refoc_dur - time_since_refocusing) # time until refocusing point in 2nd block
)
tau2 = round_to_raster(tau2, system.block_duration_raster)

if tau2 < 0:
raise ValueError(f'Desired echo time ({echo_time * 1000:.2f} ms) is too short to create the T2 prep block.')
Expand Down Expand Up @@ -234,6 +237,7 @@ def add_t2_prep(
- system.rf_dead_time # dead time of 270° pulse in tip-up block
- duration_180 / 2 * 3 / 2 # half duration of 270° pulse
)
tau3 = round_to_raster(tau3, system.block_duration_raster)

if tau3 < 0:
raise ValueError(f'Desired echo time ({echo_time * 1000:.2f} ms) is too short to create the T2 prep block.')
Expand Down
6 changes: 3 additions & 3 deletions src/mrseq/scripts/grpe_flash_dixon.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ def grpe_flash_dixon_kernel(
seq_dummy = pp.Sequence(system=system)
seq_dummy, _ = multi_echo_gradient.add_to_seq_without_pre_post_gradient(seq_dummy, n_echoes)
readout_duration = sum(seq_dummy.block_durations.values())
seq.add_block(pp.make_delay(readout_duration))
seq.add_block(pp.make_delay(round_to_raster(readout_duration, system.block_duration_raster)))

# add re-winder and spoiler gradients
gy_pre.amplitude = -gy_pre.amplitude
Expand Down Expand Up @@ -512,7 +512,7 @@ def main(
n_readout_with_oversampling = int(n_readout * readout_oversampling * partial_echo_factor)
# define ADC and gradient timing
adc_dwell_time = 1.0 / (receiver_bandwidth_per_pixel * n_readout_with_oversampling)
gx_pre_duration = 1.0e-3 # duration of readout pre-winder gradient [s]
gx_pre_duration = round_to_raster(1.2e-3, system.grad_raster_time) # duration of readout pre-winder gradient [s]
gx_flat_time, adc_dwell_time = find_gx_flat_time_on_adc_raster(
n_readout_with_oversampling, adc_dwell_time, system.grad_raster_time, system.adc_raster_time
)
Expand All @@ -522,7 +522,7 @@ def main(
n_echoes = 3

# define spoiling
gx_spoil_duration = 1.9e-3 # duration of spoiler gradient [s]
gx_spoil_duration = round_to_raster(2.0e-3, system.grad_raster_time) # duration of spoiler gradient [s]
gx_spoil_area = readout_oversampling * n_readout * 1 / fov_x # area / zeroth gradient moment of spoiler gradient
rf_spoiling_phase_increment = 117 # RF spoiling phase increment [°]. Set to 0 for no RF spoiling.

Expand Down
2 changes: 1 addition & 1 deletion src/mrseq/scripts/t1_molli_bssfp.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def main(
inversion_times = np.asarray([0.1, 0.18])

# define settings of rf excitation pulse
rf_duration = 0.5e-3 # duration of the rf excitation pulse [s]
rf_duration = 0.6e-3 # duration of the rf excitation pulse [s]
rf_flip_angle = 35 # flip angle of rf excitation pulse [°]
rf_bwt = 1.5 # bandwidth-time product of rf excitation pulse [Hz*s]
rf_apodization = 0.5 # apodization factor of rf excitation pulse
Expand Down
12 changes: 7 additions & 5 deletions src/mrseq/scripts/t1_t2_spiral_cmrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ def t1_t2_spiral_cmrf_kernel(

# cMRF specific settings
n_blocks = 15 # number of heartbeat blocks
minimum_time_to_set_label = 1e-5 # minimum time to set a label (in seconds)
minimum_time_to_set_label = round_to_raster(
1e-5, system.block_duration_raster
) # minimum time to set a label (in seconds)

# create flip angle pattern
max_flip_angles_deg = [12.5, 18.75, 25, 25, 25, 12.5, 18.75, 25, 25.0, 25, 12.5, 18.75, 25, 25, 25]
Expand Down Expand Up @@ -149,7 +151,7 @@ def t1_t2_spiral_cmrf_kernel(

# calculate minimum echo time (TE) for sequence header
min_te = pp.calc_duration(gz_dummy) / 2 + pp.calc_duration(gzr_dummy) + time_to_echo
min_te = round_to_raster(min_te, system.grad_raster_time)
min_te = round_to_raster(min_te, system.block_duration_raster)

# calculate minimum repetition time (TR)
min_tr = (
Expand All @@ -161,13 +163,13 @@ def t1_t2_spiral_cmrf_kernel(
)

# ensure minimum TR is on gradient raster
min_tr = round_to_raster(min_tr, system.grad_raster_time)
min_tr = round_to_raster(min_tr, system.block_duration_raster)

# calculate TR delay
if tr is None:
tr_delay = minimum_time_to_set_label
else:
tr_delay = round_to_raster((tr - min_tr + minimum_time_to_set_label), system.grad_raster_time)
tr_delay = round_to_raster((tr - min_tr + minimum_time_to_set_label), system.block_duration_raster)
if not tr_delay >= 0:
raise ValueError(f'TR must be larger than {min_tr * 1000:.3f} ms. Current value is {tr * 1000:.3f} ms.')

Expand Down Expand Up @@ -388,7 +390,7 @@ def main(
t2_prep_echo_times = np.array([0.03, 0.05, 0.1]) # [s]

# define T1prep settings
rf_inv_duration = 10.24e-3 # duration of adiabatic inversion pulse [s]
rf_inv_duration = 12e-3 # duration of adiabatic inversion pulse [s]
rf_inv_spoil_risetime = 0.6e-3 # rise time of spoiler after inversion pulse [s]
rf_inv_spoil_flattime = 8.4e-3 # flat time of spoiler after inversion pulse [s]

Expand Down
28 changes: 28 additions & 0 deletions src/mrseq/utils/system_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,31 @@
rf_dead_time=100e-6,
adc_dead_time=10e-6,
)

sys_a = Opts(
max_grad=30,
grad_unit='mT/m',
max_slew=120,
slew_unit='T/m/s',
grad_raster_time=8e-6,
rf_ringdown_time=60e-6,
rf_raster_time=2e-6,
rf_dead_time=100e-6,
adc_dead_time=40e-6,
adc_raster_time=2e-6,
block_duration_raster=4e-6,
)

sys_b = Opts(
max_grad=30,
grad_unit='mT/m',
max_slew=120,
slew_unit='T/m/s',
grad_raster_time=10e-6,
rf_ringdown_time=30e-6,
rf_raster_time=1e-6,
rf_dead_time=100e-6,
adc_dead_time=10e-6,
adc_raster_time=1e-7,
block_duration_raster=10e-6,
)
13 changes: 12 additions & 1 deletion tests/scripts/test_grpe_flash_dixon.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Tests for 3D FLASH sequence with golden radial phase encoding."""

import numpy as np
import pytest
from mrseq.scripts.grpe_flash_dixon import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 17.84831 # defined 2025-11-09
EXPECTED_DUR = 18.86719 # defined 2026-03-17


def test_default_seq_duration(system_defaults):
Expand All @@ -13,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.16


def test_seq_creation_error_on_short_tr(system_defaults):
"""Test if error is raised on too short repetition time."""
with pytest.raises(ValueError):
Expand Down
10 changes: 10 additions & 0 deletions tests/scripts/test_radial_flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import numpy as np
import pytest
from mrseq.scripts.radial_flash import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 0.75043 # defined 2025-10-17

Expand All @@ -14,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_te(system_defaults):
"""Test if error is raised on too short echo time."""
with pytest.raises(ValueError):
Expand Down
10 changes: 10 additions & 0 deletions tests/scripts/test_spiral_flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import numpy as np
import pytest
from mrseq.scripts.spiral_flash import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 0.70588 # defined 2026-02-10

Expand All @@ -14,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_te(system_defaults):
"""Test if error is raised on too short echo time."""
with pytest.raises(ValueError):
Expand Down
11 changes: 11 additions & 0 deletions tests/scripts/test_t1_inv_rec_gre_single_line.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Tests for Gold standard GRE-based inversion recovery sequence."""

import numpy as np
import pytest
from mrseq.scripts.t1_inv_rec_gre_single_line import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 7168.000320 # defined 2025-02-03

Expand All @@ -13,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_te(system_defaults):
"""Test if error is raised on too short echo time."""
with pytest.raises(ValueError):
Expand Down
11 changes: 11 additions & 0 deletions tests/scripts/test_t1_inv_rec_se_single_line.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Tests for Gold standard SE-based inversion recovery sequence."""

import numpy as np
import pytest
from mrseq.scripts.t1_inv_rec_se_single_line import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 7168.000320 # defined 2025-02-03

Expand All @@ -13,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_te(system_defaults):
"""Test if error is raised on too short echo time."""
with pytest.raises(ValueError):
Expand Down
13 changes: 12 additions & 1 deletion tests/scripts/test_t1_molli_bssfp.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Tests for 2D Cartesian FLASH with T2-preparation pulses for T2 mapping."""

import numpy as np
import pytest
from mrseq.scripts.t1_molli_bssfp import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 10.95339 # defined 2025-11-24
EXPECTED_DUR = 11.00523 # defined 2026-02-10


def test_default_seq_duration(system_defaults):
Expand All @@ -13,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_te(system_defaults):
"""Test if error is raised on too short echo time."""
with pytest.raises(ValueError):
Expand Down
11 changes: 11 additions & 0 deletions tests/scripts/test_t1_t2_spiral_cmrf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Tests for cardiac MR Fingerprinting sequence with spiral readout."""

import numpy as np
import pytest
from mrseq.scripts.t1_t2_spiral_cmrf import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 14.55141 # defined 2026-02-10

Expand All @@ -13,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_tr(system_defaults):
"""Test if error is raised on too short repetition time."""
with pytest.raises(ValueError):
Expand Down
11 changes: 11 additions & 0 deletions tests/scripts/test_t1rho_se_single_line.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Tests for Gold standard SE-based T1rho mapping sequence."""

import numpy as np
import pytest
from mrseq.scripts.t1rho_se_single_line import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 3072.000000 # defined 2025-02-20

Expand All @@ -13,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_te(system_defaults):
"""Test if error is raised on too short echo time."""
with pytest.raises(ValueError):
Expand Down
10 changes: 10 additions & 0 deletions tests/scripts/test_t2_multi_echo_se_single_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import numpy as np
import pytest
from mrseq.scripts.t2_multi_echo_se_single_line import main as create_seq
from mrseq.utils.system_defaults import sys_a
from mrseq.utils.system_defaults import sys_b

EXPECTED_DUR = 5120.000970 # defined 2025-02-06

Expand All @@ -14,6 +16,14 @@ def test_default_seq_duration(system_defaults):
assert duration == pytest.approx(EXPECTED_DUR)


@pytest.mark.parametrize('system', [sys_a, sys_b])
def test_seq_duration(system):
"""Test system dependance of sequence."""
seq, _ = create_seq(system=system, show_plots=False)
duration = seq.duration()[0]
assert np.abs(duration - EXPECTED_DUR) / EXPECTED_DUR < 0.05


def test_seq_creation_error_on_short_te(system_defaults):
"""Test if error is raised on too short echo time."""
with pytest.raises(ValueError):
Expand Down
Loading
Loading