How to solve three quadratic differential equations in Python? - python-3.x

I've just started to use Python for scientific drawing to plot numerical solutions of differential equations. I know how to use modules to solve and plot single differential equations, but have no idea about systems of differential equation. How can I plot following coupled system?
My system of differential equation is:
dw/dx=y and
dy/dx=-a-3*H*y and
dz/dx=-H*(1+z)
that a = 0.1 and H=sqrt((1+z)**3+w+u**2/(2*a))
And my code is:
import numpy as N
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def model(w,y,z,x,H):
dwdx=y
dydx=-a-3*H*y
dzdx=-H*(1+z)
a=0.1
H=sqrt((1+z)**3+w+u**2/(2*a))
return [w,y,z,H]
z0=1100 #initial condition
w0=-2.26e-8
y0=-.38e-4
H0=36532.63
b=0
c=10000
x=N.arange(b,c,0.01)
y=odeint(model,y0,x) #f=Function name that returns derivative values at requested y and t values as dydt = f(y,t)
w=odeint(model,w0,x)
z=odeint(model,z0,x)
plt.plot(w,x)
plt.plot(y,x)
plt.plot(z,x)
plt.legend(loc='best')
plt.show()

General purpose ODE integrators expect the dynamical system reduced to an abstract first order system. Such a system has a state vector space and the differential equation provides velocity vectors for that space. Here the state has 3 scalar components, which gives a 3D vector as state. If you want to use the components separately, the first step in the ODE function is to extract these components from the state vector, and the last step is to compose the return vector from the derivatives of the components in the correct order.
Also, you need to arrange the computation steps in order of dependence
def model(u,t):
w, y, z = u
a=0.1
H=sqrt((1+z)**3+w+u**2/(2*a))
dwdx=y
dydx=-a-3*H*y
dzdx=-H*(1+z)
return [dwdx, dydx, dzdx]
and then call the integrator once with the combined initial state
u0 = [ w0, y0, z0]
u = odeint(model, u0, x)
w,y,z = u.T
Please also check the arguments of the plot function, the general scheme is plot(x,y).

Related

How to fix the issue of plotting a 2D sine wave in python

I want to generate 2D travelling sine wave. To do this, I've set the parameters for the plane wave and generate wave for any time instants like as follows:
import numpy as np
import random
import matplotlib.pyplot as plt
f = 10 # frequency
fs = 100 # sample frequency
Ts = 1/fs # sample period
t = np.arange(0,0.5, Ts) # time index
c = 50 # speed of wave
w = 2*np.pi *f # angular frequency
k = w/c # wave number
resolution = 0.02
x = np.arange(-5, 5, resolution)
y = np.arange(-5, 5, resolution)
dx = np.array(x); M = len(dx)
dy = np.array(y); N = len(dy)
[xx, yy] = np.meshgrid(x, y);
theta = np.pi / 4 # direction of propagation
kx = k* np.cos(theta)
ky = k * np.sin(theta)
So, the plane wave would be
plane_wave = np.sin(kx * xx + ky * yy - w * t[1])
plt.figure();
plt.imshow(plane_wave,cmap='seismic',origin='lower', aspect='auto')
that gives a smooth plane wave as shown in . Also, the sine wave variation with plt.figure(); plt.plot(plane_wave[2,:]) time is given in .
However, when I want to append plane waves at different time instants then there is some discontinuity arises in figure 03 & 04 , and I want to get rid of from this problem.
I'm new in python and any help will be highly appreciated. Thanks in advance.
arr = []
for count in range(len(t)):
p = np.sin(kx * xx + ky * yy - w * t[count]); # plane wave
arr.append(p)
arr = np.array(arr)
print(arr.shape)
pp,q,r = arr.shape
sig = np.reshape(arr, (-1, r))
print('The signal shape is :', sig.shape)
plt.figure(); plt.imshow(sig.transpose(),cmap='seismic',origin='lower', aspect='auto')
plt.xlabel('X'); plt.ylabel('Y')
plt.figure(); plt.plot(sig[2,:])
This is not that much a problem of programming. It has to do more with the fact that you are using the physical quantities in a somewhat unusual way. Your plots are absolutely fine and correct.
What you seem to have misunderstood is the fact that you are talking about a 2D problem with a third dimension added for time. This is by no means wrong but if you try to append the snapshot of the 2D wave side-by-side you are using (again) the x spatial dimension to represent temporal variations. This leads to an inconsistency of the use of that coordinate axis. Now, to make this more intuitive, consider the two time instances separately. Does it not coincide with your intuition that all points on the 2D plane must have different amplitudes (unless of course the time has progressed by a multiple of the period of the wave)? This is the case indeed. Thus, when you try to append the two snapshots, a discontinuity is exhibited. In order to avoid that you have to either use a time step equal to one period, which I believe is of no practical use, or a constant time step that will make the phase of the wave on the left border of the image in the current time equal to the phase of the wave on the right border of the image in the previous time step. Yet, this will always be a constant time step, alternating the phase (on the edges of the image) between the two said values.
The same applies to the 1D case because you use the two coordinate axes to represent the wave (x is the x spatial dimension and y is used to represent the amplitude). This is what can be seen in your last plot.
Now, what would be the solution you may ask. The solution is provided by simple inspection of the mathematical formula of the wave function. In 2D, it is a scalar function of three variables (that is, takes as input three values and outputs one) and so you need at least four dimensions to represent it. Alas, we can't perceive a fourth spatial dimension, but this is not a problem in your case as the output of the function is represented with colors. Then there are three dimensions that could be used to represent the temporal evolution of your function. All you have to do is to create a 3D array where the third dimension represents time and all 2D snapshots will be stored in the first two dimensions.
When it comes to visual representation of the results you could either use some kind of waterfall plots where the z-axis will represent time or utilize the fourth dimension we can perceive, time that is, to create an animation of the evolution of the wave.
I am not very familiar with Python, so I will only provide a generic naive implementation. I am sure a lot of people here could provide some simplification and/or optimisation of the following snippet. I assume that everything in your first two blocks of code is available so changes have to be done only in the last block you present
arr = np.zeros((len(xx), len(yy), len(t))) # Initialise the array to hold the temporal evolution of the snapshots
for i in range(len(t)):
arr[:, :, i] = np.sin(kx * xx + ky * yy - w * t[i])
# Below you can plot the figures with any function you prefer or make an animation out of it

Is there a logic error with my implementation of odeint?

I am getting the wrong solution and am unsure if odeint is the correct tool for solving this system of ODEs.
I am trying to model a simple first order chemical reaction by solving a system of ODEs. From a logic standpoint my functions are correct and I can solve this problem in MATLAB with little issue. I would like to also be able to do this work in python as well. I think odeint is the tool for the job but I could be wrong. My solution should not converge at independent variable = 10 every time but it always does regardless of inputs.
from matplotlib.pyplot import (plot,grid,xlabel,ylabel,show,legend)
import numpy as np
from scipy.integrate import odeint
wght= np.linspace(0,20)
# reaction is A -> B
def PBR(fun,W):
X,y = fun
P_0=20;#%bar
v_0=5; #%m^3/min
y_A0=1; #unitless
k=.005; #m^3/kg/min
alpha=0.1; #1/kg
epi=.13; #unitless
R=8.314; #J/mol/K
F_A0= .5 ;#mol/min
ra = -k *y*(1-X)/(1+epi*X)
dX = (-ra)/F_A0
dy = -alpha*(1+epi*X)/(2*y)
return [dX,dy]
X0 = 0.0
y0 = 1.0
sol = odeint(PBR, [X0, y0],wght)
plot(wght, sol[:, 0], 'b', label='X')
plot(wght, sol[:, 1], 'g', label='y')
legend(loc='best')
xlabel('W')
grid()
show()
print(sol)
Output graph
Your graphic is not reproducible, in python 3.7, scipy 1.4.1, I get a divergence to practically infinity at around 12.5, and after cleaning the work space, both graphs moving to zero after time 10.
The problem is your division by 2*y in the second equation. In effect that means that y is the square root of some other function, and that type of function behaves badly numerically at small values as the tangent becomes vertical, changing to zero shortly after.
However, one can desingularize the division in a simple way
dy = -alpha*(1+epi*X)*y/(1e-10+2*y*y)
or in a more complicated way that leaves the solution far away from zero really unchanged
dy = -alpha*(1+epi*X)*y/(y*y+max(1e-12,y*y))
resulting in the graph
If that is still not the expected behavior, then something else is different in your ODE system relative to the Matlab version.
That the singular point is at about w=10 depends almost completely on alpha=0.1. As epi is small and X is initially small, the second equation is close to
d(y^2)/dW = -alpha ==> y^2 = 1 - alpha*W
and this hits zero at about W=10 where the solution would have to end as the square of y can not take negative values.

Choice of IMODE in gekko optimisation problems

I'm seeing here that imode=3 is equivalent to the steady-state simulation (which I guess imode=2) except that additional degrees of freedom are allowed.
How do I decide to use imode=3 instead of imode=2?
I'm doing optimization using imode=2 where I'm defining variables calculated by solver to meet constraint using m.Var & other using m.Param. What changes I need to do in variables to use imode=3 ?
Niladri,
IMODE 2 is for steady state problems with multiple data points.
Here is an example:
from gekko import GEKKO
import numpy as np
xm = np.array([0,1,2,3,4,5])
ym = np.array([0.1,0.2,0.3,0.5,1.0,0.9])
m = GEKKO()
m.x = m.Param(value=np.linspace(-1,6))
m.y = m.Var()
m.options.IMODE=2
m.cspline(m.x,m.y,xm,ym)
m.solve(disp=False)
This is a Cubic Spline approximation with multiple data points. When you switch to IMODE 3, it is very similar but it only considers one instance of your model. All of the value properties should only have 1 value such as when you optimize the Cubic spline to find the maximum value.
p = GEKKO()
p.x = p.Var(value=1,lb=0,ub=5)
p.y = p.Var()
p.cspline(p.x,p.y,xm,ym)
p.Obj(-p.y)
p.solve(disp=False)
Here is additional information on IMODE:
https://apmonitor.com/wiki/index.php/Main/OptionApmImode
https://apmonitor.com/wiki/index.php/Main/Modes
https://gekko.readthedocs.io/en/latest/imode.html
Best regards,
John Hedengren

Point of change in the slope of the signal

Following is the graph which you get on plotting the given data points. There is an exact point where the slope changes before giving a stable line. What we have done is obtaining the first derivative and looking out for a point where the slope makes transition from positive to negative values. But for many data points,such a transition was not found. So is there a better method to do this?
How do you find that point (marked as a red circle in the graph) using slope in python?
graph of the signal
[-0.0006029533498891765, -0.0005180378648295125, -0.0004122940532457625, -0.0002953349889182749, -0.00018692087906219124, -0.00010093727469359659, -4.699724959278395e-05, -1.602178963390488e-05, -5.340596544722853e-07, 9.079014125876195e-06, 1.976020721514149e-05, 3.0441400304406785e-05, 3.845229512135229e-05, 4.3258832011533466e-05, 4.432695132046416e-05, 4.592913028383938e-05, 5.020160751956215e-05, 5.6076263718660146e-05, 5.9814681299896755e-05, 6.195091991774426e-05, 6.408715853560565e-05, 6.568933749899475e-05, 6.889369542577295e-05, 7.209805335256503e-05, 7.370023231594025e-05]
This problem requires some additional specification. The core question is what do you mean by "a stable line"? One potential definition is "consecutive line segments with the exact same slope." However, since the slope of each line segment is likely not precisely similar, this may not be helpful.
Another potential definition is "consecutive line segments whose slopes differ by less than a defined cut-off value." Whenever we're talking about a difference of slopes, we want to look at the second derivative. We can identify transition points by finding where the absolute value of the second derivative is less than the chosen cut-off value at each point.
The question then becomes, what cut-off value is acceptable? Since you want a method that classifies the 10th point as a transition point, I'll use that to inform the decision.
Here is code that defines a cut-off value and uses that to identify points with nearly similar slopes:
data = [-0.0006029533498891765, -0.0005180378648295125, -0.0004122940532457625, -0.0002953349889182749, -0.00018692087906219124, -0.00010093727469359659, -4.699724959278395e-05, -1.602178963390488e-05, -5.340596544722853e-07, 9.079014125876195e-06, 1.976020721514149e-05, 3.0441400304406785e-05, 3.845229512135229e-05, 4.3258832011533466e-05, 4.432695132046416e-05, 4.592913028383938e-05, 5.020160751956215e-05, 5.6076263718660146e-05, 5.9814681299896755e-05, 6.195091991774426e-05, 6.408715853560565e-05, 6.568933749899475e-05, 6.889369542577295e-05, 7.209805335256503e-05, 7.370023231594025e-05]
# Import required libraries
import numpy as np
import matplotlib.pyplot as plt
# Generate figure
plt.figure()
plt.subplot(2,1,1)
plt.title('Data')
plt.plot(data)
plt.scatter(np.arange(len(data)), data)
plt.scatter(9, data[9], c='r') # Identify 10th point
plt.subplot(2,1,2)
plt.title('First Derivative')
deriv1 = data - np.roll(data, -1) # Use simple difference to compute the derivative
deriv1 = deriv1[0:-1] # Remove the last point
plt.plot(deriv1)
plt.scatter(np.arange(len(deriv1)), deriv1)
plt.scatter(9, deriv1[9], c='r') # Identify 10th point
plt.tight_layout()
# Approximate second derivative
deriv2 = deriv1 - np.roll(deriv1, -1) # Use simple difference to compute the derivative
deriv2 = deriv2[0:-1] # Remove the last point
# Plot data
plt.figure()
plt.subplot(2,1,1)
plt.title('Second Derivative')
x = np.arange(len(deriv2))
plt.plot(deriv2)
plt.scatter(x, deriv2)
plt.scatter(9, y[9], c='r') # Identify 10th point
plt.subplot(2,1,2)
plt.title('Absolute Value of Second Derivative')
y = np.abs(deriv2)
plt.plot(x, y)
plt.scatter(x, y)
plt.scatter(9, y[9], c='r') # Identify 10th point
# Correctly scale y axis
diff = max(y) - min(y)
scale = 0.1*diff
plt.ylim(min(y)-scale, max(y)+scale)
# Define cutoff value
cutoff = 1e-17
# Identify points where abs(deriv2) < cutoff
idx_filter = y <= cutoff
plt.axhline(y = cutoff, c='r', linestyle='--', alpha=0.5)
plt.scatter(x[idx_filter], y[idx_filter], s=200, edgecolor='r', facecolor = '')
plt.tight_layout()
As it turns out, there ARE two line segments with precisely the same slope. The 10th point identifies the start of them. The following code finds that transition point concisely, and should work for data with exactly one such point. It can be adapted to find multiple transition points if needed.
# Compute the first derivative
deriv = data - np.roll(data, -1) # Use simple difference to compute the derivative
deriv = deriv[0:-1] # Remove the last point
# Compute the second derivative
deriv2 = deriv - np.roll(deriv, -1) # Use simple difference to compute the derivative
deriv2 = deriv2[0:-1] # Remove the last point
# Define cutoff value
cutoff = 1e-17
# Identify points where abs(deriv2) < cutoff
idx_filter = y <= cutoff
x_transition = int(x[idx_filter][0])
y_transition = data[x_transition]
print('Transition Point Index: '+str(x_transition))
print('Transition Point Value: '+str(y_transition))
print('Difference in slopes: {:.20f}'.format(deriv2[x_transition]))
>>> Transition Point Index: 9
>>> Transition Point Value: 9.079014125876195e-06
>>> Difference in slopes: 0.00000000000000000000
Since there were no x-values provided, the derivative approximation is simplified by assuming that the x-distance between each successive point is 1. Addition of x-data would require slight modifications to the derivative approximations, and another derivative approximation method may be more appropriate if the data is unevenly distributed along the x-axis.

Defining new kernels to use in approximate_kernel.py

I am trying to test a new kernel method in Kernel Ridge Regression and want to do this by implementing the Fastfood transformation (https://arxiv.org/abs/1408.3060). I can write a function which computes this transform but it isn't playing nicely with the kernel ridge regression function in sklearn. As a result I have gone to the source code for sklearn kernel ridge regression (https://insight.io/github.com/scikit-learn/scikit-learn/blob/master/sklearn/kernel_ridge.py) and approximate_kernel.py (https://insight.io/github.com/scikit-learn/scikit-learn/blob/master/sklearn/kernel_approximation.py) in order to try and define this new kernel as a class definition in approximate_kernel.py. The problem is that I have no idea how to convert my construction to something which will work in the approximate_kernel KernelRidge programs. Would anybody be able to advise how best to do this please?
My construction for the fastfood transform is:
def fastfood_product(d):
'''
Constructs the fastfood matrix composition V = const*S*H*G*Pi*B where
S is a scaling matrix
H is Hadamard transform
G is a diagonal random Gaussian
Pi is a permutation matrix
B is a diagonal Rademacher matrix.
Inputs: n - dimensionality of the feature vectors for the kernel.
must be a power of two and be divisible by d. If not then can
pad the matrix with zeros but for simplicity assume this condition
is always met.
Output: V'''
S = np.zeros(shape=(d,d))
G = np.zeros_like(S)
B = np.zeros_like(S)
H = hadamard(d)
Pi = np.eye(d)
np.random.shuffle(Pi) # Permutation matrix
# Construct the simple matrices
np.fill_diagonal(B, 2*np.random.randint(low=0,high=2,size=(d,1)).flatten() - 1)
np.fill_diagonal(G, np.random.randn(G.shape[0],1)) # May want to change standard normal to arbitrary which will affect the scaling for V
np.fill_diagonal(S, np.linalg.norm(G,'fro')**(-0.5))
#print('Shapes of B {}, S {}, G {}, H{}, Pi {}'.format(B.shape, S.shape, G.shape, H.shape, Pi.shape))
V = d**(-0.5)*S.dot(H).dot(G).dot(Pi).dot(H).dot(B)
return V
def fastfood_feature_map(X, n):
'''Given a matrix X of data compute the fastfood transformation and feature mapping.
Input: X data of dimension d by m, n = the number of nonlinear basis functions to choose (power of 2)
Outputs: Phi - matrix of random features for fastfood kernel approximation.
Usage: Phi must be transposed for computation in the kernel ridge regression.
i.e solve ||Phi.T * w - b || + regulariser
Comments: This only uses a standard normal distribution but this could
be altered with different hyperparameters.'''
d,m = A.shape
V = fastfood_product(d)
Phi = n**(-0.5)*np.exp(1j*np.dot(V, X))
return Phi
I think the imports numpy as np and from linalg import hadamard will be necessary for the above.

Resources