Skip to content
Snippets Groups Projects
Commit 2798ec5d authored by Minouk Noordsij's avatar Minouk Noordsij
Browse files

Merge branch 'Interactive_Ch3' into 'main'

add interactive plot

See merge request !1
parents 36deac60 fc513db6
No related branches found
No related tags found
1 merge request!1add interactive plot
......@@ -137,48 +137,40 @@ Note, that for $Re<1$ the friction force tends to $F_f \propto -v$ as we had in
### Simulation of a falling hail stone: friction versus no friction with the surrounding air ###
```{code-cell} ipython3
:tags: [hide-input, remove-output]
:tags: [hide-input] #, remove-output]
%config InlineBackend.figure_formats = ['svg']
import plotly.graph_objects as go
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': 12})
from matplotlib.widgets import Button, Slider
from myst_nb import glue
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#"""
#Created on Wed Oct 30 11:39:43 2024
#
#@author: rmudde
#"""
rho_p = 915
################ Code used for physics ################
# Define constants
rho_p = 915
rho_air = 1.2
mu_air = 1.8e-5
D = 2.5e-3
H = 1e3
g = 9.813
v0 = 0
z0 = H
m = np.pi/6 * D**3 * rho_p
A = np.pi/4 * D**2
mu_air = 1.8e-5
D = 2.5e-3
H = 1e3
g = 9.813
v0 = 0
z0 = H
m = np.pi/6 * D**3 * rho_p
A = np.pi/4 * D**2
Vol = np.pi/6 * D**3
dt = 1e-2
dt = 1e-2
Ndata = 501
# Update D values
def recalc(D):
D = D
m = np.pi/6 * D**3 * rho_p
A = np.pi/4 * D**2
'''Function to recompute all values that depend on D'''
D = D
m = np.pi/6 * D**3 * rho_p
A = np.pi/4 * D**2
Vol = np.pi/6 * D**3
return D, m, A, Vol
def CD(v,D):
#compute drag coefficient
'''Function to compute the drag coefficient'''
Re = rho_air * np.abs(v) * D / mu_air
if Re < 1e-6:
Re = 1e-6
......@@ -186,16 +178,17 @@ def CD(v,D):
return CD
def force(v,D,m,Vol,A):
'''Function to compute the force'''
f = -m*g + rho_air*Vol*g - CD(v,D)*A*1/2*rho_air*np.abs(v)*v
return f
# The parametrized function to be plotted
def velo(D):
'''Parametrized function to determine t, v and v without friction used in plotting'''
recalc(D)
v=[]
v = []
v_nofriction = []
z=[]
t=[]
z = []
t = []
v.append(v0)
v_nofriction.append(v0)
z.append(z0)
......@@ -209,68 +202,100 @@ def velo(D):
v.append(v_new)
z.append(z_new)
v_nofriction.append(-g*teller*dt)
return t,v,v_nofriction
return t, v, v_nofriction
# Compute initial values
[t,v,v_nofriction] = velo(D)
################ Code used for Interactive Plot ################
line_red = '#e96868' # red line
line_blue = '#68a0e9' # blue line
fig = go.Figure()
fig, ax = plt.subplots(figsize=(6,4))
line, = ax.plot(t[0:Ndata], v[0:Ndata], '-r')
line2, = ax.plot(t[0:Ndata], v_nofriction[0:Ndata], '-b')
ax.axhline(y = 0, color = 'k', linestyle = ':')
ax.set_xlim(0,t[Ndata])
ax.set_ylim(2.0*v[Ndata],0)
ax.set_xlabel('$t$')
ax.set_ylabel('$v(t)$')
plt.title('velocity of a hail stone')
plt.grid()
plt.legend(['friction','no-friction'])
# adjust the main plot to make room for the sliders
fig.subplots_adjust(left=0.25, bottom=0.25)
# Make a horizontal slider to control the frequency.
axdia = fig.add_axes([0.25, 0.1, 0.65, 0.03])
dia_slider = Slider(
ax=axdia,
label='diameter [mm]',
valmin=0.1,
valmax=5.0,
valinit=2.5,
)
# Define the steps for the slider and an empty array of the minimum values used later for setting the y-axis range
slider_steps = np.arange(0.1,5,0.1)
vmin = np.zeros_like(slider_steps)
# The function to be called anytime a slider's value changes
def update(val):
D = dia_slider.val * 1e-3
for ind,val in enumerate(slider_steps):
# Rescale D to meters and compute values
D = val * 1e-3
recalc(D)
[t,v,v_nofriction] = velo(D)
line.set_ydata(v[0:Ndata])
ax.set_ylim(2.0*v[Ndata],0)
fig.canvas.draw_idle()
# register the update function with each slider
dia_slider.on_changed(update)
# Create a `matplotlib.widgets.Button` to reset the sliders to initial values.
resetax = fig.add_axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', hovercolor='0.975')
vmin[ind] = min(v)
# Add the trace with friction
fig.add_trace(
go.Scatter(
visible=False,
x = t,
y = v,
line = dict(color=line_red),
mode = 'lines',
name = 'friction'))
# Add the trace without friction
fig.add_trace(
go.Scatter(
visible=False,
x = t,
y = v_nofriction,
line = dict(color=line_blue),
mode = 'lines',
name = 'no friction'))
# Update the y-axis range
fig.update_yaxes(range=[2 * v[-1], 0], title_text=r'v(t)')
base_traces = 0
traces_per_step = 2 # Number of traces per value of a
# Show the traces for one value of a when loading the plot (initial setup)
active_index = slider_steps.shape[0]//2
# Set the traces for the active value of D to visible
for i in range(traces_per_step):
current_index = int(base_traces + active_index*traces_per_step + i)
fig.data[current_index].visible = True
steps = []
for i in range(0, slider_steps.shape[0]):
# Make only the traces for the current value of a visible
visarray = [False] * len(fig.data)
visarray[0:base_traces] = [True] * base_traces
current_index = int(base_traces + i * traces_per_step)
next_idx = int(base_traces + (i+1) * traces_per_step)
visarray[current_index:next_idx] = [True] * traces_per_step
# Define content of the slider step
step = dict(
method="update",
args=[
{"visible": visarray},
{"yaxis.range": [2 * vmin[i], 0]},
{"yaxis.titletext": r'v(t)'}
],
label=str(round(slider_steps[i], 1))
)
steps.append(step)
sliders = [dict(
active = active_index,
currentvalue = {"prefix": "diameter: ", "suffix": " mm", "font": {"size": 20}},
steps = steps
)]
def reset(event):
dia_slider.reset()
button.on_clicked(reset)
fig.update_layout(
sliders=sliders,
legend_title="Legend"
)
plt.show()
fig.update_xaxes(title_text=r't [s]', range=[0, 5])
# Save graph to load in figure later (special Jupyter Book feature)
glue("HailStone", fig, display=True)
# Update yaxis properties
fig
```
```{glue:figure} HailStone
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment