| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- import math
- from types import SimpleNamespace
- from typing import Tuple
- import numpy as np
- from scipy.optimize import minimize
- from seqgen.pypulseq.make_extended_trapezoid import make_extended_trapezoid
- from seqgen.pypulseq.opts import Opts
- def make_extended_trapezoid_area(
- area: float, channel: str, grad_end: float, grad_start: float, system: Opts
- ) -> Tuple[SimpleNamespace, np.array, np.array]:
- """
- Makes the shortest possible extended trapezoid with a given area which starts and ends (optionally) as non-zero
- gradient values.
- Parameters
- ----------
- channel : str
- Orientation of extended trapezoidal gradient event. Must be one of 'x', 'y' or 'z'.
- grad_start : float
- Starting non-zero gradient value.
- grad_end : float
- Ending non-zero gradient value.
- area : float
- Area of extended trapezoid.
- system: Opts
- System limits.
- Returns
- -------
- grad : SimpleNamespace
- Extended trapezoid event.
- times : numpy.ndarray
- amplitude : numpy.ndarray
- """
- SR = system.max_slew * 0.99
- Tp = 0
- obj1 = (
- lambda x: (
- area - __testGA(x, 0, SR, system.grad_raster_time, grad_start, grad_end)
- )
- ** 2
- )
- arr_res = [
- minimize(fun=obj1, x0=-system.max_grad, method="Nelder-Mead"),
- minimize(fun=obj1, x0=0, method="Nelder-Mead"),
- minimize(fun=obj1, x0=system.max_grad, method="Nelder-Mead"),
- ]
- arr_res = np.array([(*res.x, res.fun) for res in arr_res])
- Gp, obj1val = arr_res[:, 0], arr_res[:, 1]
- i_min = np.argmin(obj1val)
- Gp = Gp[i_min]
- obj1val = obj1val[i_min]
- if obj1val > 1e-3 or np.abs(Gp) > system.max_grad: # Search did not converge
- Gp = system.max_grad * np.sign(Gp)
- obj2 = (
- lambda x: (
- area
- - __testGA(Gp, x, SR, system.grad_raster_time, grad_start, grad_end)
- )
- ** 2
- )
- res2 = minimize(fun=obj2, x0=0, method="Nelder-Mead")
- T, obj2val = *res2.x, res2.fun
- assert obj2val < 1e-2
- Tp = np.ceil(T / system.grad_raster_time) * system.grad_raster_time
- # Fix the ramps
- Tru = (
- np.ceil(np.abs(Gp - grad_start) / SR / system.grad_raster_time)
- * system.grad_raster_time
- )
- Trd = (
- np.ceil(np.abs(Gp - grad_end) / SR / system.grad_raster_time)
- * system.grad_raster_time
- )
- obj3 = lambda x: (area - __testGA1(x, Tru, Tp, Trd, grad_start, grad_end)) ** 2
- res = minimize(fun=obj3, x0=Gp, method="Nelder-Mead")
- Gp, obj3val = *res.x, res.fun
- assert obj3val < 1e-3 # Did the final search converge?
- assert Tp >= 0
- if Tp > 0:
- times = np.cumsum([0, Tru, Tp, Trd])
- amplitudes = [grad_start, Gp, Gp, grad_end]
- else:
- Tru = (
- np.ceil(np.abs(Gp - grad_start) / SR / system.grad_raster_time)
- * system.grad_raster_time
- )
- Trd = (
- np.ceil(np.abs(Gp - grad_end) / SR / system.grad_raster_time)
- * system.grad_raster_time
- )
- if Trd > 0:
- if Tru > 0:
- times = np.cumsum([0, Tru, Trd])
- amplitudes = np.array([grad_start, Gp, grad_end])
- else:
- times = np.cumsum([0, Trd])
- amplitudes = np.array([grad_start, grad_end])
- else:
- times = np.cumsum([0, Tru])
- amplitudes = np.array([grad_start, grad_end])
- grad = make_extended_trapezoid(
- channel=channel, system=system, times=times, amplitudes=amplitudes
- )
- grad.area = __testGA1(Gp, Tru, Tp, Trd, grad_start, grad_end)
- assert np.abs(grad.area - area) < 1e-3
- return grad, times, amplitudes
- def __testGA(Gp, Tp, SR, dT, Gs, Ge):
- Tru = np.ceil(np.abs(Gp - Gs) / SR / dT) * dT
- Trd = np.ceil(np.abs(Gp - Ge) / SR / dT) * dT
- ga = __testGA1(Gp, Tru, Tp, Trd, Gs, Ge)
- return ga
- def __testGA1(Gp, Tru, Tp, Trd, Gs, Ge):
- return 0.5 * Tru * (Gp + Gs) + Gp * Tp + 0.5 * (Gp + Ge) * Trd
|