def gradient_waveforms(self): duration, num_blocks, _ = self.duration() wave_length = math.ceil(duration / self.grad_raster_time) grad_channels = 3 grad_waveforms = np.zeros((grad_channels, wave_length)) grad_channels = ['gx', 'gy', 'gz'] t0 = 0 t0_n = 0 for i in range(num_blocks): block = self.get_block(i + 1) for j in range(len(grad_channels)): if hasattr(block, grad_channels[j]): grad = getattr(block, grad_channels[j]) if grad.type == 'grad': nt_start = round( (grad.delay + grad.t[0]) / self.grad_raster_time) waveform = grad.waveform else: nt_start = round(grad.delay / self.grad_raster_time) if abs(grad.flat_time) > np.finfo(float).eps: t = np.cumsum([ 0, grad.rise_time, grad.flat_time, grad.fall_time ]) trap_form = np.multiply([0, 1, 1, 0], grad.amplitude) else: t = np.cumsum([0, grad.rise_time, grad.fall_time]) trap_form = np.multiply([0, 1, 0], grad.amplitude) tn = math.floor(t[-1] / self.grad_raster_time) t = np.append(t, t[-1] + self.grad_raster_time) trap_form = np.append(trap_form, 0) if abs(grad.amplitude) > np.finfo(float).eps: waveform = points_to_waveform( t, trap_form, self.grad_raster_time) else: waveform = np.zeros(tn + 1) if waveform.size != np.sum(np.isfinite(waveform)): raise Warning( 'Not all elements of the generated waveform are finite' ) grad_waveforms[ j, int(t0_n + nt_start):int(t0_n + nt_start + max(waveform.shape))] = waveform t0 += calc_duration(block) t0_n = round(t0 / self.grad_raster_time) return grad_waveforms
def make_extended_trapezoid(channel: str, times: np.ndarray = np.zeros(1), amplitudes: np.ndarray = np.zeros(1), system: Opts = Opts(), max_grad: float = 0, max_slew: float = 0, skip_check: bool = False): """ Creates an extend trapezoidal gradient event by defined by amplitude values in `amplitudes` at time indices in `times`. Parameters ---------- channel : str Orientation of extended trapezoidal gradient event. Must be one of x, y or z. times : numpy.ndarray, optional Time points at which `amplitudes` defines amplitude values. Default is 0. amplitudes : numpy.ndarray, optional Values defined at `times` time indices. Default is 0. system : Opts, optional System limits. Default is a system limits object initialised to default values. max_grad : float, optional Maximum gradient strength. Default is 0. max_slew : float, optional Maximum slew rate. Default is 0. skip_check : bool, optional Perform check. Default is false. Returns ------- grad : SimpleNamespace Extended trapezoid gradient event. """ if channel not in ['x', 'y', 'z']: raise ValueError() if not np.any(times): raise ValueError('At least one of the given times must be non-zero') if np.any(np.diff(times) <= 0): raise ValueError('Times must be in ascending order and all times must be distinct') if not np.any(amplitudes): raise ValueError('At least one of the given amplitudes must be non-zero') if skip_check is False and times[0] > 0 and amplitudes[0] != 0: raise ValueError('If first amplitude of a gradient is non-zero, it must connect to previous block') if max_grad <= 0: max_grad = system.max_grad if max_slew <= 0: max_slew = system.max_slew waveform = points_to_waveform(times=times, amplitudes=amplitudes, grad_raster_time=system.grad_raster_time) grad = make_arbitrary_grad(channel=channel, waveform=waveform, system=system, max_grad=max_grad, max_slew=max_slew, delay=times[0]) grad.first = amplitudes[0] grad.last = amplitudes[-1] return grad
def make_extended_trapezoid(channel, times=0, amplitudes=0, system=Opts(), max_grad=0, max_slew=0, skip_check=False): if channel not in ['x', 'y', 'z']: raise ValueError() if not np.any(times): raise ValueError('At least one of the given times must be non-zero') if np.any(np.diff(times) <= 0): raise ValueError( 'Times must be in ascending order and all times mut be distinct') if not np.any(amplitudes): raise ValueError( 'At least one of the given amplitudes must be non-zero') if skip_check is False and times[0] > 0 and amplitudes[0] != 0: raise ValueError( 'If first amplitude of a gradient is non-zero, it must connect to previous block' ) if max_grad <= 0: max_grad = system.max_grad if max_slew <= 0: max_slew = system.max_slew waveform = points_to_waveform(times=times, amplitudes=amplitudes, grad_raster_time=system.grad_raster_time) grad = make_arbitrary_grad(channel=channel, waveform=waveform, system=system, max_grad=max_grad, max_slew=max_slew, delay=times[0]) grad.first = amplitudes[0] grad.last = amplitudes[-1] return grad
def gradient_waveforms(self) -> np.ndarray: """ Decompress the entire gradient waveform. Returns an array of shape `gradient_axesxtimepoints`. `gradient_axes` is typically 3. Returns ------- grad_waveforms : numpy.ndarray Decompressed gradient waveform. """ duration, num_blocks, _ = self.duration() wave_length = math.ceil(duration / self.grad_raster_time) grad_channels = 3 grad_waveforms = np.zeros((grad_channels, wave_length)) grad_channels = ['gx', 'gy', 'gz'] t0 = 0 t0_n = 0 for i in range(num_blocks): block = self.get_block(i + 1) for j in range(len(grad_channels)): if hasattr(block, grad_channels[j]): grad = getattr(block, grad_channels[j]) if grad.type == 'grad': nt_start = round( (grad.delay + grad.t[0]) / self.grad_raster_time) waveform = grad.waveform else: nt_start = round(grad.delay / self.grad_raster_time) if abs(grad.flat_time) > np.finfo(float).eps: t = np.cumsum([ 0, grad.rise_time, grad.flat_time, grad.fall_time ]) trap_form = np.multiply([0, 1, 1, 0], grad.amplitude) else: t = np.cumsum([0, grad.rise_time, grad.fall_time]) trap_form = np.multiply([0, 1, 0], grad.amplitude) tn = math.floor(t[-1] / self.grad_raster_time) t = np.append(t, t[-1] + self.grad_raster_time) trap_form = np.append(trap_form, 0) if abs(grad.amplitude) > np.finfo(float).eps: waveform = points_to_waveform( t, trap_form, self.grad_raster_time) else: waveform = np.zeros(tn + 1) if waveform.size != np.sum(np.isfinite(waveform)): raise Warning( 'Not all elements of the generated waveform are finite' ) """ Matlab dynamically resizes arrays during slice assignment operation if assignment is out of bounds Numpy does not Following lines are a workaround """ l1, l2 = int(t0_n + nt_start), int(t0_n + nt_start + max(waveform.shape)) if l2 > grad_waveforms.shape[1]: grad_waveforms.resize((len(grad_channels), l2)) grad_waveforms[j, l1:l2] = waveform t0 += calc_duration(block) t0_n = round(t0 / self.grad_raster_time) return grad_waveforms
def add_gradients(grads: Union[list, tuple], system=Opts(), max_grad: int = 0, max_slew: int = 0) -> SimpleNamespace: """ Superpose several gradient events. Parameters ---------- grads : list or tuple List or tuple of 'SimpleNamespace' gradient events. system : Opts, optional, default=Opts() System limits. max_grad : float, optional, default=0 Maximum gradient amplitude. max_slew : float, optional, default=0 Maximum slew rate. Returns ------- grad : SimpleNamespace Superimposition of gradient events from `grads`. """ max_grad = max_grad if max_grad > 0 else system.max_grad max_slew = max_slew if max_slew > 0 else system.max_slew if len(grads) < 2: raise Exception() # First gradient defines channel channel = grads[0].channel # Find out the general delay of all gradients and other statistics delays, firsts, lasts, durs = [], [], [], [] for ii in range(len(grads)): delays.append(grads[ii].delay) firsts.append(grads[ii].first) lasts.append(grads[ii].last) durs.append(calc_duration(grads[ii])) # Convert to numpy.ndarray for fancy-indexing later on firsts, lasts = np.array(firsts), np.array(lasts) common_delay = min(delays) total_duration = max(durs) waveforms = dict() max_length = 0 for ii in range(len(grads)): g = grads[ii] if g.type == 'grad': waveforms[ii] = g.waveform elif g.type == 'trap': if g.flat_time > 0: # Triangle or trapezoid times = [ g.delay - common_delay, g.delay - common_delay + g.rise_time, g.delay - common_delay + g.rise_time + g.flat_time, g.delay - common_delay + g.rise_time + g.flat_time + g.fall_time ] amplitudes = [0, g.amplitude, g.amplitude, 0] else: times = [ g.delay - common_delay, g.delay - common_delay + g.rise_time, g.delay - common_delay + g.rise_time + g.flat_time ] amplitudes = [0, g.amplitude, 0] waveforms[ii] = points_to_waveform( times=times, amplitudes=amplitudes, grad_raster_time=system.grad_raster_time) else: raise ValueError('Unknown gradient type') if g.delay - common_delay > 0: # Stop for numpy.arange is not g.delay - common_delay - system.grad_raster_time like in Matlab # so as to include the endpoint t_delay = np.arange(0, g.delay - common_delay, step=system.grad_raster_time) waveforms[ii] = np.insert(waveforms[ii], 0, t_delay) num_points = len(waveforms[ii]) max_length = num_points if num_points > max_length else max_length w = np.zeros(max_length) for ii in range(len(grads)): wt = np.zeros(max_length) wt[0:len(waveforms[ii])] = waveforms[ii] w += wt grad = make_arbitrary_grad(channel, w, system, max_slew=max_slew, max_grad=max_grad, delay=common_delay) grad.first = np.sum(firsts[np.array(delays) == common_delay]) grad.last = np.sum(lasts[np.where(durs == total_duration)]) return grad
def add_gradients(grads, system=Opts(), max_grad=0, max_slew=0): max_grad = max_grad if max_grad > 0 else system.max_grad max_slew = max_slew if max_slew > 0 else system.max_slew if len(grads) < 2: raise Exception() # First gradient defines channel channel = grads[0].channel # Find out the general delay of all gradients and other statistics delays, firsts, lasts, durs = [], [], [], [] for ii in range(len(grads)): delays.append(grads[ii].delay) firsts.append(grads[ii].first) lasts.append(grads[ii].last) durs.append(calc_duration(grads[ii])) common_delay = min(delays) total_duration = max(durs) waveforms = dict() max_length = 0 for ii in range(len(grads)): g = grads[ii] if g.type == 'grad': waveforms[ii] = g.waveform elif g.type == 'trap': if g.flat_time > 0: # Triangle or trapezoid times = [ g.delay - common_delay, g.delay - common_delay + g.rise_time, g.delay - common_delay + g.rise_time + g.flat_time, g.delay - common_delay + g.rise_time + g.flat_time + g.fall_time ] amplitudes = [0, g.amplitude, g.amplitude, 0] else: times = [ g.delay - common_delay, g.delay - common_delay + g.rise_time, g.delay - common_delay + g.rise_time + g.flat_time ] amplitudes = [0, g.amplitude, 0] waveforms[ii] = points_to_waveform( times=times, amplitudes=amplitudes, grad_raster_time=system.grad_raster_time) else: raise ValueError('Unknown gradient type') if g.delay - common_delay > 0: t_delay = list( range(0, g.delay - common_delay - system.grad_raster_time, system.grad_raster_time)) waveforms[ii] = waveforms[ii].insert(0, t_delay) num_points = len(waveforms[ii]) max_length = num_points if num_points > max_length else max_length w = np.zeros((max_length, 1)) for ii in range(len(grads)): wt = np.zeros((max_length, 1)) wt[0:len(waveforms[ii])] = waveforms[ii] w += wt grad = make_arbitrary_grad(channel, w, system, max_slew=max_slew, max_grad=max_grad, delay=common_delay) grad.first = np.sum(firsts[np.where(delays == common_delay)]) grad.last = np.sum(lasts[np.where(durs == total_duration)]) return grad
def make_extended_trapezoid(channel: str, amplitudes: Iterable = np.zeros(1), max_grad: float = 0, max_slew: float = 0, system: Opts = Opts(), skip_check: bool = False, times: Iterable = np.zeros(1)) -> SimpleNamespace: """ Creates an extend trapezoidal gradient event by defined by amplitude values in `amplitudes` at time indices in `times`. Parameters ---------- channel : str Orientation of extended trapezoidal gradient event. Must be one of 'x', 'y' or 'z'. amplitudes : numpy.ndarray, optional, default=09 Values defined at `times` time indices. max_grad : float, optional, default=0 Maximum gradient strength. max_slew : float, optional, default=0 Maximum slew rate. system : Opts, optional, default=Opts() System limits. skip_check : bool, optional, default=False Perform check. times : numpy.ndarray, optional, default=np.zeros(1) Time points at which `amplitudes` defines amplitude values. Returns ------- grad : SimpleNamespace Extended trapezoid gradient event. Raises ------ ValueError If invalid `channel` is passed. Must be one of 'x', 'y' or 'z'. If all elements in `times` are zero. If elements in `times` are not in ascending order or not distinct. If all elements in `amplitudes` are zero. If first amplitude of a gradient is non-ero and does not connect to a previous block. """ if channel not in ['x', 'y', 'z']: raise ValueError(f"Invalid channel. Must be one of 'x', 'y' or 'z'. Passed: {channel}") if not np.any(times): raise ValueError('At least one of the given times must be non-zero') if np.any(np.diff(times) <= 0): raise ValueError('Times must be in ascending order and all times must be distinct') if not np.any(amplitudes): raise ValueError('At least one of the given amplitudes must be non-zero') if skip_check is False and times[0] > 0 and amplitudes[0] != 0: raise ValueError('If first amplitude of a gradient is non-zero, it must connect to previous block') if max_grad <= 0: max_grad = system.max_grad if max_slew <= 0: max_slew = system.max_slew waveform = points_to_waveform(times=times, amplitudes=amplitudes, grad_raster_time=system.grad_raster_time) grad = make_arbitrary_grad(channel=channel, waveform=waveform, system=system, max_grad=max_grad, max_slew=max_slew, delay=times[0]) grad.first = amplitudes[0] grad.last = amplitudes[-1] return grad