GeneralBlochOperator#

class GeneralBlochOperator(name, gamma, time_grid, gradient_waveforms=None, rf_waveforms=None, adc_events=None, submodules=(), device=None)[source]#

General bloch-simulation module: accepts RF-Waveforms, Gradient-Waveforms and ADC-events as input and integrates the Bloch-equation (trapezoidal numerical integration). The specified time-grid does not need to be uniform, but must match the definition of the specified waveforms/adc definitions. The required input format matches the common format of a sequence diagram

The integration allows for moving isochromates, as well as the addition of effects defined in the submodules. For more information on how the isochromate motion is incorporated refer to the __call__ method.

Example usage
Instantiation#
# time_raster.shape, grad_grid.shape, rf_grid.shape, adc_grid.grid
# >>> (n_reps, t), (n_reps, t, 2), (n_reps, t), (n_reps, t, 2)
gammarad = system_specs.gamma_rad.m_as("rad/mT/ms")
bloch_mod = cmrsim.bloch.GeneralBlochOperator(name="example",
                                               device="GPU:0,
                                               gamma=gammarad,
                                               time_grid=time_raster,
                                               gradient_waveforms=grad_grid,
                                               rf_waveforms=rf_grid,
                                               adc_events=adc_grid)
Parallel call#
properties = ...  # From input definition
n_samples = tf.shape(module.gradient_waveforms)[1]
n_repetitions = tf.shape(module.gradient_waveforms)[0]
data_iterator = tf.data.Dataset.from_tensor_slices(properties)
for phantom_batch in tqdm(data_iterator.batch(int(3e5))):
    m, r = bloch_mod(trajectory_module=trajectory_module,
                     run_parallel=True, **phantom_batch)
Sequential call#
properties = ...  # From input definition
n_samples = tf.shape(module.gradient_waveforms)[1]
n_repetitions = tf.shape(module.gradient_waveforms)[0]

data_iterator = tf.data.Dataset.from_tensor_slices(properties)
for phantom_batch in tqdm(data_iterator.batch(int(3e5))):
    r = phantom_batch.pop("initial_position")
    m = phantom_batch.pop("magnetization")
    for rep_idx in tqdm(range(n_repetitions)):
        m, r = module(trajectory_module=trajectory_module, initial_position=r,
                      magnetization=m, repetition_index=rep_idx, run_parallel=False,
                      **phantom_batch)
Parameters:
  • name (str) – Verbose name of the module (non-functionally relevant)

  • gamma (float) – gyromagnetic ratio in rad/ms/m

  • time_grid (Tensor) – (n_steps) - tf.float32 - time definition for numerical integration steps

  • gradient_waveforms (Tensor) – (#repetitions, #steps, 3) - tf.float32 - Definition of gradients in mT/m

  • rf_waveforms (Tensor) – (#repetitions, #steps) - tf.complex64 -

  • adc_events (Tensor) – (#repetition, #steps, 2) - tf.float32 - last axis contains the adc-on definitions and the adc-phase

  • submodules (Tuple[BaseSubmodule, ...]) – List[tf.Modules] implementing the __call__ function returning the resulting phase increment contribution

  • device (str) – str

Methods:

__call__(initial_position, magnetization, ...)

Runs bloch simulation for the specified RF/Gradients and accumulates the signal for the times specified in adc_events.

reset()

Resets the time-signal accumulators, i.e. sets all entries to 0.

Attributes:

adc_events

Sampling events definitions for each time in self.time_grid for R repetitions - shape (R, T, 2), where the two channels have the following meaning: 1.

dt_grid

Grid containing the differences of specified time_grid - shape: (T-1, )

gamma

Gyromagnetic ration in rad/ms/m specified on instantiation

gradient_waveforms

Gradient waveforms at times of self.time_grid in mT/m for R repetitions - shape: (R, T, 3)

n_repetitions

Number of repetitions derived from specified waveforms

rf_waveforms

Complex radio-frequency pulse definition at times of self.time_grid in uT for R repetitions - shape: (R, T)

time_grid

(Non-uniform) temporal raster defining the simulation steps in milliseconds - shape: (T, )

time_signal_acc

Accumulator variables for the signal acquired at specified adc-events.

__call__(initial_position, magnetization, T1, T2, M0, trajectory_module=<cmrsim.trajectory._base.StaticParticlesTrajectory object>, repetition_index=0, run_parallel=False, **kwargs)[source]#

Runs bloch simulation for the specified RF/Gradients and accumulates the signal for the times specified in adc_events. For each interval on the time grid, a trapezoidal integration of the flip angle and the gradient phase increment (including the submodules) is performed and subsequently used to rotate the magnetization vectors.

Parameters:
  • initial_position (Tensor) – (#batch, 3) - tf.float32 - initial positions of particles

  • magnetization (Tensor) – ([repetitions/1], #batch, 3) - tf.complex64 - [Mx+iMy, Mx-iMy, Mz] magnetization per particle. The first optional axis is only used if run_parallel==True.

  • T1 (Tensor) – (#batch, ) - tf.float32 - Longitudinal relaxation time in milliseconds per particle

  • T2 (Tensor) – (#batch, ) - tf.float32 - Transversal relaxtion time in milliseconds per particle

  • M0 (Tensor) – (#batch, ) - tf.float32 - Equilibrium magnetization of each particle (representing a volume)

  • trajectory_module (Module) – tf.function accepting the arguments [r[:, 3](tf.float32), dt[,](tf.float32)] and returning the updated position r_new[:, 3](tf.float32) as well as a dictionary of tensors denoting arbitrary look-up values for the new particle positions. Defaults to static particles

  • repetition_index (Tensor) – (, ) - tf.int32 - index of the current repetition index to simulate this corresponds to indexing the first dimension of the specified waveforms. Only used if run_parallel==False

  • run_parallel (bool) – bool - defaults to False. If True, the repetitions of the specified waveforms are simulated for the same particle trajectories. Otherwise only the waveforms for given repetition_index is simulated.

  • kwargs – additional properties of particles, that are required for evaluating the submodules

Returns:

m, r with shapes - [#reps, #particles, 3] if repetitions are run in parallel - [#particles, 3] if run sequential

reset()[source]#

Resets the time-signal accumulators, i.e. sets all entries to 0

adc_events: Variable = None#

Sampling events definitions for each time in self.time_grid for R repetitions - shape (R, T, 2), where the two channels have the following meaning: 1. ADC On/Off [1/0] at time t and 2. Demodulation phase of the ADC at time t in radians

dt_grid: Variable#

Grid containing the differences of specified time_grid - shape: (T-1, )

gamma: float#

Gyromagnetic ration in rad/ms/m specified on instantiation

gradient_waveforms: Variable = None#

Gradient waveforms at times of self.time_grid in mT/m for R repetitions - shape: (R, T, 3)

n_repetitions: int#

Number of repetitions derived from specified waveforms

rf_waveforms: Variable = None#

Complex radio-frequency pulse definition at times of self.time_grid in uT for R repetitions - shape: (R, T)

time_grid: Variable = None#

(Non-uniform) temporal raster defining the simulation steps in milliseconds - shape: (T, )

time_signal_acc: List[Variable] = None#

Accumulator variables for the signal acquired at specified adc-events. This is empty if no ADC==On events where specified. The length of the List is equal to the number of repetitions and the shape of each tf.Variable is (sum(ADC[R]==1), ), which can result different length if the repetitions have different numbers of samples.