<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Discretizando la ecuación de Schrödinger
Autor
Step1:  Código que controla las simulaciones
Step2:  Rutinas que generan inicializan y generan la simulación
Step3:  Rutinas que conectan los controles de la primera celda con la simulacion
<ASSISTANT_TASK:>
Python Code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
import ipywidgets as widgets
from IPython.display import display
L = 200
dx = 2.
buttonrunsim=widgets.Button(description="Simulate")
outwdt = widgets.Output()
centropaquete = widgets.FloatSlider(value=-3*L/4,min=-L,max=L,step=1,
    description='centro del paquete',
    ensure_option=True,
    disabled=False)
anchopaquete = widgets.FloatSlider(value=L/8,min=1,max=L,step=1,
    description='ancho del paquete',
    ensure_option=True,
    disabled=False)
valuek0 = widgets.FloatSlider(value=0,min=0,max=6.28/dx,step=.1,
    description='k0',
    ensure_option=True,
    disabled=False)
valueV0 = widgets.FloatSlider(value=0,min=-4,max=4,step=.1,
    description='V0',
    ensure_option=True,
    disabled=False)
potencial = widgets.Dropdown(options=["Cuadrado","Rampa","Cuadrático","Gaussiano"],
                            description="Forma del Potencial",
                             ensure_option=True,
                            disabled=False)
valueW = widgets.FloatSlider(value=1.,min=1.,max=300.,step=.5,
    description='Alcance potencial',
    ensure_option=True,
    disabled=False)
items = [[widgets.Label("Paquete"),centropaquete,anchopaquete,valuek0,
          widgets.Label("Potencial"),valueW,valueV0,potencial,buttonrunsim],[outwdt]]
widgets.HBox([widgets.VBox(it) for it in items])
Explanation: Discretizando la ecuación de Schrödinger
Autor: Juan Mauricio Matera (Facultad de Ciencias Exactas, UNLP) 
12 de agosto de 2020
La ecuación de Schrödinger dependiente del tiempo en una dimensión
$$
{\bf i}\hbar \frac{d}{dt}\psi(x,t) = -\frac{\hbar^2}{2m}\nabla^2 \psi(x,t) + V(x) \psi(x,t)
$$
define $\psi(x,t)$ a partir del valor inicial $\psi(x,t_0)$. Si bien en algunos casos especiales existen soluciones analíticas, aquí analizaremos sus soluciones numéricas vía discretización. Para eso, un primer paso
es adimensionalizar la ecuación:
$$
\frac{2 m (\Delta x)^2}{\hbar}\frac{d}{dt}\psi(x,t) = {\bf i}(\Delta x)^2\nabla^2 \psi(x,t) + (\frac{2 m (\Delta x)^2}{\hbar} V(x)) \psi(x,t)
$$
ó
$$
\frac{d}{d \tilde{t}}\tilde{\psi}(\tilde{x},\tilde{t}) = 
{\bf i}\nabla^2 \tilde{\psi}(\tilde{x},\tilde{t}) + \tilde{V}(\tilde{x})\tilde{\psi}(\tilde{x},\tilde{t})
$$
con
\begin{eqnarray}
\tilde{t}&=& \frac{2 m (\Delta x)^2}{\hbar}t\
\tilde{x}&=& x/\Delta x\
\tilde{V}(\tilde{x})&=& \frac{2 m \Delta x^2}{\hbar^2}V(\tilde{x} \Delta x)\
\tilde{\psi}(\tilde{x},\tilde{t})&=&\psi(\tilde{x}\Delta x,\tilde{t} \Delta t)
\end{eqnarray}
y $\Delta x$ elegido lo suficientemente pequeño de manera que, durante la evolución,
$$
\nabla^2\psi(x,t) \approx \frac{\psi(x+\Delta x,t)+\psi(x-\Delta x,t)-2\psi(x,t)}{(\Delta x)^2}
$$
Luego, en lugar de trabajar en el continuo, podemos resolver el problema sobre un conjunto discreto de puntos separados por una distancia $\Delta x$. Además, en lugar de trabajar sobre toda la recta real, vamos a considerar puntos dentro de un intervalo $(-L,L)$, y vamos a imponer condiciones de borde periódicas, de manera que 
$$
\psi(x+2L,t)=\psi(x,t)\;\;\mbox{y}\;\;V(x+2L)=V(x)
$$
de manera que el problema se reduce a estudiar la evolución temporal de la función de onda en $n=\frac{2 L}{\Delta x}$ puntos. 
Finalmente, asumiremos que el estado inicial es de la forma
$$
\psi(x,t_0)=\sqrt{\rho(x,t_0)}e^{{\bf i}k_0 x}
$$
con $\rho(x,t_0)=\frac{e^{-\frac{(x-x_0)^2}{2 a^2}}}{\sqrt{2\pi a}}$ en el intervalo considerado, de forma que $-L<x_0<L$ y $a+|x_0|\ll L$ y $k<\frac{2\pi}{\Delta x}$.
Simulación
End of explanation
# Para fijar parámetros a mano
#### Parámetros de la discretización
dx = 2.
dt = dx**2 * .1
L = 200
####  Estado inicial
a = 20.
x0 = -L/3
k0 = 10./a
#### Potencial
U0= -1.
w = 10.
Explanation: Código que controla las simulaciones
End of explanation
############    Inicializar  #############
def init_state(L=L,dx=dx,a=a,x0=x0,k0=k0):
    global rho0
    global psi
    global xs
    xs = np.linspace(-L,L,int(2*L/dx))
    rho0 = np.array([np.exp(-(x-x0)**2/(2*a**2))  for x in xs]) 
    rho0[0] = 0
    rho0[-1] = .5*rho0[-2]
    rho0[1] = .5*rho0[2]
    rho0 = rho0/sum(rho0)/dx
    psi0 = rho0**.5 * np.exp(1j*k0*(xs-x0))
    psi = psi0
    return psi
def evolve(psi,xs,pot):
    global dt,dx
    U = dx**2*pot(xs)
    cc = dt/dx**2
    deltapsi = ((1.+U)*psi-.5*(np.roll(psi,1)+np.roll(psi,-1))) 
    psi[:] = psi[:] - cc * 1j * deltapsi[:]
    # Corrección a segundo orden 
    deltapsi = ((1.+U)*deltapsi-.5*(np.roll(deltapsi,1)+np.roll(deltapsi,-1))) 
    psi[:] = psi[:] - (.5*(cc)**2) * deltapsi[:]
    psi[:] = psi[:] /np.sqrt(sum(np.abs(psi)**2))/dx**.5
    return psi
def Ugaussiano(xs):
    return U0*np.exp(-xs**2/(.5*w**2))
def Uescalon(xs):
    return np.array([U0 if abs(x)<.5*w else 0 for x in xs])
def Uarmonico(xs):
    return np.array([U0*(1-4*(x/w)**2) if abs(x)<.5 * w else 0 for x in xs])
def Urampadoble(xs):
    return np.array([U0*(1-2*abs(x)/w) if abs(x)<.5 * w else 0 for x in xs])
def make_animation_new(ts=50, progress=None):
    global psi
    global xs
    global U0,k0,w,x0,a,U
    
    init_state(a=a,k0=k0,x0=x0)
    rho0 = abs(psi)**2
    fig1 = plt.figure()
    plt.xlim(-L,L)
    plt.ylim(-abs(U0)-k0**2-.5,.5+abs(U0)+k0**2)
    plt.plot(xs,U(xs),ls="-.")
    density,= plt.plot(xs,rho0/max(rho0))
    def update_graphic(t,density):
        if progress is not None:
            progress.value = t
        for i in range(100):
            evolve(psi,xs,U)
        rho = abs(psi)**2
        density.set_data(xs,50*(U0+.1)*rho+.5*k0**2)
        return density,
    line_ani = animation.FuncAnimation(fig1, update_graphic, ts, fargs=(density,),
                                       interval=100, blit=True)
    plt.close()
    return HTML(line_ani.to_jshtml())
    
Explanation: Rutinas que generan inicializan y generan la simulación
End of explanation
def on_button_start_sim(b):
    global U0, w, k0, a, x0, U
    U0 = valueV0.value
    w = valueW.value
    k0 = valuek0.value
    a = anchopaquete.value
    x0 = centropaquete.value
    if potencial.value == "Gaussiano":
        U = Ugaussiano
    elif potencial.value == "Cuadrático":
        U = Uarmonico
    elif potencial.value == "Cuadrado":
        U = Uescalon
    elif potencial.value == "Rampa":
        U = Urampadoble
    else:
        print("No encontrado")
        return
    
    outwdt.clear_output()
    progress = widgets.FloatProgress(value=0,min=0,max=50,step=1,description='Simulating:',
    bar_style='info',orientation='horizontal')
    
    with outwdt:
        print([a,x0,k0,U0,w])
        display(progress)
        result = make_animation_new(progress=progress)     
        outwdt.clear_output()
        display(result)
        
buttonrunsim.on_click(on_button_start_sim)
    
    
Explanation: Rutinas que conectan los controles de la primera celda con la simulacion
End of explanation
<END_TASK>