Hi,
I would like to do something similar for my project, and then export the plot, so that after the simulation I could have it saved as a "video" or something of sorts.
I have tried several approaches, but none seem to work.
Can you give me a hand on how to go about exporting the plot of the voltages changing over time?
I have made several different scripts (some with help from GPT and making use of other topics in the forum) to try to go about this, which I will go through below.
Code: Select all
def plot_view(folder=os.getcwd(),tmin=1e5,tmax=1e5,cell=None):
ps=h.PlotShape(True)
ps.variable("v")
#ps.scale(-80, 30)
h.fast_flush_list.append(ps)
ps.exec_menu("Shape Plot")
ps.exec_menu("Show Diam")
ps.exec_menu("View = plot")
output_dir="frames"
out=os.path.join(folder,output_dir)
os.makedirs(out, exist_ok=True) # Create directory if it doesn't exist
def save_files():
t=h.t
if t>=tmin and t>=tmax:
filename=f"frame_{t:.2f}.eps"
file=os.path.join(out,filename)
ps.printfile(file)
callback=h.beforestep_callback(cell.soma(0.5))
callback.set_callback(save_files)
return ps, callback
This first one is based on the implementation you suggested here, and it works regarding visualization of the voltages changing over time, but then I can't get it to save anything. Is there a way for me to do it?
Code: Select all
def morphology_voltage_movie(cell, folder,cmap=cm.cool,tmin=1e5,tmax=1e5):
fig = go.FigureWidget()
fig.update_layout(
title="Membrane Voltage Over Time",
scene=dict(
xaxis=dict(title="X"),
yaxis=dict(title="Y"),
zaxis=dict(title="Z"),
)
)
# Extract the morphology
x_coords, y_coords, z_coords, arcs, diams, initial_v = [], [], [], [], [], []
for sec in cell.all:
for i in range(sec.n3d()):
x=sec.x3d(i)
y=sec.y3d(i)
z=sec.z3d(i)
arc=sec.arc3d(i)
diam=sec.diam3d(i)
x_coords.append(x)
y_coords.append(y)
z_coords.append(z)
arcs.append(arc)
diams.append(diam)
for sec in cell.all:
for seg in sec:
initial_v.append(seg.v)
# Normalize for the colormap
vmin, vmax = -100, 50 # Adjust based on expected voltage range
norm = cm.colors.Normalize(vmin=vmin, vmax=vmax)
colors = [f"rgb{tuple((np.array(cmap(norm(v)))[:3] * 255).astype(int))}" for v in initial_v]
scatter=go.Scatter3d(
x=x_coords,
y=y_coords,
z=z_coords,
mode='markers',
marker=dict(
size=5,
color=colors,
showscale=True,
colorbar=dict(title="Voltage (mV)"),
colorscale="Viridis"
),
text=[f"Voltage: {v:.2f} mV" for v in initial_v]
)
fig.add_trace(scatter)
# for sec in cell.all:
# for seg in sec:
# # x_coords.append(h.x3d(sec.arc3d(seg.x)))
# # y_coords.append(h.y3d(sec.arc3d(seg.x)))
# # z_coords.append(h.z3d(sec.arc3d(seg.x)))
# x_coords.append(seg.x_xtra)
# y_coords.append(seg.y_xtra)
# z_coords.append(seg.z_xtra)
# v_values.append(seg.v) # Initial voltage
output_dir="frames"
out=os.path.join(folder,output_dir)
os.makedirs(out, exist_ok=True) # Create directory if it doesn't exist
def update_plot():
"""
Callback to update the morphology plot with current voltage values.
"""
# Update voltage values
current_voltages = []
for sec in cell.all:
for seg in sec:
current_voltages.append(seg.v)
# Map voltage to colors
colors = [f"rgb{tuple((np.array(cmap(norm(v)))[:3] * 255).astype(int))}" for v in current_voltages]
scatter.marker.color = colors
scatter.text = [f"Voltage: {v:.2f} mV" for v in current_voltages]
# Save frame to file
if tmin <= h.t <= tmax:
filename=f"frame_{h.t:.2f}.png"
out_file=os.path.join(out,filename)
fig.write_image(out_file)
# Register the callback with NEURON
callback = h.beforestep_callback(cell.soma(0.5))
callback.set_callback(update_plot)
return fig, callback
This one makes use of the way to add a colorbar described in
viewtopic.php?p=20058#p20058, and also the beforestep_py mod file described in
viewtopic.php?t=3389. In this I was trying to avoid using a PlotShape directly, cause it wasn't working for me, but have had no success in getting it to actually plot or save something.
Code: Select all
def plot_voltage_shape(folder,cell,max_shift):
# locations=load_geometry(folder)
voltages=load_voltages(folder)
ps = h.PlotShape(False)
vmin=min(max_shift)
vmax=max(max_shift)
ps.show(0)
ps.variable("v") # Associate the PlotShape with the 'v' variable
ps.scale(vmin, vmax) # Set the color scale
fig=ps.plot(plotly, cmap=cm.cool)
# Create a custom colormap using Matplotlib (cool colormap)
cmap = cm.cool
# Collect values of the variable from all segments
# Create a colormap function
colormap = cm.ScalarMappable(cmap=cmap, norm=mcolors.Normalize(vmin=0, vmax=1)).to_rgba
# Map the normalized values to a Plotly colorscale as strings
plotly_colorscale = [[v, f'rgb{tuple(int(255 * c) for c in colormap(v)[:3])}'] for v in np.linspace(0, 1, cmap.N)]
# Create a separate scatter plot for the colorbar
colorbar_trace = go.Scatter(
x=[0],
y=[0],
mode='markers',
marker=dict(
colorscale=plotly_colorscale,
cmin=vmin,
cmax=vmax,
colorbar=dict(
title="Max Shift",
thickness=20 # Adjust the thickness of the colorbar
),
showscale=True
)
)
# Add the colorbar trace to the figure
fig.add_trace(colorbar_trace)
fig.update_xaxes(showticklabels=False, showgrid=False)
fig.update_yaxes(showticklabels=False, showgrid=False)
fig.update_layout(
plot_bgcolor='rgba(0,0,0,0)'
)
fig.show()
def saveplot():
output_dir="frames"
out=os.path.join(folder,output_dir)
os.makedirs(out, exist_ok=True) # Create directory if it doesn't exist
file=os.path.join(out,f"pshape_t{t}.eps")
ps.printfile(file)
for i,t in enumerate(voltages["t"]):
v_values=voltages.iloc[i,1:]
index=0
for sec in cell.all:
for seg in sec:
seg.v=v_values[index]
index+=1
saveplot()
This last one makes use of the voltages for each time step that I saved in a csv file. It doesn't work in regards to saving the plot, but even if it did, I can't help but think it's an inefficient way to go about it. I know I have made some mistakes, so I was hoping to get some guidance on how to fix them and how to go about doing this... Apologies if my post is too confusing and if there is missing information.