Ordinary differential equation - Odeint with unknown initial value in python, jupyter - python

I'm trying to solve ODE with odeint in python. I'm working on a physics project.
My goal is to calculate the direction and angle of trajectory in a spherical coordinate when I put the speed of matter, my location, landing point.
for example, when I put parameter - 800m/s, (S50° E135°)(my location), 10km to the south(landing point). then the result says like this - shoot 180°(0° is north), the elevation of 37°.
I can calculate the trajectory when I know the initial value, I can calculate the result and it's trajectory but I can't know what to do to complete my object.
G=6.673*10**(-11) # gravity constant
M=5.972*10**(24) # mass of earth
R=6374916 # radius of earth
w=7.3*10**-5 # angular velocity of earth's ratation
L=np.pi*7/9 # my longitude
l=float(input('Input your latitude(-90~90):'))
m=float(input('Input direction(0~360):'))
d=float(input('How far do you want to fire?(In meter):'))
v=int(input('How fast your matter?(In m/s):'))
dt=l+d*np.sin(m/180*np.pi)/R #target latitude
dp=L+d*np.cos(m/180*np.pi)/R #target longitude
Enter some parameter so far
def nodrag(y,t):
dydt0=y[1]
dydt1=y[0]*(y[3]**2)+y[0]*(y[5]**2)*(np.sin(y[2])**2)-G*M/(y[0]**2)
dydt2=y[3]
dydt3=-2*y[1]/y[0]*y[3]+(y[5]**2)*np.sin(y[2])*np.cos(y[2])
dydt4=y[5]
dydt5=-2*y[1]/y[0]*y[5]-2*y[3]*y[5]*np.cos(y[2])/np.sin(y[2])
return [dydt0,dydt1,dydt2,dydt3,dydt4,dydt5]
rlist=[]
thlist=[]
pilist=[]
ddp=[]
def ODE(azimuth,angle):
yini=np.array([R,v*np.sin(angle),np.pi/2-l*np.pi/180,-v*np.cos(angle)*np.cos(azimuth)/R,np.pi*135/180,w+v*np.cos(angle)*np.sin(azimuth)/R])
t=np.linspace(0,v,v*1000)
result=odeint(nodrag,yini,t)
r=result[:,0] # value of radius
th=result[:,2] # value of theta
pi=result[:,4] # value of pi
rr=list(r)
tt=list(th)
pp=list(pi) # change array to list
for i in range (1,v*1000):
if rr[i]<R:
number=i
break # find time when it reach r=R again
for i in range (number,v*1000):
tt.pop(-1)
pp.pop(-1) # remove extra
thlist.append(tt[-1])
pilist.append(pp[-1]) # save last values (r=R)
move=dp+w*t[number] # displacement of landing point of pi because of rotation of the earth
ddp.append(move)
So far soving ODE, my next part is brute force
min=np.pi*2
for i in range (0,360):
i=i/180*np.pi
for j in range (0,91):
j=j/180*np.pi
ODE(i,j) #calculate all directions
for k in range (0,32760):
compare=np.arccos(np.sin(dt)*np.sin(thlist[k])*np.cos(ddp[k]-pilist[k])+np.cos(dt)*np.cos(thlist[k]))
if compare<=min:
min=compare
azi=k//360
ang=k-azi*360
This code is quite inefficient. It requires about 40minutes
This is an example of my code - when I put some initial value.
but If I put an unknown value in the initial value, odeint does not work. What should I do?
When I get to know how to proceed, I'm going to iterate with the error.

You have a problem of the form that you know posA and posB, and a length constraint v0 for velA. The time to go from A to B is unknown. You have a function derivs (to stay generic, or nodrag in your case) to compute the derivative of combined state of pos and vel from physical principles.
With this you can set up the boundary value solver over the interval [0,1], scaling with the parameter T to get the real interval [0,T] with variable end point. Consequently, if u(s)=y(T*s) the derivative is u'(s)=T*y'(T*s)=T*derivs(T*s,u(s)).
def bvp_ode(s,u,T): return T*np.asarray(derivs(s*T,u))
def bvp_bc(uA,uB,T):
pA, vA=uA[:3],uA[3:] # or uA[0::2],uA[1::2]
pB, vB=uB[:3],uB[3:] # or uB[0::2],uB[1::2]
return np.concatenate(pA-posA, pB-posB, [sum(velA**2)-v0**2])
s_init = [0,1]
u_init = np.asarray([np.concatenate(pos,[0,0,0]) for pos in (posA, posB) ]).T
T = 100 # or dist(posA,posB)/v0
res = solve_bvp(bvp_ode, bvp_bc, s_init, u_init, p=[T])
print(res.message)
T=res.p[0]
velA = res.y[3:,0]
print("T=",T,", velA=",velA)

Related

Underestimation of f(x) by using a piecewise linear function

I am trying to check if there is any Matlab/Python procedure to underestimate f(x) by using a piecewise linear function g(x). That is g(x) needs to be less or equal to, f(x). See the picture and code below. Could you please help to modify this code to find how to underestimate this function?
x = 0.000000001:0.001:1;
y = abs(f(x));
%# Find section sizes, by using an inverse of the approximation of the derivative
numOfSections = 5;
totalRange = max(x(:))-min(x(:));
%# The relevant nodes
xNodes = x(1) + [ 0 cumsum(sectionSize)];
yNodes = abs(f(xNodes));
figure;plot(x,y);
hold on;
plot (xNodes,yNodes,'r');
scatter (xNodes,yNodes,'r');
legend('abs(f(x))','adaptive linear interpolation');
This approach is based on Luis Mendo's comment. The idea is the following:
Select a number of points from the original curve, your final piecewise linear curve will pass through these points
For each point calculate the equation of the tangent to the original curve. Because your graph is convex, the tangents of consecutive points in your sample will intersect below the curve
Calculate, for each set of consecutive tangents, the x-coordinate of the point of intersection. Use the equation of the tangent to calculate the corresponding y-coordinate
Now, after reordering the points, this gives you a piecewise linear approximation with the constraints you want.
h = 0.001;
x = 0.000000001:h:1;
y = abs(log2(x));
% Derivative of function on all the points
der = diff(y)/h;
NPts = 10; % Number of sample points
% Draw the index of the points by which the output will pass at random
% Still make sure you got first and last point
idx = randperm(length(x)-3,NPts-2);
idx = [1 idx+1 length(x)-1];
idx = sort(idx);
x_pckd = x(idx);
y_pckd = y(idx);
der_pckd = der(idx);
% Use obscure math to calculate the points of intersection
xder = der_pckd.*x_pckd;
x_2add = -(diff(y_pckd)-(diff(xder)))./diff(der_pckd);
y_2add = der_pckd(1:(end-1)).*(x_2add-(x_pckd(1:(end-1))))+y_pckd(1:(end-1));
% Calculate the error as the sum of the errors made at the middle points
Err_add = sum(abs(y_2add-interp1(x,y,x_2add)));
% Get final x and y coordinates of interpolant
x_final = [reshape([x_pckd(1:end-1);x_2add],1,[]) x_pckd(end)];
y_final = [reshape([y_pckd(1:end-1);y_2add],1,[]) y_pckd(end)];
figure;
plot(x,y,'-k');
hold on
plot(x_final,y_final,'-or')
You can see in my code that the points are drawn at random. If you want to do some sort of optimization (e.g. what is the set of points that minimizes the error), you can just run this a high amount of time and keep track of the best contender. For example, 10000 random draws see the rise of this guy:

How to offset the error in calculating cartesian coordinates from polar coordinates

I'm currently trying to develop a to-scale model of the universe using pygame. At the moment, when I'm calculating the x, y positions of the planets w.r.t. the sun, the planets are slowly falling towards the sun, despite only using equations for position based on the distance and angle of the planet (no force).
Here is the code snippet for calculating distance from a given star currently:
def d_obj(self, reference):
x_diff_sq = pow(self.x - reference.pos[0], 2)
y_diff_sq = pow(self.y - reference.pos[1], 2)
return pow(x_diff_sq + y_diff_sq, 0.5)
And then I pass what this function returns into the next function for calculating the position
def move(self, d):
self.theta += self.d_theta
self.x = int(d * math.cos(self.theta)) + total_d/2
self.y = int(d * math.sin(self.theta)) + total_d/2
total_d/2 is a co-ordinate offset and self.d_theta is the rotational period for the given planet.
Each planet has its initial position hard coded and I'm using this to calculate the difference between initial distance and current distance for all of the planets, every tick it is apparent that the planet moves about 1km towards the sun. Is there any way I can attempt to offset this?
I understand that in the scale of things where I'm drawing things in terms of millions of km, I'm just curious what part of these equations is causing the error. I've tried using the '**' operator over pow and after some research online found that pow is better used for powers involving floats.
Should also mention that all calculations are in kilometers, then before drawing, the planets radius and x, y are mapped to the screen from a set distance that is currently around 4 AU.
You're trying to move your planets in circles, right?
In your code, you
Use x and y to calculate distance,
Use delta_theta to calculate new theta,
Use new theta and distance to calculate new x and y.
You don't have to do all that. Instead, you can keep a hardcoded distance and just
Use delta_theta to calculate new theta,
Use new theta and (known) distance to calculate x and y for drawing.
Then your distance will not drift at all.
Side note: If you're planning to keep the planets moving for long times, make sure you keep your theta between 0 and 2*pi, or rounding errors will start kicking in and your theta accuracy will deteriorate.
You're thinking this will make adding moons and asteroids difficult.
Not really!
You can do the same for moons, by noting which planet they belong to, the distance to that planet, delta_theta and initial theta (based on their parent planet).
If you want to start doing ellipses instead of circles, you can change your calculations (use convenient constant orbital elements instead of distance and delta_theta, which will not be constant anymore) to apply Kepler's laws.
You can also add asteroids later. You can keep the Cartesian positions and velocities of the asteroids, and calculate their motion separately, after calculating the motion of all the "trivially" moving objects.

Vpython: property 'axis' must be a vector

I am doing a Vpython assignment for my physics 2 class, which is asking to program the electric field of a dipole. I have written the following code:
## constants
oofpez = 9e9 # stands for One Over Four Pi Epsilon-Zero
qe = 1.6e-19 # postive charge value
s = 4e-11 # charge separation
R = 3e-10 # display Enet on a circle of radius R
scalefactor = 3e-20 # for scaling arrows to represent electric field
## objects
## Represent the two charges of the dipole by red and blue spheres:
plus = sphere(pos=vector(s/2,0,0), radius=1e-11, color=color.red)
qplus = qe # charge of positive particle
neg = sphere(pos=vector(-s/2,0,0), radius=1e-11, color=color.blue)
qneg = -qplus # charge of negative particle
## calculations
## You will complete the lines required to make a loop calculate and display the net dipole electric field
## at equally spaced angles on a circle radius R around the dipole. The dipole is centered at the origin.
theta = 0
while theta < 2*pi:
rate(2) # tell computer to go through loop slowly
## Calculate observation location (tail of arrow) using current value of theta:
Earrow = arrow(pos=R*vector(cos(theta),sin(theta),0), axis=vector(1e-10,0,0), color=color.orange)
## assign the name TestLocation to be the observation location on the circle radius R
TestLocation=R*vector(cos(theta),sin(theta),0)
## write instructions below to tell the computer how to calculate the correct
## net electric field Enet at the observation location (the position of Earrow):
rPlus=TestLocation-plus.pos
rPlusMag=((R*cos(theta)-(s/2))^2+(R*sin(theta))^2)^0.5
rPlusHat=rPlus/rPlusMag
Eplus=oofpez*qplus/(rPlusMag)^2*rPlusHat
rNeg=TestLocation-neg.pos
rNegMag=((R*cos(theta)-(-s/2))^2+(R*sin(theta))^2)^0.5
rNegHat=rNeg/rNegMag
Eneg=oofpez*qneg/(rNegMag)^2*rNegHat
Etotal=Eplus+Eneg
Enet=arrow(pos=TestLocation,axis=Etotal*scalefactor, color=color.green)
## change the axis of Earrow to point in the direction of the electric field at that location
## and scale it so it looks reasonable
## Efield = arrow(pos=R*vector(cos(theta),sin(theta),0), axis=Etotal*scalefactor, color=color.blue)
Earrow.axis=Etotal*scalefactor
## Assign a new value to theta
theta = theta + pi/6
The assignment had a pre-made template created with the comments, the proper variables declared and said variables assigned the correct values, so in theory it should run correctly if I input the rest of the code correctly. The code I wrote starts at "rPlus=..." and ends at "Enet=..." However, when I run it (using GlowScript IDE) it gives an error message saying "Error: Property 'axis' must be a vector," which I'm sure means that there's something wrong with the value assigned to Enet.axis within that section of the code. I have looked through the code I've generated and can't seem to find the error.
We are learning python supplementary to our regular course work so I do not have any background in python besides these assignments. I don't need help with finding the electric field, just why the error message is popping up. Any help or hint in the right direction would be appreciated!
Thanks
On line 31 you used ^ instead of ** for exponentiation.
If you use the Chrome browser you can get the line number of an error.
I found the problem by inserting print statements at key locations in the code, which turned up the fact that rPlusMag was 0, not a vector.

How to account for radian to degrees inaccuracy

I am trying to perform a simple task using simple math in python and I suspect that the inherit error in converting from radians to degrees as a result of an error with floating point math (as garnered from another question on the topic please don't mark this as a duplicate question, it's not).
I am trying to extend a line by 500m. To do this I am taking the the endpoint coordinates from a supplied line and using the existing heading of said line to generate the coordinates of the point which is 500m in the same heading.
Heading is important in this case as it is the source of my error. Or so I suspect.
I use the following function to calculate the interior angle of my right angle triangle, built using the existing line, or in this case my hypotenuse:
def intangle(xypoints):
angle = []
for i in xypoints:
x1 = i[0][0]
x2 = i[1][0]
y1 = i[0][1]
y2 = i[1][1]
gradient = (x1 - x2)/(y1-y2)
radangle = math.atan(gradient)
angle.append((math.degrees(radangle)))
return angle
My input points are, for example:
(22732.23679147904, 6284399.7935522054)
(20848.591367954294, 6281677.926560438)
I know going into this that my angle is 35° as these coordinates are programmatically generated by a separate function and when plotted are out by around 3.75" for each KM. Another error as a result of converting radians to degrees but acceptable in its scope.
The error generated by the above function however, results in an angle that plots my new endpoint in such a place that the line is no longer perfectly straight when I connect the dots and I absolutely have to have a straight line.
How can I go about doing this differently to account for the floating point error? Is it even possible? If not, then what would be an acceptable method of extending my line by howevermany meters using euclidean geometry?
To add to this, I have already done all relevant geographic conversions and I am 100% sure that I am working on a 2D plane so the ellipsoid and such do not play a role in this at all.
Using angles is unnecessary, and there are problems in the way you do it. Using the atan will only give you angles between -pi/2 and pi/2, and you will get the same angle value for opposite directions.
You should rather use Thales:
import math
a = (22732.23679147904, 6284399.7935522054)
b = (20848.591367954294, 6281677.926560438)
def extend_line(a, b, length):
"""
Returns the coordinates of point C at length beyond B in the direction of A->B"""
ab = math.sqrt((a[0]-b[0])**2 + (a[1]-b[1])**2)
coeff = (ab + length)/ab
return (a[0] + coeff*(b[0]-a[0]), a[1] + coeff*(b[1]-a[1]) )
print(extend_line(a, b, 500))
# (20564.06031560228, 6281266.7792872535)

get angles using atan2 on circle for robot simulation

I am doing a simulation on a robot that makes circular motion with a center coordinate (x1, y1). The robot measures its position periodically and returns the information. Let's say the returned coordinate is (x3, y3). However, I'm not too sure about the correct way of using atan2 in python to calculate the angles between the current measurement and the last measurement with the center.
so far, I am doing the following, and would like to confirm if it's right:
current_angle = atan2(y3-y1, x3-x1)
last_angle = atan2(y2-y1, x2-x1)
angle_difference = current_angle - last_angle
With the angle_difference, I then calculate the angular velocity = angle_difference / dt
(dt is the time spent between the last measurement and the current measurement)
Almost correct. The formula as given will have trouble when passing across axes between the first and fourth quadrants and the second and third quadrants, so you need to take the minimum of the absolute values of the differences of the angles instead, and then compensate for sign.

Categories

Resources