# 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...