pulseqzero 0.1.0__py3-none-any.whl
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.
- pulseqzero/__init__.py +92 -0
- pulseqzero/adapter/__init__.py +47 -0
- pulseqzero/adapter/adc.py +37 -0
- pulseqzero/adapter/delay.py +22 -0
- pulseqzero/adapter/grads.py +222 -0
- pulseqzero/adapter/opts.py +98 -0
- pulseqzero/adapter/pulses.py +253 -0
- pulseqzero/adapter/seq_convert.py +237 -0
- pulseqzero/adapter/sequence.py +66 -0
- pulseqzero-0.1.0.dist-info/LICENSE +661 -0
- pulseqzero-0.1.0.dist-info/METADATA +298 -0
- pulseqzero-0.1.0.dist-info/RECORD +14 -0
- pulseqzero-0.1.0.dist-info/WHEEL +5 -0
- pulseqzero-0.1.0.dist-info/top_level.txt +1 -0
pulseqzero/__init__.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Impl:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.use_pypulseq()
|
|
7
|
+
|
|
8
|
+
def use_pypulseq(self):
|
|
9
|
+
import pypulseq as pp
|
|
10
|
+
self.calc_SAR = pp.calc_SAR
|
|
11
|
+
self.Sequence = pp.Sequence
|
|
12
|
+
self.add_gradients = pp.add_gradients
|
|
13
|
+
self.align = pp.align
|
|
14
|
+
self.calc_duration = pp.calc_duration
|
|
15
|
+
self.calc_ramp = pp.calc_ramp
|
|
16
|
+
self.calc_rf_bandwidth = pp.calc_rf_bandwidth
|
|
17
|
+
self.calc_rf_center = pp.calc_rf_center
|
|
18
|
+
self.make_adc = pp.make_adc
|
|
19
|
+
self.make_adiabatic_pulse = pp.make_adiabatic_pulse
|
|
20
|
+
self.make_arbitrary_grad = pp.make_arbitrary_grad
|
|
21
|
+
self.make_arbitrary_rf = pp.make_arbitrary_rf
|
|
22
|
+
self.make_block_pulse = pp.make_block_pulse
|
|
23
|
+
self.sigpy_n_seq = pp.sigpy_n_seq
|
|
24
|
+
self.make_slr = pp.make_slr
|
|
25
|
+
self.make_sms = pp.make_sms
|
|
26
|
+
self.make_delay = pp.make_delay
|
|
27
|
+
self.make_digital_output_pulse = pp.make_digital_output_pulse
|
|
28
|
+
self.make_extended_trapezoid = pp.make_extended_trapezoid
|
|
29
|
+
self.make_extended_trapezoid_area = pp.make_extended_trapezoid_area
|
|
30
|
+
self.make_gauss_pulse = pp.make_gauss_pulse
|
|
31
|
+
self.make_label = pp.make_label
|
|
32
|
+
self.make_sinc_pulse = pp.make_sinc_pulse
|
|
33
|
+
self.make_trapezoid = pp.make_trapezoid
|
|
34
|
+
self.SigpyPulseOpts = pp.SigpyPulseOpts
|
|
35
|
+
self.make_trigger = pp.make_trigger
|
|
36
|
+
self.Opts = pp.Opts
|
|
37
|
+
self.points_to_waveform = pp.points_to_waveform
|
|
38
|
+
self.rotate = pp.rotate
|
|
39
|
+
self.scale_grad = pp.scale_grad
|
|
40
|
+
self.split_gradient = pp.split_gradient
|
|
41
|
+
self.split_gradient_at = pp.split_gradient_at
|
|
42
|
+
self.get_supported_labels = pp.get_supported_labels
|
|
43
|
+
self.traj_to_grad = pp.traj_to_grad
|
|
44
|
+
|
|
45
|
+
def use_pulseqzero(self):
|
|
46
|
+
from . import adapter as ad
|
|
47
|
+
self.calc_SAR = ad.calc_SAR
|
|
48
|
+
self.Sequence = ad.Sequence
|
|
49
|
+
# self.add_gradients = ad.add_gradients
|
|
50
|
+
# self.align = ad.align
|
|
51
|
+
self.calc_duration = ad.calc_duration
|
|
52
|
+
# self.calc_ramp = ad.calc_ramp
|
|
53
|
+
self.calc_rf_bandwidth = ad.calc_rf_bandwidth
|
|
54
|
+
self.calc_rf_center = ad.calc_rf_center
|
|
55
|
+
self.make_adc = ad.make_adc
|
|
56
|
+
# self.make_adiabatic_pulse = ad.make_adiabatic_pulse
|
|
57
|
+
self.make_arbitrary_grad = ad.make_arbitrary_grad
|
|
58
|
+
self.make_arbitrary_rf = ad.make_arbitrary_rf
|
|
59
|
+
self.make_block_pulse = ad.make_block_pulse
|
|
60
|
+
# self.sigpy_n_seq = ad.sigpy_n_seq
|
|
61
|
+
# self.make_slr = ad.make_slr
|
|
62
|
+
# self.make_sms = ad.make_sms
|
|
63
|
+
self.make_delay = ad.make_delay
|
|
64
|
+
self.make_digital_output_pulse = ad.make_digital_output_pulse
|
|
65
|
+
self.make_extended_trapezoid = ad.make_extended_trapezoid
|
|
66
|
+
# self.make_extended_trapezoid_area = ad.make_extended_trapezoid_area
|
|
67
|
+
self.make_gauss_pulse = ad.make_gauss_pulse
|
|
68
|
+
self.make_label = ad.make_label
|
|
69
|
+
self.make_sinc_pulse = ad.make_sinc_pulse
|
|
70
|
+
self.make_trapezoid = ad.make_trapezoid
|
|
71
|
+
# self.SigpyPulseOpts = ad.SigpyPulseOpts
|
|
72
|
+
self.make_trigger = ad.make_trigger
|
|
73
|
+
self.Opts = ad.Opts
|
|
74
|
+
# self.points_to_waveform = ad.points_to_waveform
|
|
75
|
+
# self.rotate = ad.rotate
|
|
76
|
+
# self.scale_grad = ad.scale_grad
|
|
77
|
+
# self.split_gradient = ad.split_gradient
|
|
78
|
+
# self.split_gradient_at = ad.split_gradient_at
|
|
79
|
+
self.get_supported_labels = ad.get_supported_labels
|
|
80
|
+
# self.traj_to_grad = ad.traj_to_grad
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
pp_impl = Impl()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@contextmanager
|
|
87
|
+
def mr0_mode():
|
|
88
|
+
pp_impl.use_pulseqzero()
|
|
89
|
+
try:
|
|
90
|
+
yield
|
|
91
|
+
finally:
|
|
92
|
+
pp_impl.use_pypulseq()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
def calc_SAR(file):
|
|
2
|
+
pass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def make_label(label, type, value):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def calc_duration(*args):
|
|
10
|
+
# We assume that all events have a duration property
|
|
11
|
+
return max(
|
|
12
|
+
(event.duration for event in args if event is not None),
|
|
13
|
+
default=0.0
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def calc_rf_bandwidth(rf, cutoff=0.5, return_axis=False, return_spectrum=False):
|
|
18
|
+
import numpy as np
|
|
19
|
+
bw = 0
|
|
20
|
+
spectrum = np.zeros(1)
|
|
21
|
+
w = np.zeros(1)
|
|
22
|
+
|
|
23
|
+
if return_spectrum and not return_axis:
|
|
24
|
+
return bw, spectrum
|
|
25
|
+
if return_axis:
|
|
26
|
+
return bw, spectrum, w
|
|
27
|
+
return bw
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def calc_rf_center(rf):
|
|
31
|
+
return 0, 0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_supported_labels():
|
|
35
|
+
return (
|
|
36
|
+
"SLC", "SEG", "REP", "AVG", "SET", "ECO", "PHS", "LIN", "PAR", "NAV",
|
|
37
|
+
"REV", "SMS", "REF", "IMA", "NOISE", "PMC", "NOROT", "NOPOS", "NOSCL",
|
|
38
|
+
"ONCE", "TRID",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
from .opts import Opts
|
|
43
|
+
from .delay import make_delay, make_trigger, make_digital_output_pulse
|
|
44
|
+
from .adc import make_adc
|
|
45
|
+
from .grads import make_trapezoid, make_arbitrary_grad, make_extended_trapezoid
|
|
46
|
+
from .pulses import make_arbitrary_rf, make_block_pulse, make_gauss_pulse, make_sinc_pulse
|
|
47
|
+
from .sequence import Sequence
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from ..adapter import Opts
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def make_adc(
|
|
6
|
+
num_samples,
|
|
7
|
+
delay=0,
|
|
8
|
+
duration=None,
|
|
9
|
+
dwell=None,
|
|
10
|
+
freq_offset=0,
|
|
11
|
+
phase_offset=0,
|
|
12
|
+
system=None
|
|
13
|
+
):
|
|
14
|
+
if (dwell is None) == (duration is None):
|
|
15
|
+
raise ValueError("Either dwell or duration must be defined")
|
|
16
|
+
|
|
17
|
+
if dwell is None:
|
|
18
|
+
dwell = duration / num_samples
|
|
19
|
+
if system is None:
|
|
20
|
+
system = Opts.default
|
|
21
|
+
if delay < system.adc_dead_time:
|
|
22
|
+
delay = system.adc_dead_time
|
|
23
|
+
|
|
24
|
+
return Adc(num_samples, dwell, delay, freq_offset, phase_offset)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class Adc:
|
|
29
|
+
num_samples: ...
|
|
30
|
+
dwell: ...
|
|
31
|
+
delay: ...
|
|
32
|
+
freq_offset: ... # ignored by sim
|
|
33
|
+
phase_offset: ...
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def duration(self):
|
|
37
|
+
return self.delay + self.num_samples * self.dwell
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def make_delay(d):
|
|
5
|
+
return Delay(d)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def make_trigger(channel, delay=0, duration=0, system=None):
|
|
9
|
+
return Delay(delay)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def make_digital_output_pulse(channel, delay=0, duration=None, system=None):
|
|
13
|
+
return Delay(delay)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Delay:
|
|
18
|
+
delay: ...
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def duration(self):
|
|
22
|
+
return self.delay
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from copy import copy
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ..adapter import Opts, calc_duration
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def scale_grad(grad, scale):
|
|
8
|
+
grad = copy(grad)
|
|
9
|
+
if isinstance(grad, TrapGrad):
|
|
10
|
+
grad.amplitude *= scale
|
|
11
|
+
if isinstance(grad, FreeGrad):
|
|
12
|
+
grad.waveform *= scale
|
|
13
|
+
return grad
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def split_gradient(grad, system):
|
|
17
|
+
assert isinstance(grad, TrapGrad)
|
|
18
|
+
if system is None:
|
|
19
|
+
system = Opts.default
|
|
20
|
+
total_duration = calc_duration(grad)
|
|
21
|
+
|
|
22
|
+
ramp_up = make_extended_trapezoid(
|
|
23
|
+
channel=grad.channel,
|
|
24
|
+
amplitudes=np.array([0, grad.amplitude]),
|
|
25
|
+
times=np.ndarray([0, grad.rise_time])
|
|
26
|
+
)
|
|
27
|
+
flat_top = make_extended_trapezoid(
|
|
28
|
+
channel=grad.channel,
|
|
29
|
+
amplitudes=np.array([grad.amplitude, grad.amplitude]),
|
|
30
|
+
times=np.ndarray([grad.rise_time, grad.rise_time + grad.flat_time])
|
|
31
|
+
)
|
|
32
|
+
ramp_down = make_extended_trapezoid(
|
|
33
|
+
channel=grad.channel,
|
|
34
|
+
amplitudes=np.array([grad.amplitude, 0]),
|
|
35
|
+
times=np.ndarray([grad.rise_time + grad.flat_time, total_duration])
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return ramp_up, flat_top, ramp_down
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_trapezoid(
|
|
42
|
+
channel,
|
|
43
|
+
amplitude=None,
|
|
44
|
+
area=None,
|
|
45
|
+
delay=0,
|
|
46
|
+
duration=None,
|
|
47
|
+
fall_time=None,
|
|
48
|
+
flat_area=None,
|
|
49
|
+
flat_time=None,
|
|
50
|
+
max_grad=None,
|
|
51
|
+
max_slew=None,
|
|
52
|
+
rise_time=None,
|
|
53
|
+
system=None,
|
|
54
|
+
):
|
|
55
|
+
if system is None:
|
|
56
|
+
system = Opts.default
|
|
57
|
+
if max_grad is None:
|
|
58
|
+
max_grad = system.max_grad
|
|
59
|
+
if max_slew is None:
|
|
60
|
+
max_slew = system.max_slew
|
|
61
|
+
|
|
62
|
+
# new_amp is only calculated to set rise_time below, the actual
|
|
63
|
+
# amplitude is then calculated from tmp_amp and the timing
|
|
64
|
+
|
|
65
|
+
# TODO: This function should really be split into multiple with the different argument combination options
|
|
66
|
+
|
|
67
|
+
if flat_time is not None:
|
|
68
|
+
if amplitude is not None:
|
|
69
|
+
new_amp = amplitude
|
|
70
|
+
elif area is not None:
|
|
71
|
+
assert rise_time is not None
|
|
72
|
+
if fall_time is None:
|
|
73
|
+
fall_time = rise_time
|
|
74
|
+
new_amp = area / (rise_time / 2 + flat_time + fall_time / 2)
|
|
75
|
+
else:
|
|
76
|
+
assert flat_area is not None
|
|
77
|
+
new_amp = flat_area / flat_time
|
|
78
|
+
|
|
79
|
+
if rise_time is None:
|
|
80
|
+
rise_time = abs(new_amp) / max_slew
|
|
81
|
+
if fall_time is None:
|
|
82
|
+
fall_time = rise_time
|
|
83
|
+
|
|
84
|
+
elif duration is not None:
|
|
85
|
+
if amplitude is None:
|
|
86
|
+
assert area is not None
|
|
87
|
+
|
|
88
|
+
if rise_time is None:
|
|
89
|
+
_, rise_time, flat_time, fall_time = calc_params_for_area(area, max_slew, max_grad)
|
|
90
|
+
assert duration >= rise_time + flat_time + fall_time
|
|
91
|
+
|
|
92
|
+
dC = 1 / abs(2 * max_slew)
|
|
93
|
+
new_amp = (
|
|
94
|
+
duration - (duration**2 - 4 * abs(area) * dC)
|
|
95
|
+
) / (2 * dC)
|
|
96
|
+
else:
|
|
97
|
+
if fall_time is None:
|
|
98
|
+
fall_time = rise_time
|
|
99
|
+
new_amp = area / (duration - rise_time / 2 - fall_time / 2)
|
|
100
|
+
else:
|
|
101
|
+
new_amp = amplitude
|
|
102
|
+
|
|
103
|
+
if rise_time is None:
|
|
104
|
+
rise_time = abs(new_amp) / max_slew
|
|
105
|
+
if fall_time is None:
|
|
106
|
+
fall_time = rise_time
|
|
107
|
+
flat_time = duration - rise_time - fall_time
|
|
108
|
+
|
|
109
|
+
if amplitude is None:
|
|
110
|
+
new_amp = area / (rise_time / 2 + flat_time + fall_time / 2)
|
|
111
|
+
|
|
112
|
+
else:
|
|
113
|
+
assert area is not None
|
|
114
|
+
new_amp, rise_time, flat_time, fall_time = calc_params_for_area(area, max_slew, max_grad)
|
|
115
|
+
|
|
116
|
+
return TrapGrad(
|
|
117
|
+
channel,
|
|
118
|
+
new_amp,
|
|
119
|
+
rise_time,
|
|
120
|
+
flat_time,
|
|
121
|
+
fall_time,
|
|
122
|
+
delay
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def calc_params_for_area(area, max_slew, max_grad):
|
|
127
|
+
rise_time = (abs(area) / max_slew)**0.5
|
|
128
|
+
amplitude = area / rise_time
|
|
129
|
+
t_eff = rise_time
|
|
130
|
+
|
|
131
|
+
if abs(amplitude) > max_grad:
|
|
132
|
+
t_eff = abs(area) / max_grad
|
|
133
|
+
amplitude = area / t_eff
|
|
134
|
+
rise_time = abs(amplitude) / max_slew
|
|
135
|
+
|
|
136
|
+
flat_time = t_eff - rise_time
|
|
137
|
+
fall_time = rise_time
|
|
138
|
+
|
|
139
|
+
return amplitude, rise_time, flat_time, fall_time
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass
|
|
143
|
+
class TrapGrad:
|
|
144
|
+
channel: ...
|
|
145
|
+
amplitude: ...
|
|
146
|
+
rise_time: ...
|
|
147
|
+
flat_time: ...
|
|
148
|
+
fall_time: ...
|
|
149
|
+
delay: ...
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def area(self):
|
|
153
|
+
return self.amplitude * (self.rise_time / 2 + self.flat_time + self.fall_time / 2)
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def flat_area(self):
|
|
157
|
+
return self.amplitude * self.flat_time
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def duration(self):
|
|
161
|
+
return self.delay + self.rise_time + self.flat_time + self.fall_time
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def make_arbitrary_grad(
|
|
165
|
+
channel,
|
|
166
|
+
waveform,
|
|
167
|
+
delay=0,
|
|
168
|
+
max_grad=None,
|
|
169
|
+
max_slew=None,
|
|
170
|
+
system=None,
|
|
171
|
+
):
|
|
172
|
+
if system is None:
|
|
173
|
+
system = Opts.default
|
|
174
|
+
|
|
175
|
+
tt = (np.arange(len(waveform)) + 0.5) * system.grad_raster_time
|
|
176
|
+
|
|
177
|
+
return FreeGrad(
|
|
178
|
+
channel,
|
|
179
|
+
waveform,
|
|
180
|
+
delay,
|
|
181
|
+
tt,
|
|
182
|
+
len(waveform) * system.grad_raster_time
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@dataclass
|
|
187
|
+
class FreeGrad:
|
|
188
|
+
channel: ...
|
|
189
|
+
waveform: ...
|
|
190
|
+
delay: ...
|
|
191
|
+
tt: ...
|
|
192
|
+
shape_dur: ...
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def duration(self):
|
|
196
|
+
return self.delay + self.shape_dur
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def area(self):
|
|
200
|
+
return 0.5 * (
|
|
201
|
+
(self.tt[1:] - self.tt[:-1]) *
|
|
202
|
+
(self.waveform[1:] + self.waveform[:-1])
|
|
203
|
+
).sum()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def make_extended_trapezoid(
|
|
207
|
+
channel,
|
|
208
|
+
amplitudes=np.zeros(1),
|
|
209
|
+
convert_to_arbitrary=False,
|
|
210
|
+
max_grad=None,
|
|
211
|
+
max_slew=None,
|
|
212
|
+
skip_check=False,
|
|
213
|
+
system=None,
|
|
214
|
+
times=np.zeros(1),
|
|
215
|
+
):
|
|
216
|
+
return FreeGrad(
|
|
217
|
+
channel,
|
|
218
|
+
amplitudes,
|
|
219
|
+
times[0],
|
|
220
|
+
times - times[0],
|
|
221
|
+
times[-1]
|
|
222
|
+
)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
def convert(from_value, from_unit, gamma, to_unit=""):
|
|
2
|
+
from math import pi
|
|
3
|
+
|
|
4
|
+
value_SI = from_value * {
|
|
5
|
+
"Hz/m": 1,
|
|
6
|
+
"mT/m": 1e-3 * gamma,
|
|
7
|
+
"rad/ms/mm": 1e6 / (2 * pi),
|
|
8
|
+
"Hz/m/s": 1,
|
|
9
|
+
"mT/m/ms": gamma,
|
|
10
|
+
"T/m/s": gamma,
|
|
11
|
+
"rad/ms/mm/ms": 1e9 / (2 * pi),
|
|
12
|
+
}[from_unit]
|
|
13
|
+
|
|
14
|
+
return value_SI * {
|
|
15
|
+
"Hz/m": 1,
|
|
16
|
+
"mT/m": 1e3 / gamma,
|
|
17
|
+
"rad/ms/mm": 1e-6 * 2 * pi,
|
|
18
|
+
"Hz/m/s": 1,
|
|
19
|
+
"mT/m/ms": 1 / gamma,
|
|
20
|
+
"T/m/s": 1 / gamma,
|
|
21
|
+
"rad/ms/mm/ms": 1e-9 * 2 * pi
|
|
22
|
+
}[to_unit]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Opts:
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
adc_dead_time=None,
|
|
29
|
+
adc_raster_time=None,
|
|
30
|
+
block_duration_raster=None,
|
|
31
|
+
gamma=None,
|
|
32
|
+
grad_raster_time=None,
|
|
33
|
+
grad_unit="Hz/m",
|
|
34
|
+
max_grad=None,
|
|
35
|
+
max_slew=None,
|
|
36
|
+
rf_dead_time=None,
|
|
37
|
+
rf_raster_time=None,
|
|
38
|
+
rf_ringdown_time=None,
|
|
39
|
+
rise_time=None,
|
|
40
|
+
slew_unit="Hz/m/s",
|
|
41
|
+
B0=None,
|
|
42
|
+
):
|
|
43
|
+
def select(a, b):
|
|
44
|
+
if a is not None:
|
|
45
|
+
return a
|
|
46
|
+
else:
|
|
47
|
+
return getattr(self.default, b)
|
|
48
|
+
return a if a is not None else b
|
|
49
|
+
|
|
50
|
+
self.gamma = select(gamma, "gamma")
|
|
51
|
+
self.max_grad = select(convert(max_grad, grad_unit, self.gamma, "Hz/m"), "max_grad")
|
|
52
|
+
self.max_slew = select(convert(max_slew, slew_unit, self.gamma, "Hz/m/s"), "max_slew")
|
|
53
|
+
# Rise time seems to overwrite
|
|
54
|
+
if rise_time is not None:
|
|
55
|
+
self.max_slew = self.max_grad / rise_time
|
|
56
|
+
self.adc_dead_time = select(adc_dead_time, "adc_dead_time")
|
|
57
|
+
self.adc_raster_time = select(adc_raster_time, "adc_raster_time")
|
|
58
|
+
self.block_duration_raster = select(block_duration_raster, "block_duration_raster")
|
|
59
|
+
self.rf_dead_time = select(rf_dead_time, "rf_dead_time")
|
|
60
|
+
self.rf_raster_time = select(rf_raster_time, "rf_raster_time")
|
|
61
|
+
self.grad_raster_time = select(grad_raster_time, "grad_raster_time")
|
|
62
|
+
self.rf_ringdown_time = select(rf_ringdown_time, "rf_ringdown_time")
|
|
63
|
+
self.B0 = select(B0, "B0")
|
|
64
|
+
|
|
65
|
+
def set_as_default(self):
|
|
66
|
+
from copy import copy
|
|
67
|
+
Opts.default = copy(self)
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def reset_default(cls):
|
|
71
|
+
cls.default = Opts(
|
|
72
|
+
gamma=42.576e6,
|
|
73
|
+
max_grad=40,
|
|
74
|
+
grad_unit="mT/m",
|
|
75
|
+
max_slew=170,
|
|
76
|
+
slew_unit="T/m/s",
|
|
77
|
+
rf_dead_time=0,
|
|
78
|
+
rf_ringdown_time=0,
|
|
79
|
+
adc_dead_time=0,
|
|
80
|
+
adc_raster_time=100e-9,
|
|
81
|
+
rf_raster_time=1e-6,
|
|
82
|
+
grad_raster_time=10e-6,
|
|
83
|
+
block_duration_raster=10e-6,
|
|
84
|
+
B0=1.5
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def __str__(self) -> str:
|
|
88
|
+
"""
|
|
89
|
+
Print a string representation of the system limits objects.
|
|
90
|
+
"""
|
|
91
|
+
variables = vars(self)
|
|
92
|
+
s = [f"{key}: {value}" for key, value in variables.items()]
|
|
93
|
+
s = "\n".join(s)
|
|
94
|
+
s = "System limits:\n" + s
|
|
95
|
+
return s
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
Opts.reset_default()
|