cannot create mpf from array error for Elliptic Integrals - python

I'm working on this code that requires the use of elliptic integrals to solve an equation as a function of time. Most of the code is pretty straight forward but it runs into an error on the final equation containing the elliptic integrals, reading "cannot create mpf from array". Not quite sure if there is an easy fix but any insight would be greatly appreciated. The code can be found below:
import matplotlib.pyplot as plt
import numpy as np
from scipy import integrate
from mpmath import *
G = 6.6743*10**(-11) #Gravitational Constant
M = 10*(1.988*10**30) #Mass of the Black Hole (kg)
m = 1*(1.988*10**30) #Mass of the Companion Star (kg)
Mt = M + m #Total Mass(kg)
q = m/M #Mass Ratio
c = 2.99792458*10**8 #Speed of Light (m/s)
Period = 10 #Orbital Period (Days)
P = Period*86400 #Orbital Period (Seconds)
phi = 0.01*(np.pi/(180)) #Inclination from Line-of-Sight
t0 = Period/2 #Pulse Mid-Point
a = ((P**2*G*(Mt))/(4*np.pi**2))**(1/3) #Semi-Major Axis
omega = ((G*(m + M))/a**3)**0.5 #Angular Velocity
vtran = a*omega #Transverse Velocity
Rs = (2*G*M)/c**2 #Schwarzschild Radius
Rein = (2*Rs*a)**0.5 #Einstein Radius
te = (Rein/vtran)/86400 #Einstein Time
u0 = (a/Rein)*phi #Closest Angular Approach
pstar = ((1*(6.957*10**8))/Rein)
t = np.linspace(0,P,2500000) #Time Vector
u = ((u0**2)+((((t/86400)-t0)/te))**2)**0.5 #Angular Separation
n = ((4*u*pstar)/(u + pstar)**2)
k = ((4*n)/(4 + (u - pstar)**2))**0.5
Amp = (1/(2*(np.pi)))*(((((u+pstar)/(pstar**2))*np.sqrt(4+(u-pstar)**2)*ellipe(k**2))-(((u-pstar)/(pstar**2))*(((8+u**2-pstar**2)/(np.sqrt(4+(u-pstar)**2)))*ellipk(k**2)))+(((4*(u-pstar)**2)/(pstar**2*(u+pstar)))*(((1+pstar**2)/(np.sqrt(4+(u-pstar)**2)))*ellippi(n,k**2)))))

The problem is specifically with mpmath.ellipe() and mpmath.ellipk() and mpath.ellippi().
The docs are here:
https://mpmath.org/doc/current/functions/elliptic.html#ellipe
(and similarly for ellipk and ellippi).
But in summary, as the comment has pointed out, mpmath.ellipe(*args)
Called with a single argument m, evaluates the Legendre complete elliptic integral of the second kind, E(m)
You have passed in a list.

Related

solve_ivp discards imaginary part of complex solution

I am computing a solution to the free basis expansion of the dirac equation for electron-positron pairproduction. For this i need to solve a system of equations that looks like this:
Equation for pairproduction, from Mocken at al.
EDIT: This has been solved by passing y0 as complex type into the solver. As is stated in this issue: https://github.com/scipy/scipy/issues/8453 I would definitely consider this a bug but it seems like it has gone under the rock for at least 4 years
for this i am using SciPy's solve_ivp integrator in the following way:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from scipy.integrate import solve_ivp
import scipy.constants as constants
#Impulse
px, py = 0 , 0
#physics constants
e = constants.e
m = constants.m_e # electronmass
c = constants.c
hbar = constants.hbar
#relativistic energy
E = np.sqrt(m**2 *c**4 + (px**2+py**2) * c**2) # E_p
#adiabatic parameter
xi = 1
#Parameter of the system
w = 0.840 #frequency in 1/m_e
N = 8 # amount of amplitudes in window
T = 2* np.pi/w
#unit system
c = 1
hbar = 1
m = 1
#strength of electric field
E_0 = xi*m*c*w/e
print(E_0)
#vectorpotential
A = lambda t,F: -E_0/w *np.sin(t)*F
def linearFenster2(t):
conditions = [t <=0, (t/w>=0) and (t/w <= T/2), (t/w >= T/2) and (t/w<=T*(N+1/2)), (t/w>=T*(N+1/2)) and (t/w<=T*(N+1)), t/w>=T*(N+1)]
funcs = [lambda t: 0, lambda t: 1/np.pi *t, lambda t: 1, lambda t: 1-w/np.pi * (t/w-T*(N+1/2)), lambda t: 0]
return np.piecewise(t,conditions,funcs)
#Coefficient functions
nu = lambda t: -1j/hbar *e*A(w*t,linearFenster2(w*t)) *np.exp(2*1j/hbar * E*t) *(px*py*c**2 /(E*(E+m*c**2)) + 1j*(1- c**2 *py**2/(E*(E+m*c**2))))
kappa = lambda t: 1j*e*A(t,linearFenster2(w*t))* c*py/(E * hbar)
#System to solve
def System(t, y, nu, kappa):
df = kappa(t) *y[0] + nu(t) * y[1]
dg = -np.conjugate(nu(t)) * y[0] + np.conjugate(kappa(t))*y[1]
return np.array([df,dg], dtype=np.cdouble)
def solver(tmin, tmax,teval=None,f0=0,g0=1):
'''solves the system.
#tmin: starttime
#tmax: endtime
#f0: starting percentage of already present electrons of positive energy usually 0
#g0: starting percentage of already present electrons of negative energy, usually 1, therefore full vaccuum
'''
y0=[f0,g0]
tspan = np.array([tmin, tmax])
koeff = np.array([nu,kappa])
sol = solve_ivp(System,tspan,y0,t_eval= teval,args=koeff)
return sol
#Plotting of windowfunction
amount = 10**2
t = np.arange(0, T*(N+1), 1/amount)
vlinearFenster2 = np.array([linearFenster2(w*a) for a in t ], dtype = float)
fig3, ax3 = plt.subplots(1,1,figsize=[24,8])
ax3.plot(t,E_0/w * vlinearFenster2)
ax3.plot(t,A(w*t,vlinearFenster2))
ax3.plot(t,-E_0 /w * vlinearFenster2)
ax3.xaxis.set_minor_locator(ticker.AutoMinorLocator())
ax3.set_xlabel("t in s")
ax3.grid(which = 'both')
plt.show()
sol = solver(0, 70,teval = t)
ts= sol.t
f=sol.y[0]
fsquared = 2* np.absolute(f)**2
plt.plot(ts,fsquared)
plt.show()
The plot for the window function looks like this (and is correct)
window function
however the plot for the solution looks like this:
Plot of pairproduction probability
This is not correct based on the papers graphs (and further testing using mathematica instead).
When running the line 'sol = solver(..)' it says:
\numpy\core\_asarray.py:102: ComplexWarning: Casting complex values to real discards the imaginary part
return array(a, dtype, copy=False, order=order)
I simply do not know why solve_ivp discard the imaginary part. Its absolutely necessary.
Can someone enlighten me who knows more or sees the mistake?
According to the documentation, the y0 passed to solve_ivp must be of type complex in order for the integration to be over the complex domain. A robust way of ensuring this is to add the following to your code:
def solver(tmin, tmax,teval=None,f0=0,g0=1):
'''solves the system.
#tmin: starttime
#tmax: endtime
#f0: starting percentage of already present electrons of positive energy usually 0
#g0: starting percentage of already present electrons of negative energy, usually 1, therefore full vaccuum
'''
f0 = complex(f0) # <-- added
g0 = complex(g0) # <-- added
y0=[f0,g0]
tspan = np.array([tmin, tmax])
koeff = np.array([nu,kappa])
sol = solve_ivp(System,tspan,y0,t_eval= teval,args=koeff)
return sol
I tried the above, and it indeed made the warning disappear. However, the result of the integration seems to be the same regardless.

Python Code Not Working Properly in Jupyter Notebook

I have this Python code that clearly works but not in Jupyter notebook for some reason. The code should return a graph like the one pictured here: Correct Plot
However, when I run the code in Jupyter notebook the graph looks nothing likes this Wrong Plot. Not sure what may be causing the discrepancy. The code is written below. Any advice would be greatly appreciated! Be warned in advance, the code takes about 17 mins to run
import matplotlib.pyplot as plt
import numpy as np
from scipy import integrate
G = 6.6743*10**(-11) #Gravitational Constant
M = 10*(1.988*10**30) #Mass of the Black Hole (kg)
m = 1*(1.988*10**30) #Mass of the Companion Star (kg)
Mt = M + m #Total Mass(kg)
q = m/M #Mass Ratio
c = 2.99792458*10**8 #Speed of Light (m/s)
Period = 10 #Orbital Period (Days)
P = Period*86400 #Orbital Period (Seconds)
phi = 0.001*(np.pi/(180)) #Inclination from Line-of-Sight
t0 = Period/2 #Pulse Mid-Point
a = ((P**2*G*(Mt))/(4*np.pi**2))**(1/3) #Semi-Major Axis
omega = ((G*(m + M))/a**3)**0.5 #Angular Velocity
vtran = a*omega #Transverse Velocity
Rs = (2*G*M)/c**2 #Schwarzschild Radius
Rein = (2*Rs*a)**0.5 #Einstein Radius
te = (Rein/vtran)/86400 #Einstein Time
u0 = (a/Rein)*phi #Closest Angular Approach
pstar = ((1*(6.957*10**8))/Rein)
t = np.linspace(0,P,15000) #Time Vector
u = ((u0**2)+((((t/86400)-t0)/te))**2)**0.5 #Angular Separation
U=lambda r:(1-(3/2)*((pstar**2-r**2)/pstar**2)**0.5)
gamma = 0.3
#bottom part funtion
pre_bottom=lambda r:r*(1-gamma*U(r))
#integrates bottom part
bottom_res,bottom_err=integrate.quadrature(pre_bottom,0,pstar,maxiter=100)
bottom=2*np.pi*bottom_res
#array for values
A=np.empty(len(u))
#def top part of function
pre_top=lambda r,th:(1-gamma*U(r))*(r*(u[i]**2 + r**2 - 2*u[i]*r*np.cos(th)+2))/((((u[i]**2 + r**2 - 2*u[i]*r*np.cos(th)))**0.5)*(((u[i]**2 + r**2 - 2*u[i]*r*np.cos(th)+4))**0.5))
#change upper bound on number of subineverals for integration
options={'limit':100}
#take integral of top for all time values
for i in range(len(u)):
top_res,top_err=integrate.nquad(pre_top,[[0,pstar],[0,(2*np.pi)]],opts=[options,options])
#get value of A
A[i]=top_res/bottom
plt.plot(t,A)
plt.xlim([426000, 438000])
plt.show()

GEKKO does not find optimal solution for a moon lander that does not go to zero height at zero speed

I'm trying to solve an optimal control problem where a person lands on the Moon, but has a device that can propel her upwards, via a control parameter, alpha. The objective of the problem is to find the minimal time in which the moon lander can reach the surface of the moon, at speed zero (all motion along the vertical axis).
Now, I have implemented the code using gekko, with python, and it works just fine if the person starts, for instance, 40m above the surface of the Moon, and reaches the surface (final height = 0m) at speed zero. However, if I modify the code to have the person start, say, from 50m above the surface and get to a final height of 10m, gekko always converges to a point of local infeasibility. I have tried many different final heights, and it only seems to work when I set it to 0.
Is it a gekko problem or am I overlooking something in my code?
I followed the ideas shown here: https://apmonitor.com/do/index.php/Main/MinimizeFinalTime
Here's my code:
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
m = GEKKO(remote=True)
m.solver_options = ['max_iter 500']
nt = 501
tm = np.linspace(0,1,nt)
m.time = tm
#variables
initialMass = m.Const(value=10.)
initialSpeed = m.Const(value=0.)
initialHeight = m.Const(value=40.)
finalHeight = m.Const(value=10.)
g = m.Const(value=1.625) #gravitational acceleration
k = m.Const(value=0.01)
power = m.Const(value=initialMass)
v = m.Var(value=initialSpeed) #position
h = m.Var(value=initialHeight) #height
mass = m.Var(value=initialMass, lb=0., ub=initialMass) #mass
#MV
alpha = m.MV(value=0.2, lb=0.2, ub=1.) #control variable
alpha.STATUS = 1
#FV
tf = m.FV(value=1., lb=0.1)
tf.STATUS = 1 # the value can be adjusted by the optimizer
p = np.zeros(nt)
p[-1] = 1.
final = m.Param(value=p)
#Equations
m.Equation(v.dt() == tf*(-g+2*alpha*g*power/mass))
m.Equation(h.dt() == tf*v)
m.Equation(mass.dt() == -tf*k*alpha)
m.Equation(h*final == finalHeight)
m.Equation(v*final == 0.)
m.Obj(tf)
m.options.IMODE = 6
m.solve(disp=True)
print('Solution')
print('Final time: '+str(tf.value[0]))
I managed to solve the problem. First, I added a lower bound for h, the height,
h = m.Var(value=initialHeight,lb=finalHeight)
and then I added an objective function to be minimized. Instead of
m.Equation(h*final == finalHeight)
now I have written
m.Minimize(final*(h+1)**2)
I followed the ideas presented here https://apmonitor.com/wiki/index.php/Apps/BrysonDenhamProblem

Using GEKKO to solve 2-D Heat Equations

I am basically trying to solve a dynamic second order partial differential equation using GEKKO. This is the equation for reference:
2-D Heat transfer equation.
Here, t is time, T is temperature, (k, rho, Cp,l e, sigma and Z) are all constants.
T has been inputted as an array (I think that is where the problem lies).
To solve the equation numerically, boundary conditions are required which were inputted as other equations.
This is the code:
import numpy as np
from gekko import GEKKO
m = GEKKO()
tf = 5*60*60
dt = int(tf/300)+1
m.time = np.linspace(0,tf,dt)
# Number of nodes
n = 41
# Length of domain
Lx = 1
Ly = Lx # square domain
x_div = np.linspace(0,Lx,n)
y_div = np.linspace(Ly,0,n)
[X, Y] = np.meshgrid(x_div, y_div)
# step size
dx = x_div[1] - x_div[0]
dy = y_div[1] - y_div[0]
# Temp. initialization
T = m.Var(np.ones((n,n))*290)
# Equation set-up
# Middle segments
for i in range(1,n-1):
for j in range(1,n-1):
m.Equations(T[i,j].dt() == (k/(rho*Cp)*((T[i+1,j]-2*T[i,j]+T[i-1,j])/dx**2 + (T[i,j+1]-2*T[i,j]+T[i,j-1])/dy**2))
+ (G_total - em*sigma*(T[i,j]**4-T_surr**4))/(rho*Cp*Z))
# Boundary Conditions
m.Equations(T[0,:]==310,
T[-1,:]==310,
T[1:-2,0]==315,
T[1:-2,-1]==315,
T[0,0]==312,
T[n,0]==312,
T[0,n]==312,
T[n,n]==312)
Basically, I am trying to solve this meshgrid consisting of temperatures. I get the following error: 'numpy.float64' object has no attribute 'dt'
If I just write T instead of T[i,j], I get this error: 'int' object is not subscriptable
My questions:
Is GEKKO able to solve such equations, that are 2 dimensional in nature? How do I go about it?
Are there any other cool libraries out there for this purpose? I need to be able to draw a contour plot having temperatures of the plate as time progresses; for which I need to solve the equations.
Thank you for your time.
Here is a solution with Lutz Lehmann's suggestion to use m.Array to define T.
import numpy as np
from gekko import GEKKO
m = GEKKO(remote=False)
tf = 5*60*60
dt = int(tf/300)+1
#m.time = np.linspace(0,tf,dt)
# for testing
m.time = [0,0.01,0.02,0.03]
# Number of nodes
n = 41
# Length of domain
Lx = 1
Ly = Lx # square domain
# Define for material
k = 1; rho = 8000; Cp = 500
G_total=1; em=1; sigma=1
T_surr=298; Z=1
x_div = np.linspace(0,Lx,n)
y_div = np.linspace(Ly,0,n)
[X, Y] = np.meshgrid(x_div, y_div)
# step size
dx = x_div[1] - x_div[0]
dy = y_div[1] - y_div[0]
# Temp. initialization
T = m.Array(m.Var,(n,n),value=290)
# Equation set-up
# Middle segments
for i in range(1,n-1):
for j in range(1,n-1):
m.Equation(rho*Cp*T[i,j].dt() == (k*\
((T[i+1,j]-2*T[i,j]+T[i-1,j])/dx**2 \
+ (T[i,j+1]-2*T[i,j]+T[i,j-1])/dy**2))
+ (G_total - em*sigma*(T[i,j]**4-T_surr**4))/Z)
# Boundary Conditions
m.Equations([T[0,i]==310 for i in range(1,n-1)])
m.Equations([T[-1,i]==310 for i in range(1,n-1)])
m.Equations([T[i,0]==315 for i in range(1,n-1)])
m.Equations([T[i,-1]==315 for i in range(1,n-1)])
m.Equations([T[0,0]==312, T[n-1,0]==312, \
T[0,n-1]==312, T[n-1,n-1]==312])
m.options.IMODE=7
m.solve(disp=False)
import matplotlib.pyplot as plt
plt.figure(figsize=(8,8))
for i in range(0,4):
for j in range(0,4):
plt.subplot(4,4,i*4+j+1)
plt.plot(m.time,T[i,j].value)
plt.savefig('heat.png',dpi=600)
plt.show()
There are additional examples of hyperbolic and parabolic PDEs with Gekko. The strength of Gekko is with optimization. For simulation, it may be better to use standard simulation methods for PDEs. Also, you may get faster solutions for simulation using IMODE=7.

Plot trajectory (python)

Hi I want to plot a rocket trajectory and it gives me this error: float() argument must be a string or a number, not 'function'. I want to plot the whole trajectory of a rocket that is losing mass to gain thrust. When the fuel ends, it describes a parabolic trajectory. Data of the problem can be changed. Those are the values I put, where mo is the initial mass of the rocket, q is the flow of gasses (how mass changes over time), g is gravitational acceleration, xo is the initia position, and t is time.
My code is:
import math
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
Data:
mo = 1500
q = 2.5
u = 6000
vo = 0
g = 9.8
x0 = 0
t = np.arange(0,1001)
t
velocity ecuation:
def v(t):
return vo + u*(math.log(mo/(mo-q*t))-g*t)
Position ecuation:
def x(t):
return x0 + vo*t - 0.5*g*t^2 + u*t*math.log(mo) + (u/q)*((mo - q*t)*math.log(mo - q*t) + q*t - mo*math.log(mo))
for t in (0,100):
plt.plot(x,t)
plt.grid()
Thanks for helping me, I really appreciate it.
There are four problems.
you need to call a function, not state it, x(t).
Don't use math when working with arrays. Instead use numpy.
A power in python is written as **, not ^.
Don't use negative values inside of logarithms.
The correct code may then look like:
import numpy as np
import matplotlib.pyplot as plt
mo = 1500
q = 2.5
u = 6000
vo = 0
g = 9.8
x0 = 0
t = np.arange(0,int(1500/2.5))
def v(t):
return vo + u*(np.log(mo/(mo-q*t))-g*t)
def x(t):
return x0 + vo*t - 0.5*g*t**2 + u*t*np.log(mo) + \
(u/q)*((mo - q*t)*np.log(mo - q*t) + q*t - mo*np.log(mo))
plt.plot(x(t),t)
plt.grid()
plt.show()
producing
It should be
plt.plot(x(t), t)
instead of
plt.plot(x, t)
What you're doing above is treating each (x,y) as a set of data. This isn't correct, since it's rather the collection of ((0, x(0)), (1, x(1))...) that is your set of data. A readable way is to have an array for your x-axis and y-axis:
x_ = np.arange(0,100)
y_ = x(x_) # available in newer versions of numpy.
plt.plot(x_, y_)

Categories