Step 4: Plot The Reconstructed Orbits¶
%matplotlib ipympl
import plotly.express as px
import plotly.graph_objects as go
import polars as pl
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import plotly.io as pio
pio.renderers.default = "notebook" # or "browser"
Data loading and preprocessing¶
subharmonics_colors = {
0: "#BAC24C",
1: "#5179D6",
2: "#2ca02c",
3: "#d62728",
4: "#9467bd",
5: "#9651D6",
}
orbits = pl.read_parquet("outputs/orbits.parquet")
orbits
shape: (58, 19)
| x | dotx | v | Ev | xw | w0 | Ad | Q | fd | alpha | C0 | R | L | M | detected_subharmonic | target_frequency | orbit_label | attractor_label | Eh |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | i64 | f64 | i64 | i64 | f64 |
| -0.000265 | 0.293995 | -3.101933 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 35.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 1 | 35.0 | 0 | 0 | 0.000021 |
| 0.000683 | 0.050324 | 0.622915 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 44.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 3 | 44.0 | 1 | 1 | 0.000001 |
| 0.000031 | -0.068758 | -0.651765 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 44.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 3 | 44.0 | 1 | 2 | 8.7274e-7 |
| -0.00075 | 0.044738 | 0.006936 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 44.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 3 | 44.0 | 1 | 3 | 7.6701e-7 |
| -0.000499 | -0.009333 | 0.024962 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 50.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 1 | 50.0 | 2 | 4 | 6.5385e-9 |
| … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … |
| 0.000701 | 0.056828 | 0.654325 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 47.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 3 | 47.0 | 30 | 53 | 0.000001 |
| -0.000207 | 0.247926 | -2.655609 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 32.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 1 | 32.0 | 31 | 54 | 0.000017 |
| -0.000063 | 0.102962 | -1.171436 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 20.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 1 | 20.0 | 32 | 55 | 0.000007 |
| -0.000564 | -0.028966 | 0.325005 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 38.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 2 | 38.0 | 33 | 56 | 2.9101e-7 |
| -0.000347 | -0.002615 | -0.181397 | 0.0 | 0.0005 | 121.0 | 2.5 | 87.0 | 38.0 | 0.068 | 0.000001 | 7830.0 | 0.025 | 0.0173 | 2 | 38.0 | 33 | 57 | 4.3629e-8 |
orbit_data = pl.read_parquet("outputs/orbit_data.parquet")
orbit_data.sort("Ph", descending=True)
shape: (34, 5)
| orbit_label | fd | detected_subharmonic | Eh | Ph |
|---|---|---|---|---|
| i64 | f64 | i64 | f64 | f64 |
| 4 | 50.0 | 1 | 0.000053 | 0.002674 |
| 10 | 47.0 | 1 | 0.000045 | 0.002125 |
| 19 | 44.0 | 1 | 0.000038 | 0.001669 |
| 28 | 41.0 | 1 | 0.000032 | 0.001293 |
| 29 | 38.0 | 1 | 0.000026 | 0.000987 |
| … | … | … | … | … |
| 18 | 44.0 | 1 | 1.3140e-8 | 5.7818e-7 |
| 8 | 47.0 | 1 | 9.1480e-9 | 4.2996e-7 |
| 9 | 47.0 | 1 | 9.1450e-9 | 4.2982e-7 |
| 2 | 50.0 | 1 | 6.5385e-9 | 3.2693e-7 |
| 3 | 50.0 | 1 | 6.5373e-9 | 3.2687e-7 |
power_data = orbit_data.with_columns(
(pl.col("Ph") * 1000.0).alias("Ph_scaled"),
pl.col("detected_subharmonic").cast(pl.Utf8).alias("detected_subharmonic_label"),
)
fig = px.scatter(
power_data.to_pandas(),
x="fd",
y="Ph_scaled",
color="detected_subharmonic_label",
symbol="detected_subharmonic_label",
color_discrete_map={str(key): value for key, value in subharmonics_colors.items()},
hover_data=["orbit_label", "fd", "Ph", "detected_subharmonic"],
log_y=True,
title="Damping dissipation vs. Drive Frequency",
labels={
"fd": "Drive Frequency [Hz]",
"Ph_scaled": "Harvested Power [mW]",
"detected_subharmonic_label": "Detected subharmonic",
},
)
fig.update_traces(marker=dict(size=10))
fig.update_layout(width=1000, height=700)
fig
Orbits a given frequency¶
fd = 50.0
x = "x"
y = "dotx"
z = "v"
xlabel = "Position, x"
ylabel = "Speed, dot x"
zlabel = "Voltage, v"
orbits_fd = orbits.filter(pl.col("fd") == fd)
orbits_data_fd = orbit_data.filter(pl.col("fd") == fd)
orbits_dic = {}
for row in orbits_fd.iter_rows(named=True):
olabel = row["orbit_label"]
alabel = row["attractor_label"]
if olabel not in orbits_dic.keys():
orbits_dic[olabel] = {"orbit": {}, "data": row}
odata = pl.read_parquet(
f"outputs/orbits_from_attractors/orbit_{olabel}_attractor_{alabel}.parquet"
)
orbits_dic[olabel]["orbit"][alabel] = odata
fig = go.Figure()
for ok, odata in orbits_dic.items():
xv = []
yv = []
zv = []
name = ""
sh = odata["data"]["detected_subharmonic"]
if sh == 1:
name += "H"
else:
name += f"SH"
name += f"{sh}_id{ok}"
for ak, adata in odata["orbit"].items():
orbit = odata["orbit"][ak].to_pandas()
xv.append(orbit[x].values)
yv.append(orbit[y].values)
zv.append(orbit[z].values)
xv = np.concatenate(xv)
yv = np.concatenate(yv)
zv = np.concatenate(zv)
Np = len(xv) // sh
fig.add_trace(go.Scatter3d(
x=xv,
y=yv,
z=zv,
mode="lines",
legendgroup=f"g{ok}",
line=dict(
color=subharmonics_colors[odata["data"]["detected_subharmonic"]], width=4
),
name=name,
)
)
fig.add_trace(go.Scatter3d(
x=xv[::Np],
y=yv[::Np],
z=zv[::Np],
mode="markers",
legendgroup=f"g{ok}",
marker=dict(symbol="circle", color=subharmonics_colors[odata["data"]["detected_subharmonic"]], size=3),
showlegend=False
)
)
fig.update_layout(
title=f"Orbits and attractors at Drive Frequency fd={fd} Hz",
legend=dict(
title="Orbits and attractors",
x=0.02, y=0.98,
bgcolor="rgba(255,255,255,0.7)"
),
margin=dict(l=0, r=0, t=50, b=0),
scene=dict(
xaxis=dict(
title=xlabel,
showgrid=True,
gridcolor="rgba(0,0,0,0.15)",
zeroline=True,
zerolinecolor="rgba(0,0,0,0.25)",
showbackground=True,
backgroundcolor="rgba(245,245,245,1)",
ticks="outside",
),
yaxis=dict(
title=ylabel,
showgrid=True,
gridcolor="rgba(0,0,0,0.15)",
zeroline=True,
showbackground=True,
backgroundcolor="rgba(245,245,245,1)",
),
zaxis=dict(
title=zlabel,
showgrid=True,
gridcolor="rgba(0,0,0,0.15)",
zeroline=True,
showbackground=True,
backgroundcolor="rgba(245,245,245,1)",
),
# Keep scales comparable (optional)
aspectmode="cube", # or "data" / "manual"
# aspectratio=dict(x=1, y=1, z=0.6),
# Initial view (optional)
camera=dict(
eye=dict(x=1.6, y=1.6, z=1.1)
)
)
)
fig.update_layout(
width=1000,
height=1000,
)
fig.show()
Orbits as a function of frequency¶
x = "x"
y = "dotx"
xlabel = "Position, x"
ylabel = "Speed, dot x"
zlabel = "Drive Frequency, fd [Hz]"
orbits_dic = {}
for row in orbits.iter_rows(named=True):
olabel = row["orbit_label"]
alabel = row["attractor_label"]
if olabel not in orbits_dic.keys():
orbits_dic[olabel] = {"orbit": {}, "data": row}
odata = pl.read_parquet(
f"outputs/orbits_from_attractors/orbit_{olabel}_attractor_{alabel}.parquet"
)
orbits_dic[olabel]["orbit"][alabel] = odata
fig = go.Figure()
show_legend = set()
for ok, odata in orbits_dic.items():
xv = []
yv = []
name = ""
sh = odata["data"]["detected_subharmonic"]
fd = odata["data"]["fd"]
if sh == 1:
name += "H"
else:
name += f"SH"
name += f"{sh}"
for ak, adata in odata["orbit"].items():
orbit = odata["orbit"][ak].to_pandas()
xv.append(orbit[x].values)
yv.append(orbit[y].values)
xv = np.concatenate(xv)
yv = np.concatenate(yv)
Np = len(xv) // sh
zv = np.ones_like(xv) * fd
oname = name + "_orbit"
aname = name + "_attractor"
fig.add_trace(go.Scatter3d(
x=xv,
y=yv,
z=zv,
mode="lines",
legendgroup=oname,
line=dict(
color=subharmonics_colors[odata["data"]["detected_subharmonic"]], width=4
),
name=oname,
showlegend=oname not in show_legend,
)
)
show_legend.add(oname)
fig.add_trace(go.Scatter3d(
x=xv[::Np],
y=yv[::Np],
z=zv[::Np],
mode="markers",
legendgroup=aname,
name =aname,
marker=dict(symbol="circle", color=subharmonics_colors[odata["data"]["detected_subharmonic"]], size=3),
showlegend=aname not in show_legend
)
)
show_legend.add(aname)
fig.update_layout(
title=f"Orbits and attractors at Drive Frequency fd={fd} Hz",
legend=dict(
title="Orbits and attractors",
x=0.02, y=0.98,
bgcolor="rgba(255,255,255,0.7)"
),
margin=dict(l=0, r=0, t=50, b=0),
scene=dict(
xaxis=dict(
title=xlabel,
showgrid=True,
gridcolor="rgba(0,0,0,0.15)",
zeroline=True,
zerolinecolor="rgba(0,0,0,0.25)",
showbackground=True,
backgroundcolor="rgba(245,245,245,1)",
ticks="outside",
),
yaxis=dict(
title=ylabel,
showgrid=True,
gridcolor="rgba(0,0,0,0.15)",
zeroline=True,
showbackground=True,
backgroundcolor="rgba(245,245,245,1)",
),
zaxis=dict(
title=zlabel,
showgrid=True,
gridcolor="rgba(0,0,0,0.15)",
zeroline=True,
showbackground=True,
backgroundcolor="rgba(245,245,245,1)",
),
# Keep scales comparable (optional)
aspectmode="cube", # or "data" / "manual"
# aspectratio=dict(x=1, y=1, z=0.6),
# Initial view (optional)
camera=dict(
eye=dict(x=1.6, y=1.6, z=1.1)
)
)
)
fig.update_layout(
width=1000,
height=700,
)
fig.show()