Solving a PDE using FFT ( Cahn Hilliard equation) - python

I am trying to solve the cahn hilliard equation using fft on python. My code is simple and logical however it is not working, the final mixture is not mixing correctly , I am obtaining a random concentration at the end. Here is my code, any help is greatly appreciated. The equation has two laplacian derivatives. (dc/dt = laplacian( c^3 - c - a^2*laplacian(c)) )
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
import matplotlib.animation as ani
from scipy.fft import fft, ifft
C=np.random.uniform(-1,1, size=(128,128))
C=np.reshape(C,(len(C),len(C)))
print(C)
w2xx=np.zeros((len(C),len(C)))
w2yy=np.zeros((len(C),len(C)))
f2xx=np.zeros((len(C),len(C)))
f2yy=np.zeros((len(C),len(C)))
E=np.zeros((len(C),len(C)))
Cold=np.zeros((len(C),len(C)))
Cnew=E=np.zeros((len(C),len(C)))
#w2xx=np.reshape(A,(len(C),len(C)))
#w2yy=np.reshape(B,(len(C),len(C)))
#f2xx=np.reshape(D,(len(C),len(C)))
E=np.reshape(E,(len(C),len(C)))
delta_t=10**-8
a=0.01
delta_x=7.814*(10**-3)
x = np.linspace(0, 1, len(C))
y = np.linspace(0, 1,len(C))
x1 =np.arange(0, 127 , 1)
y1= np.linspace(0,1, len(C))
X, Y = np.meshgrid(x, y)
plt.contourf(X, Y, C, 10, cmap='RdGy')
plt.show()
time= np.arange(0, 0.012/delta_t , 1)
cc1=1
cc2=1
N=128
k1=np.arange(0,(N/2),1)
k2=np.arange((-N/2)+1,0,1)
k=np.r_[k1,0,k2]
k=np.fft.fftshift(k)
for t in time:
for i in x1:
vx_hat=fft(C[i,0:len(C)])
vy_hat=fft(C[0:len(C),i])
wx_hat=np.multiply(-(k**2),(vx_hat.real))
wy_hat=np.multiply(-(k**2),((vy_hat.real)))
w2xx[i,:]=ifft(wx_hat).real
w2yy[:,i]=ifft(wy_hat).real
F=(np.power(C,3))-C-((a**2)*(w2xx+w2yy))
for i in x1:
vx_hat=fft(F[i,:])
vy_hat=fft(F[:,i])
wx_hat=np.multiply(-(k**2),(vx_hat.real))
wy_hat=np.multiply(-(k**2),((vy_hat.real)))
f2xx[i,:]=ifft(wx_hat).real
f2yy[:,i]=ifft(wy_hat).real
# if t<0.005:
# Cold=C
C=C+(((2*10**-6))*(f2xx+f2yy))
# else:
# Cnew = 2*C - Cold + ((delta_t**2)*(f2xx+f2yy))
# Cold = C
# C = Cnew
plt.contourf(X, Y, C, 10, cmap='RdGy')
plt.show()

Related

about reconstructing the curve from piecewise polynomial spline

I am self learning the spline used in computer graphical theory. I read one book about interpolation and spline and now would like to try my understanding with Python. I created some random points, build the Bspline with Scipy, and convert the spline pp form, try to reconstruct the function with the breaks and coefficients. From the help of scir about scipy.interpolate.PPoly, I got
The polynomial between x[i] and x[i + 1] is written in the local power basis:
S = sum(c[m, i] * (xp - x[i])**(k-m) for m in range(k+1))
where k is the degree of the polynomial.
My understanding is if the coefficients c and breaks x[i] are provided, to reconstruct the function within x[i] and x[i+1], one only have to build the polynomial up to k degree. I try the following code
from scipy.interpolate import BSpline, CubicSpline
from scipy.interpolate import splrep
from scipy.interpolate import BPoly, PPoly
import numpy as np
x = np.array([-0.5537, 0.5257, 1.3598, 1.8014, 2.4393, 4.0584, 4.9416, 5.4813, 5.9720, 6.6098, 6.9042, 7.3458, 7.8855, 7.9836, 8.2290])
y = np.array([16.5526, 16.4054, 15.6694, 14.1484, 12.5783, 12.9708, 14.1484, 14.9825, 15.9638, 16.6507, 16.6016, 16.0129, 15.2278, 13.7558, 12.0386])
tck = t,c,k = splrep(x, y, s=0.8, k=3)
spl = BSpline(t, c, k)
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
xx = np.linspace(0, 8.5, 500)
ax.plot(xx, spl(xx), 'b-', lw=4, alpha=0.7, label='BSpline')
ax.plot(x, y, 'ks')
F = PPoly.from_spline(tck)
i = 0
xxx = np.linspace(x[i], x[i+1], 500)
yyy = sum(F.c[m, i] * (xxx - x[i])**(k-m) for m in range(k+1))
plt.plot(xxx, yyy,'r')
Here I only interpret the x variables from x[i=0] to x[i+1=1], it does plot a good fit from x[0] to x[1] as follow
Following the similar idea, I am going to plot the section from x[1] to x[2] with i=1, the code is modified as
i = 1
xxx = np.linspace(x[i], x[i+1], 500)
yyy = sum(F.c[m, i] * (xxx - x[i])**(k-m) for m in range(k+1))
plt.plot(xxx, yyy,'r')
I expect it plot the curve as shown in the magenta but I got the red line instead. Could someone help me out and point out which step I did it wrong. Many thanks.
Someone help to point out the mistake I made

How to indicate on a graph where the line started?

I would like to indicate the starting point of the graph - where the line started. This is my code
import numpy as np
from scipy.integrate import odeint
from numpy import sin, cos, pi, array
import matplotlib
from matplotlib import rcParams
import matplotlib.pyplot as plt
from pylab import figure, axes, title, show
import xlsxwriter
def deriv(z, t):
l = 0.3 #unextended length of the spring, in m
m = 1 #mass of the bob, in kg
k = 1 #spring constant, in Nm^-1
g = 9.81 #gravitational acceleration, in ms^-2
x, y, dxdt, dydt = z
dx2dt2 = (l+x)*(dydt)**2 - k/m*x + g*cos(y)
dy2dt2 = (-g*sin(y) - 2*(dxdt)*(dydt))/(l+x)
#equations of motion
return np.array([dxdt, dydt, dx2dt2, dy2dt2])
init = array([0.3, pi/2, 0.0, 2])
#initial conditions (x, y, xdot, ydot)
time = np.linspace(0, 100, 10000)
#time intervals (start, end, number of intervals)
sol = odeint(deriv, init, time)
#solving the equations of motion
x = sol[:,0]
y = sol[:,1]
l = 0.3 #unextended length of the spring, in m
n = (l+x) * sin(y)
u = -(l+x) * cos(y)
#converting x and y to Cartesian coordinates
plt.plot(n,u)
plt.xlabel('$n$ (m)')
plt.ylabel('$u$ (m)')
plt.title('$n$ versus $u$ for 'r'$\theta_0 = \frac{\pi}{2}+0.001$')
plt.show()
which generates this graph:
However, it is unclear where the line actually started (somewhere in the upper right, I think, near where it ended). Is there some way I can add a brightly coloured dot to the starting point not specific just to this graph (i.e. so I can reproduce in on other graphs with different conditions)?
Thank you!
Plotting the first point can be done by adding plt.plot(n[0], u[0], '*') to your code, see below.
Full documentation for the plot function (thanks for the comment mostlyoxygen), to get a better idea of how you can change the colour, size and shape of the dot.
from scipy.integrate import odeint
from numpy import array, linspace, sin, cos, pi, array
from matplotlib import rcParams
import matplotlib.pyplot as plt
def deriv(z, t):
l = 0.3 #unextended length of the spring, in m
m = 1 #mass of the bob, in kg
k = 1 #spring constant, in Nm^-1
g = 9.81 #gravitational acceleration, in ms^-2
x, y, dxdt, dydt = z
dx2dt2 = (l+x)*(dydt)**2 - k/m*x + g*cos(y)
dy2dt2 = (-g*sin(y) - 2*(dxdt)*(dydt))/(l+x)
#equations of motion
return array([dxdt, dydt, dx2dt2, dy2dt2])
init = array([0.3, pi/2, 0.0, 2])
#initial conditions (x, y, xdot, ydot)
time = linspace(0, 100, 10000)
#time intervals (start, end, number of intervals)
sol = odeint(deriv, init, time)
#solving the equations of motion
x = sol[:,0]
y = sol[:,1]
l = 0.3 #unextended length of the spring, in m
n = (l+x) * sin(y)
u = -(l+x) * cos(y)
#converting x and y to Cartesian coordinates
plt.plot(n,u)
plt.plot(n[0], u[0], '*')
plt.xlabel('$n$ (m)')
plt.ylabel('$u$ (m)')
plt.title('$n$ versus $u$ for 'r'$\theta_0 = \frac{\pi}{2}+0.001$')
plt.show()

odeint from scipy.integrate in Python giving wrong result?

I am trying to solve the ivp y'=-y-5 * exp(-t) * sin(5 t), y(0)=1, using the following code:
%pylab inline
%matplotlib inline
from scipy.integrate import odeint
def mif(t, y):
return -y-5*exp(-t)*sin(5*t)
tspan = np.arange(0, 3, 0.000001)
y0 = 1.0
y_result = odeint(mif, y0, tspan)
y_result = y_result[:, 0] # convert the returned 2D array to a 1D array
plt.figure()
plt.plot(tspan, y_result)
plt.show()
However, the plot I get is wrong, it does not match what I obtain, say, with Matlab or Mathematica. It is actually different from the following alternative integration:
from scipy.integrate import ode
# initialize the 4th order Runge-Kutta solver
solver = ode(mif).set_integrator('dop853')
# initial value
y0 = 1.0
solver.set_initial_value(y0, 0)
values = 1000
t = np.linspace(0.0001, 3, values)
y = np.zeros(values)
for ii in range(values):
y[ii] = solver.integrate(t[ii])[0] #z[0]=u
which does yield correct result. What am I doing wrong with the odeint?
The function arguments change between ode and odeint. For odeint you need
def mif(y, t):
and for ode
def mif(t, y):
e.g.
%pylab inline
%matplotlib inline
from scipy.integrate import odeint
def mif(t,y):
return y
tspan = np.arange(0, 3, 0.000001)
y0 = 0.0
y_result = odeint(mif, y0, tspan)
plt.figure()
plt.plot(tspan, y_result)
plt.show()
and
from scipy.integrate import ode
def mif(y, t):
return y
# initialize the 4th order Runge-Kutta solver
solver = ode(mif).set_integrator('dop853')
# initial value
y0 = 0.000000
solver.set_initial_value([y0], 0.0)
values = 1000
t = np.linspace(0.0000001, 3, values)
y = np.zeros(values)
for ii in range(values):
y[ii] = solver.integrate(t[ii]) #z[0]=u
plt.figure()
plt.plot(t, y)
plt.show()

Numerical ODE solving in Python

How do I numerically solve an ODE in Python?
Consider
\ddot{u}(\phi) = -u + \sqrt{u}
with the following conditions
u(0) = 1.49907
and
\dot{u}(0) = 0
with the constraint
0 <= \phi <= 7\pi.
Then finally, I want to produce a parametric plot where the x and y coordinates are generated as a function of u.
The problem is, I need to run odeint twice since this is a second order differential equation.
I tried having it run again after the first time but it comes back with a Jacobian error. There must be a way to run it twice all at once.
Here is the error:
odepack.error: The function and its Jacobian must be callable functions
which the code below generates. The line in question is the sol = odeint.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from numpy import linspace
def f(u, t):
return -u + np.sqrt(u)
times = linspace(0.0001, 7 * np.pi, 1000)
y0 = 1.49907
yprime0 = 0
yvals = odeint(f, yprime0, times)
sol = odeint(yvals, y0, times)
x = 1 / sol * np.cos(times)
y = 1 / sol * np.sin(times)
plot(x,y)
plt.show()
Edit
I am trying to construct the plot on page 9
Classical Mechanics Taylor
Here is the plot with Mathematica
In[27]:= sol =
NDSolve[{y''[t] == -y[t] + Sqrt[y[t]], y[0] == 1/.66707928,
y'[0] == 0}, y, {t, 0, 10*\[Pi]}];
In[28]:= ysol = y[t] /. sol[[1]];
In[30]:= ParametricPlot[{1/ysol*Cos[t], 1/ysol*Sin[t]}, {t, 0,
7 \[Pi]}, PlotRange -> {{-2, 2}, {-2.5, 2.5}}]
import scipy.integrate as integrate
import matplotlib.pyplot as plt
import numpy as np
pi = np.pi
sqrt = np.sqrt
cos = np.cos
sin = np.sin
def deriv_z(z, phi):
u, udot = z
return [udot, -u + sqrt(u)]
phi = np.linspace(0, 7.0*pi, 2000)
zinit = [1.49907, 0]
z = integrate.odeint(deriv_z, zinit, phi)
u, udot = z.T
# plt.plot(phi, u)
fig, ax = plt.subplots()
ax.plot(1/u*cos(phi), 1/u*sin(phi))
ax.set_aspect('equal')
plt.grid(True)
plt.show()
The code from your other question is really close to what you want. Two changes are needed:
You were solving a different ODE (because you changed two signs inside function deriv)
The y component of your desired plot comes from the solution values, not from the values of the first derivative of the solution, so you need to replace u[:,0] (function values) for u[:, 1] (derivatives).
This is the end result:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def deriv(u, t):
return np.array([u[1], -u[0] + np.sqrt(u[0])])
time = np.arange(0.01, 7 * np.pi, 0.0001)
uinit = np.array([1.49907, 0])
u = odeint(deriv, uinit, time)
x = 1 / u[:, 0] * np.cos(time)
y = 1 / u[:, 0] * np.sin(time)
plt.plot(x, y)
plt.show()
However, I suggest that you use the code from unutbu's answer because it's self documenting (u, udot = z) and uses np.linspace instead of np.arange. Then, run this to get your desired figure:
x = 1 / u * np.cos(phi)
y = 1 / u * np.sin(phi)
plt.plot(x, y)
plt.show()
You can use scipy.integrate.ode. To solve dy/dt = f(t,y), with initial condition y(t0)=y0, at time=t1 with 4th order Runge-Kutta you could do something like this:
from scipy.integrate import ode
solver = ode(f).set_integrator('dopri5')
solver.set_initial_value(y0, t0)
dt = 0.1
while t < t1:
y = solver.integrate(t+dt)
t += dt
Edit: You have to get your derivative to first order to use numerical integration. This you can achieve by setting e.g. z1=u and z2=du/dt, after which you have dz1/dt = z2 and dz2/dt = d^2u/dt^2. Substitute these into your original equation, and simply iterate over the vector dZ/dt, which is first order.
Edit 2: Here's an example code for the whole thing:
import numpy as np
import matplotlib.pyplot as plt
from numpy import sqrt, pi, sin, cos
from scipy.integrate import ode
# use z = [z1, z2] = [u, u']
# and then f = z' = [u', u''] = [z2, -z1+sqrt(z1)]
def f(phi, z):
return [z[1], -z[0]+sqrt(z[0])]
# initialize the 4th order Runge-Kutta solver
solver = ode(f).set_integrator('dopri5')
# initial value
z0 = [1.49907, 0.]
solver.set_initial_value(z0)
values = 1000
phi = np.linspace(0.0001, 7.*pi, values)
u = np.zeros(values)
for ii in range(values):
u[ii] = solver.integrate(phi[ii])[0] #z[0]=u
x = 1. / u * cos(phi)
y = 1. / u * sin(phi)
plt.figure()
plt.plot(x,y)
plt.grid()
plt.show()
scipy.integrate() does ODE integration. Is that what you are looking for?

scipy: Interpolating trajectory

I have a trajectory formed by a sequence of (x,y) pairs. I would like to interpolate points on this trajectory using splines.
How do I do this? Using scipy.interpolate.UnivariateSpline doesn't work because neither x nor y are monotonic. I could introduce a parametrization (e.g. length d along the trajectory), but then I have two dependent variables x(d) and y(d).
Example:
import numpy as np
import matplotlib.pyplot as plt
import math
error = 0.1
x0 = 1
y0 = 1
r0 = 0.5
alpha = np.linspace(0, 2*math.pi, 40, endpoint=False)
r = r0 + error * np.random.random(len(alpha))
x = x0 + r * np.cos(alpha)
y = x0 + r * np.sin(alpha)
plt.scatter(x, y, color='blue', label='given')
# For this special case, the following code produces the
# desired results. However, I need something that depends
# only on x and y:
from scipy.interpolate import interp1d
alpha_i = np.linspace(alpha[0], alpha[-1], 100)
r_i = interp1d(alpha, r, kind=3)(alpha_i)
x_i = x0 + r_i * np.cos(alpha_i)
y_i = x0 + r_i * np.sin(alpha_i)
plt.plot(x_i, y_i, color='green', label='desired')
plt.legend()
plt.show()
Using splprep you can interpolate over curves of any geometry.
from scipy import interpolate
tck,u=interpolate.splprep([x,y],s=0.0)
x_i,y_i= interpolate.splev(np.linspace(0,1,100),tck)
Which produces a plot like the one given, but only using the x and y points and not the alpha and r paramters.
Sorry about my original answer, I misread the question.

Categories

Resources