import mmf_setup
mmf_setup.nbinit()
%pylab inline --no-import-all
This cell adds /home/docs/checkouts/readthedocs.org/user_builds/gpe/checkouts/latest/src to your path, and contains some definitions for equations and some CSS for styling the notebook. If things look a bit strange, please try the following:
- Choose "Trust Notebook" from the "File" menu.
- Re-execute this cell.
- Reload the notebook.
%pylab is deprecated, use %matplotlib inline and import the required libraries.
Populating the interactive namespace from numpy and matplotlib
Travelling Waves: Shooting#
Here we consider shooting for solutions to the traveling wave problem. For more discussion see:
GPE#
We start with solutions to the GPE in 1D:
Implementing a full Galilean transformation, we have
This is a complex-valued ODE which we can solve by shooing. Using the global phase invariance and translational invariance, we can assume that at \(x_V=0\), the wavefunction is real and has maximum density. With the parametrization \(\psi_V(x) = \sqrt{n(x)}e^{\I x k(x)}\) this means:
(It might not be obvious that \(k'(0) = 0\), but expanding the GPE about \(x=0\) and collecting the imaginary parts of the \(x^2\) terms in the GPE requires this.)
Thus, we have a continuous family of solutions with \(0 < n_0 < (\mu-E_0)/g\). As independent parameters we may take \(\hbar\), \(m\), \(g\), specifying units, and \(0<n_0\), \(0<f=gn_0/(\mu-E_0)<1\), and \(k_0\) specifying the solution.
Numerical Example#
Here we numerically solve this equation using the scipy IVP solver, then compare this with our exact solution as implemented in the gpe.exact_solutions.TravellingWave class.
from scipy.integrate import solve_ivp
class ODE:
hbar = 1.0
g = 1.0
m = 1.0
periods = 3
n0 = 1.0
fraction = 0.9
k0 = 0.0
@property
def mu(self):
return self.g * self.n0 / self.fraction + self.E0
@property
def E0(self):
return (self.hbar * self.k0) ** 2 / 2 / self.m
@property
def y0(self):
"""Return the initial state"""
psi0 = np.sqrt(self.n0)
dpsi0 = 1j * self.k0 * psi0
return (psi0, dpsi0)
def f(self, x, y):
psi, dpsi = y
ddpsi = 2 * self.m * (self.g * np.abs(psi) ** 2 - self.mu) * psi / self.hbar**2
return (dpsi, ddpsi)
def event(self, x, y):
"""Termination when dpsi=0 again"""
if x <= 0:
return -1
psi, dpsi = y
return dpsi.real
event.terminal = True
event.direction = -1
def solve(self, max_step=0.1):
x_max = 10.0
ivp = solve_ivp(
self.f,
(0, x_max),
self.y0,
max_step=max_step,
# events=[self.event],
)
return ivp
ode = ODE()
ode.k0 = 0.3
ode.fraction = 0.9
ode.n0 = 1.0
ode.f(0, ode.y0)
(0.3j, np.float64(-0.3122222222222222))
ivp = ode.solve()
print(ivp.message)
x, psi = ivp.t, ivp.y[0]
plt.figure(figsize=(10, 3))
ax1 = plt.subplot(131)
ax1.plot(x, psi.real)
ax1.plot(x, psi.imag)
ax1.set(xlabel="x", ylabel=r"$\psi$")
ax2 = plt.subplot(132)
ax2.plot(x, abs(psi) ** 2)
ax2.set(xlabel="x", ylabel=r"$n=\psi^2$")
# L = ivp.t_events[0]
The solver successfully reached the end of the integration interval.
[Text(0.5, 0, 'x'), Text(0, 0.5, '$n=\\psi^2$')]