Skip to content

Note

Click here to download the full example code

Time-reverse simulation for phased array

The skull adds aberrations to the beam propagation; phased arrays can compensate for those by having different delays for each element, but estimating these delays can be challenging. One method to estimate the delays is a "time reverse" simulation as described in this Article. This notebook demonstrates the "time reverse" method to estimate the delays. The notebook sets up a scenario with a phased array source and a target and then runs a simulation with the source and target reversed to calculate the delays. Finally, it uses the calculated delays to perform a forward-time simulation.

Note

In this notebook, we refer to the "true" target as the eventual brain region we would like to stimulate, and the "true" source as the placement of the ultrasound probes. We refer to the "reversed" or "simulated" target and point-source as the values defined in our simulation, which are reversed from the physical setup to help calculate values.

import matplotlib.pyplot as plt
import numpy as np

import neurotechdevkit as ndk

# Parameters
NUM_ELEMENTS = 20
ELEMENT_WIDTH = 1.2e-3

Helper function to make the scenario with a PhasedArraySource

def make_scenario(element_delays=None):
    true_scenario = ndk.scenarios.built_in.Scenario2_2D()

    # define a phased-array source
    default_source = true_scenario.sources[0]
    true_source = ndk.sources.PhasedArraySource2D(
        element_delays=element_delays,
        position=default_source.position,
        direction=default_source.unit_direction,
        num_elements=NUM_ELEMENTS,
        pitch=default_source.aperture / NUM_ELEMENTS,
        element_width=ELEMENT_WIDTH,
        num_points=1000,
    )

    true_scenario.sources = [true_source]
    return true_scenario

Set up and visualize the forward scenario

true_scenario = make_scenario()
true_scenario.make_grid()
true_scenario.compile_problem()
true_scenario.render_layout()

Scenario Layout

Simulate the time-reverse scenario

Place a point source at the true target, and simulate a pulse. The point source is visualized as a gray dot.

# Reinitialize the scenario
reversed_scenario = ndk.scenarios.built_in.Scenario2_2D()
# and reverse the source
point_source = ndk.sources.PointSource2D(
    position=true_scenario.target.center,
)
reversed_scenario.sources.append(point_source)

reversed_scenario.make_grid()
reversed_scenario.compile_problem()
reversed_scenario.render_layout()

Scenario Layout

result = reversed_scenario.simulate_pulse()
assert isinstance(result, ndk.results.PulsedResult2D)
result.render_pulsed_simulation_animation()

Out:

Estimated time to complete simulation: 2 minutes. Memory required is 9.898794145189598 GB (available 73.624408064 GB). These values are approximated.
/home/circleci/.cache/pypoetry/virtualenvs/neurotechdevkit-3aSsmiER-py3.10/lib/python3.10/site-packages/devito/finite_differences/differentiable.py:224: DeprecationWarning: NotImplemented should not be used in a boolean context
  return super(Differentiable, self).__eq__(other) and\
/home/circleci/.cache/pypoetry/virtualenvs/neurotechdevkit-3aSsmiER-py3.10/lib/python3.10/site-packages/devito/finite_differences/differentiable.py:224: DeprecationWarning: NotImplemented should not be used in a boolean context
  return super(Differentiable, self).__eq__(other) and\
gcc -O3 -g -fPIC -Wall -std=c99 -march=native -Wno-unused-result -Wno-unused-variable -Wno-unused-but-set-variable -ffast-math -shared -fopenmp /tmp/devito-jitcache-uid1001/bc8fb1f60cd9db4d35f8beda3d6ca87c83ea57bd.c -lm -o /tmp/devito-jitcache-uid1001/bc8fb1f60cd9db4d35f8beda3d6ca87c83ea57bd.so
/home/circleci/project/src/neurotechdevkit/rendering/_animations.py:118: UserWarning: You passed in an explicit save_count=563 which is being ignored in favor of frames=563.
  anim = FuncAnimation(