Simulations

Simulating and Visualizing Traveling Waves with WaveSpace

This tutorial demonstrates how to use the WaveSpace toolbox to simulate different spatiotemporal wave patterns, add noise, and visualize the results.

Setup

Before running the examples, ensure that the project root is on the Python path:

import sys
import os

# Add project root to Python path (only necessary if not installed as a package)
path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, path)
print(path)

from WaveSpace.Simulation import SimulationFuns
from WaveSpace.PlottingHelpers import Plotting
from WaveSpace.Utils import HelperFuns as hf

import numpy as np
import matplotlib.pyplot as plt

Simulation Conditions

We will generate several types of waves:

  • Plane waves

  • Target waves (inward/outward)

  • Rotating waves (spirals)

  • Local oscillations (random vs. synchronized)

Conditions = [
  "PlaneWave_45", "PlaneWave_135",
  "TargetWave_in", "TargetWave_out",
  "RotatingWave_CW", "RotatingWave_CCW",
  "LocalOscillationRandom", "LocalOscillationSynched"
]

Plane Wave Simulation

We start with unidirectional plane waves mixed with spatial noise.

Type = "PlaneWave"
nTrials = 4
MatrixSize = 20
SampleRate = 500
SimDuration = 1.6

SpatialFrequency = [0.6, 0.6, 0.6, 0.6]
TemporalFrequency = [10, 10, 10, 10]
WaveDirection = [45, 45, 135, 135]
SimLayout = "grid"

WaveOnset = [300, 300, 300, 300]
WaveDuration = 1000

planeWave = SimulationFuns.simulate_signal(
    Type, nTrials, MatrixSize, SampleRate, SimDuration,
    SimLayout=SimLayout,
    TemporalFrequency=TemporalFrequency,
    SpatialFrequency=SpatialFrequency,
    WaveDirection=WaveDirection,
    WaveOnset=WaveOnset,
    WaveDuration=WaveDuration
)

planeWaveNoise = SimulationFuns.simulate_signal(
    Type="SpatialPinkNoise",
    ntrials=nTrials,
    MatrixSize=MatrixSize,
    SampleRate=SampleRate,
    SimLayout=SimLayout,
    SimDuration=SimDuration
)

SNR = 0.8
planeWaveData = SimulationFuns.SNRMix(planeWave, planeWaveNoise, SNR, SimLayout="grid")

Target Wave Simulation

Type = "TargetWave"
CenterX, CenterY = 2, 2

SpatialFrequency = [0.6, 0.6, 0.6, 0.6]
TemporalFrequency = [10, 10, 10, 10]
WaveDirection = [-1, -1, 1, 1]  # -1 = inward, 1 = outward

WaveOnset = 300
WaveDuration = 1000

targetWave = SimulationFuns.simulate_signal(
    Type, nTrials, MatrixSize, SampleRate, SimDuration,
    SimLayout="grid",
    TemporalFrequency=TemporalFrequency,
    SpatialFrequency=SpatialFrequency,
    WaveDirection=WaveDirection,
    WaveOnset=WaveOnset,
    WaveDuration=WaveDuration,
    CenterX=CenterX,
    CenterY=CenterY
)

targetNoise = SimulationFuns.simulate_signal("SpatialPinkNoise", nTrials, MatrixSize, SampleRate, SimDuration, SimLayout="grid")
targetWaveData = SimulationFuns.SNRMix(targetWave, targetNoise, 0.8, SimLayout="grid")

Rotating Wave Simulation

Type = "RotatingWave"
WaveDirection = [1, 1, -1, -1]  # 1 = CW, -1 = CCW

spiralWave = SimulationFuns.simulate_signal(
    Type, nTrials, MatrixSize, SampleRate, SimDuration,
    SimLayout="grid",
    TemporalFrequency=TemporalFrequency,
    SpatialFrequency=SpatialFrequency,
    WaveDirection=WaveDirection,
    WaveOnset=WaveOnset,
    WaveDuration=WaveDuration,
    CenterX=CenterX,
    CenterY=CenterY
)

spiralNoise = SimulationFuns.simulate_signal("SpatialPinkNoise", nTrials, MatrixSize, SampleRate, SimDuration, SimLayout="grid")
spiralWaveData = SimulationFuns.SNRMix(spiralWave, spiralNoise, 0.8, SimLayout="grid")

Local Oscillation Simulation

Type = "LocalOscillation"

localOscillators = SimulationFuns.simulate_signal(
    Type=Type, ntrials=nTrials,
    MatrixSize=MatrixSize, SampleRate=SampleRate, SimDuration=SimDuration,
    SimLayout="grid",
    WaveOnset=WaveOnset,
    WaveDuration=WaveDuration,
    OscillatoryPhase="Random",
    TemporalFrequency=TemporalFrequency,
    OscillatorProportion=0.4
)

oscillatorNoise = SimulationFuns.simulate_signal("SpatialPinkNoise", nTrials, MatrixSize, SampleRate, SimDuration, SimLayout="grid")
oscillatorWaveData = SimulationFuns.SNRMix(localOscillators, oscillatorNoise, 0.8, SimLayout="grid")

Combining Data

We now combine all simulated data into one dataset.

simCondList = []
for item in Conditions:
    simCondList.append(item)
    simCondList.append(item)

waveData = SimulationFuns.combine_SimData(
    [planeWaveData, targetWaveData, spiralWaveData, oscillatorWaveData],
    dimension='trl',
    SimCondList=simCondList
)

output_path = "ExampleData/Output"
if not os.path.exists(output_path):
    os.makedirs(output_path)
waveData.save_to_file(os.path.join(output_path, "SimulatedData"))

Visualization

We can animate the simulated wave activity across the grid.

for trl in range(waveData.get_data("SimulatedData").shape[0]):
    ani = Plotting.animate_grid_data(
        waveData,
        DataBucketName="SimulatedData",
        dataInd=trl,
        probepositions=[(0,15), (5,15), (10,15), (15,15), (19,15), (19,15)]
    )
    plot_file = output_path + f"/SimulationAnimation_{trl}.gif"
    ani.save(plot_file)

Next Steps

  • Explore other wave types such as StationaryPulse or WhiteNoise.

  • Adjust SNR, frequencies, and onsets to simulate different experimental conditions.

  • Use plotting helpers to create static or interactive visualizations of your simulated data.