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()
Related
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.
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
I am solving an ODE for an harmonic oscillator numerically with Python. When I add a driving force it makes no difference, so I'm guessing something is wrong with the code. Can anyone see the problem? The (h/m)*f0*np.cos(wd*i) part is the driving force.
import numpy as np
import matplotlib.pyplot as plt
# This code solves the ODE mx'' + bx' + kx = F0*cos(Wd*t)
# m is the mass of the object in kg, b is the damping constant in Ns/m
# k is the spring constant in N/m, F0 is the driving force in N,
# Wd is the frequency of the driving force and x is the position
# Setting up
timeFinal= 16.0 # This is how far the graph will go in seconds
steps = 10000 # Number of steps
dT = timeFinal/steps # Step length
time = np.linspace(0, timeFinal, steps+1)
# Creates an array with steps+1 values from 0 to timeFinal
# Allocating arrays for velocity and position
vel = np.zeros(steps+1)
pos = np.zeros(steps+1)
# Setting constants and initial values for vel. and pos.
k = 0.1
m = 0.01
vel0 = 0.05
pos0 = 0.01
freqNatural = 10.0**0.5
b = 0.0
F0 = 0.01
Wd = 7.0
vel[0] = vel0 #Sets the initial velocity
pos[0] = pos0 #Sets the initial position
# Numerical solution using Euler's
# Splitting the ODE into two first order ones
# v'(t) = -(k/m)*x(t) - (b/m)*v(t) + (F0/m)*cos(Wd*t)
# x'(t) = v(t)
# Using the definition of the derivative we get
# (v(t+dT) - v(t))/dT on the left side of the first equation
# (x(t+dT) - x(t))/dT on the left side of the second
# In the for loop t and dT will be replaced by i and 1
for i in range(0, steps):
vel[i+1] = (-k/m)*dT*pos[i] + vel[i]*(1-dT*b/m) + (dT/m)*F0*np.cos(Wd*i)
pos[i+1] = dT*vel[i] + pos[i]
# Ploting
#----------------
# With no damping
plt.plot(time, pos, 'g-', label='Undampened')
# Damping set to 10% of critical damping
b = (freqNatural/50)*0.1
# Using Euler's again to compute new values for new damping
for i in range(0, steps):
vel[i+1] = (-k/m)*dT*pos[i] + vel[i]*(1-(dT*(b/m))) + (F0*dT/m)*np.cos(Wd*i)
pos[i+1] = dT*vel[i] + pos[i]
plt.plot(time, pos, 'b-', label = '10% of crit. damping')
plt.plot(time, 0*time, 'k-') # This plots the x-axis
plt.legend(loc = 'upper right')
#---------------
plt.show()
The problem here is with the term np.cos(Wd*i). It should be np.cos(Wd*i*dT), that is note that dT has been added into the correct equation, since t = i*dT.
If this correction is made, the simulation looks reasonable. Here's a version with F0=0.001. Note that the driving force is clear in the continued oscillations in the damped condition.
The problem with the original equation is that np.cos(Wd*i) just jumps randomly around the circle, rather than smoothly moving around the circle, causing no net effect in the end. This can be best seen by plotting it directly, but the easiest thing to do is run the original form with F0 very large. Below is F0 = 10 (ie, 10000x the value used in the correct equation), but using the incorrect form of the equation, and it's clear that the driving force here just adds noise as it randomly moves around the circle.
Note that your ODE is well behaved and has an analytical solution. So you could utilize sympy for an alternate approach:
import sympy as sy
sy.init_printing() # Pretty printer for IPython
t,k,m,b,F0,Wd = sy.symbols('t,k,m,b,F0,Wd', real=True) # constants
consts = {k: 0.1, # values
m: 0.01,
b: 0.0,
F0: 0.01,
Wd: 7.0}
x = sy.Function('x')(t) # declare variables
dx = sy.Derivative(x, t)
d2x = sy.Derivative(x, t, 2)
# the ODE:
ode1 = sy.Eq(m*d2x + b*dx + k*x, F0*sy.cos(Wd*t))
sl1 = sy.dsolve(ode1, x) # solve ODE
xs1 = sy.simplify(sl1.subs(consts)).rhs # substitute constants
# Examining the solution, we note C3 and C4 are superfluous
xs2 = xs1.subs({'C3':0, 'C4':0})
dxs2 = xs2.diff(t)
print("Solution x(t) = ")
print(xs2)
print("Solution x'(t) = ")
print(dxs2)
gives
Solution x(t) =
C1*sin(3.16227766016838*t) + C2*cos(3.16227766016838*t) - 0.0256410256410256*cos(7.0*t)
Solution x'(t) =
3.16227766016838*C1*cos(3.16227766016838*t) - 3.16227766016838*C2*sin(3.16227766016838*t) + 0.179487179487179*sin(7.0*t)
The constants C1,C2 can be determined by evaluating x(0),x'(0) for the initial conditions.
I have the following code. This code is simulation of orbiting objects around other objects, E.g. Solar system. As you run it, the objects orbit in circular trajectory.
import math
from vpython import *
lamp = local_light(pos=vector(0,0,0), color=color.yellow)
# Data in units according to the International System of Units
G = 6.67 * math.pow(10,-11)
# Mass of the Earth
ME = 5.973 * math.pow(10,24)
# Mass of the Moon
MM = 7.347 * math.pow(10,22)
# Mass of the Mars
MMa = 6.39 * math.pow(10,23)
# Mass of the Sun
MS = 1.989 * math.pow(10,30)
# Radius Earth-Moon
REM = 384400000
# Radius Sun-Earth
RSE = 149600000000
RMS = 227900000000
# Force Earth-Moon
FEM = G*(ME*MM)/math.pow(REM,2)
# Force Earth-Sun
FES = G*(MS*ME)/math.pow(RSE,2)
# Force Mars-Sun
FEMa = G*(MMa*MS)/math.pow(RMS,2)
# Angular velocity of the Moon with respect to the Earth (rad/s)
wM = math.sqrt(FEM/(MM * REM))
# Velocity v of the Moon (m/s)
vM = wM * REM
print("Angular velocity of the Moon with respect to the Earth: ",wM," rad/s")
print("Velocity v of the Moon: ",vM/1000," km/s")
# Angular velocity of the Earth with respect to the Sun(rad/s)
wE = math.sqrt(FES/(ME * RSE))
# Angular velocity of the Mars with respect to the Sun(rad/s)
wMa = math.sqrt(FEMa/(MMa * RMS))
# Velocity v of the Earth (m/s)
vE = wE * RSE
# Velocity v of the Earth (m/s)
vMa = wMa * RMS
print("Angular velocity of the Earth with respect to the Sun: ",wE," rad/s")
print("Velocity v of the Earth: ",vE/1000," km/s")
# Initial angular position
theta0 = 0
# Position at each time
def positionMoon(t):
theta = theta0 + wM * t
return theta
def positionMars(t):
theta = theta0 + wMa * t
return theta
def positionEarth(t):
theta = theta0 + wE * t
return theta
def fromDaysToS(d):
s = d*24*60*60
return s
def fromStoDays(s):
d = s/60/60/24
return d
def fromDaysToh(d):
h = d * 24
return h
# Graphical parameters
print("\nSimulation Earth-Moon-Sun motion\n")
days = 365
seconds = fromDaysToS(days)
print("Days: ",days)
print("Seconds: ",seconds)
v = vector(384,0,0)
E = sphere(pos = vector(1500,0,0), color = color.blue, radius = 60, make_trail=True)
Ma = sphere(pos = vector(2300,0,0), color = color.orange, radius = 30, make_trail=True)
M = sphere(pos = E.pos + v, color = color.white,radius = 10, make_trail=True)
S = sphere(pos = vector(0,0,0), color = color.yellow, radius=700)
t = 0
thetaTerra1 = 0
dt = 5000
dthetaE = positionEarth(t+dt)- positionEarth(t)
dthetaM = positionMoon(t+dt) - positionMoon(t)
dthetaMa = positionMars(t+dt) - positionMars(t)
print("delta t:",dt,"seconds. Days:",fromStoDays(dt),"hours:",fromDaysToh(fromStoDays(dt)),sep=" ")
print("Variation angular position of the Earth:",dthetaE,"rad/s that's to say",degrees(dthetaE),"degrees",sep=" ")
print("Variation angular position of the Moon:",dthetaM,"rad/s that's to say",degrees(dthetaM),"degrees",sep=" ")
while t < seconds:
rate(500)
thetaEarth = positionEarth(t+dt)- positionEarth(t)
thetaMoon = positionMoon(t+dt) - positionMoon(t)
thetaMars = positionMars(t+dt) - positionMars(t)
# Rotation only around z axis (0,0,1)
E.pos = rotate(E.pos,angle=thetaEarth,axis=vector(0,1,0))
Ma.pos = rotate(Ma.pos,angle=thetaMars,axis=vector(0,1,0))
v = rotate(v,angle=thetaMoon,axis=vector(0,1,0))
M.pos = E.pos + v
t += dt
I am wondering How to change the path of orbit to elliptical?
I have tried several ways but I could not manage to find any solution.
Thank you.
Thank you
This seems like more of a physics issue as opposed to a programming issue. The problem is that you are assuming that each of the orbits are circular when calculating velocity and integrating position linearly (e.g v * dt). This is not how you would go about calculating the trajectory of an orbiting body.
For the case of simplicity, we will assume all the masses are point masses so there aren't any weird gravity gradients or attitude dynamics to account for.
From there, you can refer to this MIT page. (http://web.mit.edu/12.004/TheLastHandout/PastHandouts/Chap03.Orbital.Dynamics.pdf) on orbit dynamics. On the 7th page, there is an equation relating the radial position from your centerbody as a function of a multitude of orbital parameters. It seems like you have every parameter except the eccentricity of the orbit. You can either look that up online or calculate it if you have detailed ephemeral data or apoapsis/periapsis information.
From that equation, you will see a phi - phi_0 term in the denominator. That is colloquially known as the true anomaly of the satellite. Instead of time, you would iterate on this true anomaly parameter from 0 to 360 to find your radial distance, and from true anomaly, inclination, right angle to the ascending node, and the argument of periapses, you can find the 3D cartesian coordinates at a specific true anomaly.
Going from true anomaly is a little less trivial. You will need to find the eccentric anomaly and then the mean anomaly at each eccentric anomaly step. You now have mean anomaly as a function of time. You can linearly interpolate between "nodes" at which you calculate the position with v * dt. You can calculate the velocity from using the vis-viva equation and dt would be the difference between the calculated time steps.
At each time step you can update the satellite's position in your python program and it will properly draw your trajectories.
For more information of the true anomaly, wikipedia has a good description of it: https://en.wikipedia.org/wiki/True_anomaly
For more information about orbital elements (which are needed to convert from radial position to cartesian coordinates): https://en.wikipedia.org/wiki/Orbital_elements
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_)