Skip to article frontmatterSkip to article content
# Prepare Python environment
import numpy as np
import plotly.express as px
import os
import nibabel as nib
import numpy as np
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import math
import json
PI_UNICODE = "\U0001D70B"

# Parameters
num_frames = 200
omega_0 = 1  # Larmor frequency
omega_1 = 0.9  # Inhomogeneous spin
time_max = 5  # [s]

# Initial phase of the spin
initial_phase = 0.5

# Time array
time = np.linspace(0, time_max, num_frames)

# Generate data for spins
x = np.cos(omega_0 * (2*math.pi) * time + initial_phase)
y = np.sin(omega_0 * (2*math.pi) * time + initial_phase)
x1 = np.cos(omega_1 * (2*math.pi) * time + initial_phase)
y1 = np.sin(omega_1 * (2*math.pi) * time + initial_phase)

# Generate data for spins in rotating frame of reference
x_rot = np.cos((omega_0-omega_0) * (2*math.pi) * time + initial_phase)
y_rot = np.sin((omega_0-omega_0) * (2*math.pi) * time + initial_phase)
x1_rot = np.cos((omega_1-omega_0) * (2*math.pi) * time + initial_phase)
y1_rot = np.sin((omega_1-omega_0) * (2*math.pi) * time + initial_phase)

# Calculate angles
angles = (np.arctan2(y,x))
angles1 = (np.arctan2(y1,x1))
angles_rot = (np.arctan2(y_rot,x_rot))
angles1_rot = (np.arctan2(y1_rot,x1_rot))

# Create figure
fig = make_subplots(rows=1, cols=2, shared_xaxes=False, horizontal_spacing=0.1, subplot_titles=("Rotating Spin", "Signal Phase (rad)"))

# Add spin as an arrow
fig.add_trace(go.Scatter(
    x=[0, x[0]],
    y=[0, y[0]],
    mode='lines+markers',
    marker=dict(size=5),
    line=dict(color='blue', width=3),
    name='Spin at f0'),
    row=1, col=1)
fig.add_trace(go.Scatter(
    x=[0, x1[0]],
    y=[0, y1[0]],
    mode='lines+markers',
    marker=dict(size=5),
    line=dict(color='red', width=3),
    name='Inhomogeneous Spin'),
    row=1, col=1)

# Add phase of the signal
fig.add_trace(go.Scatter(
    x=[time[0]],
    y=[angles[0]],
    mode='markers',
    marker=dict(color='blue', size=5),
    name='Phase'),
    row=1, col=2
)
fig.add_trace(go.Scatter(
    x=[time],
    y=[angles1],
    mode='markers',
    marker=dict(color='red', size=5),
    name='Inhomogeneous Phase'),
    row=1, col=2
)
# Rotating frame
fig.add_trace(go.Scatter(
    x=[0, x_rot[0]],
    y=[0, y_rot[0]],
    mode='lines+markers',
    marker=dict(size=5),
    line=dict(color='blue', width=3),
    name='Spin at f0', visible=False),
    row=1, col=1)
fig.add_trace(go.Scatter(
    x=[0, x1_rot[0]],
    y=[0, y1_rot[0]],
    mode='lines+markers',
    marker=dict(size=5),
    line=dict(color='red', width=3),
    name='Inhomogeneous Spin', visible=False),
    row=1, col=1)

# Add phase of the signal
fig.add_trace(go.Scatter(
    x=[time[0]],
    y=[angles_rot[0]],
    mode='markers',
    marker=dict(color='blue', size=5),
    name='Phase', visible=False),
    row=1, col=2
)
fig.add_trace(go.Scatter(
    x=[time],
    y=[angles1_rot],
    mode='markers',
    marker=dict(color='red', size=5),
    name='Inhomogeneous Phase', visible=False),
    row=1, col=2
)

fig.update_xaxes(range=[-1.1, 1.1], row=1, col=1)
fig.update_yaxes(range=[-1.1, 1.1], row=1, col=1)
fig.update_xaxes(range=[np.min(time), np.max(time)], row=1, col=2)
fig.update_yaxes(range=[np.min(angles) + 0.1*np.min(angles), np.max(angles) + 0.1*np.max(angles)], row=1, col=2)

# Add frames
frames = [dict(
    data=[go.Scatter(x=[0, x[i]],
                     y=[0, y[i]],
                     mode='lines+markers',
                     marker=dict(size=5),
                     line=dict(color='#636EFA', width=3),
                     name='Spin at f0'),
          go.Scatter(x=[0, x1[i]],
                     y=[0, y1[i]],
                     mode='lines+markers',
                     marker=dict(size=5),
                     line=dict(color='#fa6363', width=3),
                     name='Inhomogeneous Spin'),
          go.Scatter(x=time[:i],
                     y=angles[:i],
                     mode='lines',
                     marker=dict(size=5),
                     line=dict(color='blue', width=3),
                     name='Phase'),
          go.Scatter(x=time[:i],
                     y=angles1[:i],
                     mode='lines',
                     marker=dict(size=5),
                     line=dict(color='red', width=3),
                     name='Inhomogeneous Phase'),
          go.Scatter(x=[0, x_rot[i]],
                     y=[0, y_rot[i]],
                     mode='lines+markers',
                     marker=dict(size=5),
                     line=dict(color='#636EFA', width=3),
                     name='Spin at f0'),
          go.Scatter(x=[0, x1_rot[i]],
                     y=[0, y1_rot[i]],
                     mode='lines+markers',
                     marker=dict(size=5),
                     line=dict(color='#fa6363', width=3),
                     name='Inhomogeneous Spin'),
          go.Scatter(x=time[:i],
                     y=angles_rot[:i],
                     mode='lines',
                     marker=dict(size=5),
                     line=dict(color='blue', width=3),
                     name='Phase'),
          go.Scatter(x=time[:i],
                     y=angles1_rot[:i],
                     mode='lines',
                     marker=dict(size=5),
                     line=dict(color='red', width=3),
                     name='Inhomogeneous Phase')
         
         
         ],
    name=str(i),
    traces=[0,1,2,3,4,5,6,7]) for i in range(num_frames)]

fig.frames = frames

# Determine the maximum absolute value of coordinates
max_coord = max(abs(x.max()), abs(y.max()))

fig.update_xaxes(title_text="x", row=1, col=1)
fig.update_xaxes(title_text="time", row=1, col=2)
fig.update_yaxes(title_text="y", row=1, col=1)
fig.update_yaxes(title_text="rad", tickmode = 'array',
        tickvals = [-math.pi, 0, math.pi],
        ticktext = [f"-{PI_UNICODE}", 0, f'{PI_UNICODE}'], row=1, col=2)


# Update layout
fig.update_layout(
    height=450,
    width=750,
    title="Spins rotating",
    xaxis=dict(autorange=False),
    yaxis=dict(autorange=False),
    updatemenus=
    [dict(
        type='buttons',
        buttons=[dict(label='Play',
                      method='animate',
                      args=[None, dict(frame=dict(duration=50, redraw=False), fromcurrent=True, mode='immediate')]),
                 dict(label='Pause',
                      method='animate',
                      args=[[None], dict(frame=dict(duration=0, redraw=False), mode='immediate')])
                ],
         ),
     dict(
        buttons=[dict(
                    args=[{"visible": [True, True, True, True, False, False, False, False]}],
                    label="Laboratory Frame",
                    method="update"),
                dict(
                    args=[{"visible": [False, False, False, False, True, True, True, True]}],
                    label="Rotating Frame",
                    method="update"
                )],
            direction="down",
            pad={"b": 70},
            showactive=True,
            x=-0.13,
            xanchor="left",
            y=-0.15,
            yanchor="top"
     )
    ]
)

# Show figure
fig.show()
Loading...