Position Analysis of System - python

I am trying to do this kinematic position analysis in Python using Numpy library. However, facing some issues about below code. The position analysis of xC and xP is not correct.
import numpy as np
import math as mt
import matplotlib.pyplot as plt
#Linkage Dimensions in meter
a = 0.130 #Crank Lenght
b = 0.200 #Coupler lenght
c = 0.170 #Rocker Lenght
d = 0.220 #Lenght between ground pins
p = 0.250 #Lenght from B to P
gamma = 20*mt.pi/180 #Angle between BP and coupler converted radian
print('Math module Pi',mt.pi)
#Coordinates of ground pins
x0 = np.array([[0],[0]]) #point A the origin of system
xD = np.array([[d],[0]]) #point D
N=360 #Number of times perform position analysis
This creating matrix for position:
#Allocate for position B,C and P
xB = np.zeros([2,N])
xC = np.zeros([2,N])
xP = np.zeros([2,N])
#Allocate space for angles
theta2 = np.zeros([1,N])
theta3 = np.zeros([1,N])
theta4 = np.zeros([1,N])
#Defining Unit Vector Function
#Calculates the unit vector and unit normal for a given angle
#theta = angle of unitvector
#e = unit vector in the direction of theta
#n = unit normal to the vector e
def UnitVector(theta):
e = np.array([[np.cos(theta)], [np.sin(theta)]])
n = np.array([[-np.sin(theta)], [np.cos(theta)]])
return [e,n]
#Defining FindPos function
#Calculate the position of a point on a link using relative position
#formula
#x0 = position of first point on the link
#L = Lenght of vector between first and second points
#e = Unit Vector between first and second points
#x = Position of second point on the link
def FindPos(x0,L,e):
x = x0 + L + e
return x
t3 = float((mt.pi)/4) #Initial Guess for Newton-Raphson
t4 = float((mt.pi)/2) #Initial Guess for Newton-Raphson
print('Initial Guess for t3: ',t3)
print('Initial Guess for t4: ',t4)
The below block is about two dimension Newton-Raphson algorithm:
for i in range(1,N):
theta2[0,(i-1)] = (i-1)*(2*mt.pi)/(N-1)
#Newton-Raphson Calculation
for j in range(1,6):
phi11 = a*mt.cos(theta2[0,(i-1)]) + b*mt.cos(t3) - c*mt.cos(t4) - d
phi12 = a*mt.sin(theta2[0,(i-1)]) + b*mt.sin(t3) - c*mt.sin(t4)
phi = np.array([[phi11], [phi12]])
norm = ((phi[0])**2 + (phi[1])**2)**(0.5)
#If constrain equations are satisfied then terminate
if (norm < 0.000000000001):
theta3[0,i-1] = t3
theta4[0,i-1] = t4
break
Calculation of Jacobien matrix:
#Calculate Jacobien Matrix
J00 = -b*mt.sin(t3) #(1,1) element of matrix
J01 = c*mt.sin(t4) #(1,2) eleemnt of matrix
J10 = b*mt.cos(t3) #(2,1) element of matrşx
J11 = -c*mt.cos(t4) #(2,2) element of mattix
J = np.array([[J00, J01],[J10, J11]])
#Update Variables using Newton-Raphson Equation
dq = np.linalg.solve(J,phi)
t3 = t3 + dq[0,0]
t4 = t4 + dq[1,0]
#Calculating UnitVectors
[e2,n2] = UnitVector(theta2[0,(i-1)])
[e3,n3] = UnitVector(theta3[0,(i-1)])
[e4,n4] = UnitVector(theta4[0,(i-1)])
[eBP,nBP] = UnitVector(theta3[0,(i-1)]+gamma)
Replacing calculated values with zeros in matrix:
#Solve for position of posints B, C and P on the linkage
xB[:,(i-1)][0] = FindPos(x0,a,e2)[0][0]
xB[:,(i-1)][1] = FindPos(x0,a,e2)[1][0]
Plotting these values as position:
plt.plot(xB[0,:359],xB[1,:359])
plt.xlabel('x position [m]')
plt.ylabel('y position [m]')
plt.title('Four Bar Crank Position Analysis')
plt.grid(True)
plt.show()
Here is where the wrong results come in. xC and xP graphs are not correct.
xC[:,(i-1)][0] = FindPos(xD,c,e4)[0][0]
xC[:,(i-1)][1] = FindPos(xD,c,e4)[1][0]
xP[:,(i-1)][0] = FindPos(xB[:,i-1],p,eBP)[0][0]
xP[:,(i-1)][1] = FindPos(xB[:,i-1],p,eBP)[1][0]
plt.plot(xC[0,:359],xC[1,:359])
plt.xlabel('x position [m]')
plt.ylabel('y position [m]')
plt.title('Four Bar Crank Position Analysis')
plt.grid(True)
plt.show()
plt.plot(xP[0,:359],xP[1,:359])
plt.xlabel('x position [m]')
plt.ylabel('y position [m]')
plt.title('Four Bar Crank Position Analysis')
plt.grid(True)
plt.show()
Any suggestion is welcome. Thanks.

Related

Updated xyz positions

By using python, I want to plot 6 points in same diagram where first 3 points are 3 initial (x,y,z) position points and other 3 points are 3 updated position points (X,Y,Z).
The updated points formulas are X =π‘₯+π‘Ÿπœ‡π‘₯ , Y=𝑦+π‘Ÿπœ‡y , and Z=𝑧+π‘Ÿ*πœ‡z .
I plotted the 3 initial points. The problem is that all the 3 updated points have same value, then same point in the diagram. How can I make them different?
I think the problem is that in the Updated xyz positions, it takes only this point (x,y,z)=(3,6,9). How can I make these 3 updated position points different?
# Updated Positions and Direction Cosines
############################################
# Request libraries
############################################
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import random
from random import gauss
import math
from math import log, cos, acos
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
############################################
# Initial Positions and Direction Cosines
############################################
# Photons Number
PhotonsTotalNumber = 3
#PhotonsUniqueNumber= np.array(0,PhotonsTotalNumber)
# Initial Direction Cosines
Initial_ux= math.cos(math.radians(90))
Initial_uy= math.cos(math.radians(90))
Initial_uz= math.cos(math.radians(0))
# Initial xyz Positions
Initialx = np.array([1,2,3])
Initialy = np.array([4,5,6])
Initialz = np.array([7,8,9])
for x, y, z in zip(Initialx, Initialy, Initialz):
text = '({},{}, {})'.format(x,y,z)
ax.text(x,y,z, text, zdir=(1, 1, 1))
print(x,y,z)
############################################
# Photon Propagation Equations
############################################
# Random Numbers Function
def get_rand_number(min_value, max_value):
range = max_value - min_value
choice = random.uniform(0,1)
return min_value + range*choice
# Photon path length
r = -math.log(get_rand_number(0,1))/0.15 # 0.15 attenuation coefficients for clear water
# The anisotropy factor g
values = [1,-1]
g = np.random.choice(values)
g = int(g)
# Scattering Angles (theta and phi)
theta = (((1 + g*g - ((1 - g*g)/(1 - g + 2*g*get_rand_number(0,1)))**2)/(2*g)))
phi = 2 * math.radians(180) * get_rand_number(0,1)
Theta = gauss( 0, theta)
Phi = gauss( 0, phi)
############################################
# Updated Positions and Direction Cosines
############################################
# Updated direction cosines
New_ux = math.sin(Theta) * math.cos(Phi)
New_uy = math.sin(Theta) * math.sin(Phi)
New_uz = (Initial_uz/abs(Initial_uz))*math.cos(Theta)
# Updated xyz positions
Updatedx = x + (r * New_ux)
Updatedy = y + (r * New_uy)
Updatedz = z + (r * New_uz)
UpdatedX = np.array ([Updatedx,Updatedx,Updatedx])
UpdatedY = np.array ([Updatedy,Updatedy,Updatedy])
UpdatedZ = np.array ([Updatedz,Updatedz,Updatedz])
for X, Y, Z in zip(UpdatedX, UpdatedY, UpdatedZ):
text = '({},{}, {})'.format(X,Y,Z)
ax.text(X,Y,Z, text, zdir=(1, 1, 1))
ax.scatter(Initialx,Initialy,Initialz, s=100,c='b', label='True Position')
ax.scatter(UpdatedX,UpdatedY,UpdatedZ, s=100,c='g', label='True Position')
ax.set_xlabel("x axis")
ax.set_ylabel("y axis")
ax.set_zlabel("z axis")
plt.show()
####################

PDE 2D Colormap

Need help finishing the Code specifically steps 3 and 4.
Problem
Compute the temperature (K) profile throughout a cylindrical SiGe wire with thermal conductivity k = 4.2 W/(m*K), length L = 0.05 m, and radius R = 0.005 m.
The boundary conditions are given in the figure below. The solid lines correspond to zero-flux boundary conditions, the long-dashed line to open boundaries with a known temperature, and the short-dashed lines to open boundaries with known flux via convection (the listed T is the β€œambient” T). In this figure, assume z (in m) varies in the horizontal direction while r (in m) varies in the vertical direction (the upper left corner is the origin).
Justify your approach. Plot the temperature distribution throughout the wire using a 2D color- map with proper labels. Include contour lines.
https://www.chegg.com/homework-help/questions-and-answers/problem-2-compute-temperature-k-profile-throughout-cylindrical-sige-wire-thermal-conductiv-q96105385
Code
# Problem 2
# Import the required modules
import numpy as np
import matplotlib.pyplot as plt
# Constants
k = 4.2 # Thermal conductivity in W/(m.K)
L = 0.05 # Length in m
R = 0.005 # Radius in m
T = 575 # Ambient temp. in K
T1 = 423 # K
h1 = 45 # kW/(m^2.K)
T2 = 348 # K
h2 = 650 # kW/(m^2.K)
Ta = 298 # K
h = 7.5 # kW/(m^2.K)
# Iteration parameters
maxit = 2000
tol = 0.0001 # Relative tolerance
merr = 1e5
lam = 1.4 # Parameter for convergence rate
# Setup grid
dr = 0.01
nr = int(R/dr) + 1
nz = int(L/dr) + 1
rr = np.linspace(0,R,num=nr,endpoint=True);
zz = np.linspace(0,L,num=nz,endpoint=True);
# Step 1 - Initial Guesses
M = np.ones((nz,nr)) # Create 2D array w/ ones (z = # of rows, r = # of col)
M = M*T # Matrix of T now
# Step 2 - Apply Boundary Conditions
M[0,0:nr] = T; M[-1,0:nr] = T1;
M[0:nz,0] = T2; M[0:nz,-1] = Ta;
# Step 3 and 4 - Apply LDE, walking over nodes
cc = 0; # Counter
a = k*dr*dr/(4*R)
while merr > tol:
Mold = np.copy(M) # Save current values to old
M[-1,-1]=(2*M[-2,-1]+2*M[-1,-2])/(4+a) # Corner
for j in range(1,nz-1):
M[j,-1]=(2*M[j,-2]+M[j-1,-1]+M[j+1,-1])/(4+a)
for i in range(1,nr-1):
M[-1,i]=(M[-1,i-1]+M[-1,i+1]+2*M[-2,i])/(4+a)
for i in range(1,70):
M[0,i]=(M[0,i-1]+M[0,i+1]+2*M[1,i])/(4+a)
for j in range(1,nz-1):
for i in range(1,nr-1):
M[j,i] = (M[j,i-1]+M[j,i+1]+M[j-1,i]+M[j+1,i])/(4+a)
M = lam*M+(1-lam)*Mold # Adjust for convergence rate
ea = np.abs((M-Mold)/Mold)
cc = cc + 1;
merr = np.max(ea)
# Plot color mesh
X, Y = np.meshgrid(rr, zz)
p = plt.pcolormesh(rr, zz, M, cmap="RdBu", shading="flat", vmin=0, vmax=100)
ct = plt.contour(X, Y, M, cmap="gray", levels=10, vmin=0, vmax=100)
c = plt.colorbar(p)
plt.xlabel("R (m)")
plt.ylabel("Z (m)")
c.set_label("Teamperature (K)")
plt.show()
# Print Results
print("Converged in %d iterations" % cc)
print("Max error is %f" % merr)
print("Mean Teamperature along central axis %f K" %(np.mean(M[:,0])))

Problem with earth orbit plot using python

I am trying to write a code for the orbit of the earth in SI using a symplectic integrator, my attempt is as follows:
import numpy as np
import matplotlib.pyplot as plt
#Set parameters
G = 6.67348e-11
mEar = 5.972e24
mSun = 1.989e30
def earth_orbit(x0, y0, vx0, vy0, N):
dt = 1/N #timestep
pos_arr = np.zeros((N,2)) #empty array to store position
vel_arr = np.zeros((N,2)) #empty array to store velocities
#Initial conditions
# x0 = x
# y0 = y
# vx0 = vx
# vy0 = vy
pos_arr[0] = (x0,y0) #set the intial positions in the array
vel_arr[0] = (vx0,vy0) #set the initial velocities in the array
#Implement Verlet Algorithm
for k in range (N-1):
pos_arr[k+1] = pos_arr[k] + vel_arr[k]*dt #update positions
force = -G * mSun * mEar * pos_arr[k+1] / (np.linalg.norm(pos_arr[k+1])**3) #force calculation
vel_arr[k+1] = vel_arr[k] + (force/mEar) * dt #update velocities
#Plot:
plt.plot(pos_arr, 'go', markersize = 1, label = 'Earth trajectory')
# plt.plot(0,0,'yo', label = 'Sun positon') # yellow marker
# plt.plot(pos_arr[0],'bo', label = 'Earth initial positon') # dark blue marker
plt.axis('equal')
plt.xlabel ('x')
plt.ylabel ('y')
return pos_arr, vel_arr
earth_orbit(149.59787e9, 0, 0, 29800, 1000)
The output is 2 dots and I can't figure out if this is a unit issue or a calculation issue?
Display the trajectory
pos_arr contains the x and y coordinates in its columns. To display the whole trajectory, plt.plot(pos_arr[:,0], pos_arr[:,1]) can thus be used. I would prefer to use plt.plot(*pos_arr.T) as a shorter alternative. The line that displays the trajectory must be replaced by:
plt.plot(*pos_arr.T, 'g', label = 'Earth trajectory')
Change the timestep
Here the timestep (in second) is chosen as 1/N, where N is the number of iterations. So, the total duration of the simulation is equal to timestep * N = 1 second ! For N=1000, you can instead try with timestep = 3600*12 (half-day), so that the total duration is a little less than 1.5 years. I suggest passing the duration as a parameter of the function earth_orbit and then setting timestep as duration / N.
def earth_orbit(x0, y0, vx0, vy0, N=1000, duration=3.15e7):
dt = duration / N
...
As said in the comments, this is not the Verlet algorithm, but the symplectic Euler algorithm. The difference is in the initialization, but in comparing against a more exact reference solution and with several step sizes, the difference in the orders, 2 vs. 1, will be quite visible.
A short change to the time loop ensuring that the velocities are at the half-time steps as required for Leapfrog Verlet could look like this:
def force(pos): return -G * mSun * mEar * pos_arr[k+1] / (np.linalg.norm(pos_arr[k+1])**3) #force calculation
pos_arr[0] = (x0,y0) #set the intial positions in the array
vel_arr[0] = (vx0,vy0) #set the initial velocities in the array
vel_arr[0] += (force(pos_arr[0])/mEar) * (0.5*dt) #correct for velocity at half-time
#Implement Verlet Algorithm
for k in range (N-1):
pos_arr[k+1] = pos_arr[k] + vel_arr[k] * dt #update positions
vel_arr[k+1] = vel_arr[k] + (force(pos_arr[k+1])/mEar) * dt #update velocities

Problem with 2D mapping graphs using matplotlib

I am plotting 2D images of energy and density distribution. There is always a slight misalignment in the mapping where the very first "columns" seem to go to the last columns during the plot.
I have attach link to for data test file.
Data files
Here is the plot :
Is there anything to prevent this ?
The partial code in plotting is as follows:
import numpy as np
import matplotlib.pyplot as plt
import pylab as pyl
import scipy.stats as ss
import matplotlib.ticker as ticker
import matplotlib.transforms as tr
#%matplotlib inline
pi = 3.1415
n = 5e24 # density plasma
m = 9.109e-31
eps = 8.85e-12
e = 1.6021725e-19
c = 3e8
wp=np.sqrt(n*e*e/(m*eps))
kp = np.sqrt(n*e*e/(m*eps))/c #plasma wavenumber
case=400
## decide on the target range of analysis for multiples
start= 20500
end = 21500
gap = 1000
## Multiples plots
def target_range (start, end, gap):
while start<= end:
yield start
start += gap
for step in target_range(start, end, gap):
fdata =np.genfromtxt('./beam_{}'.format(step)).reshape(-1,6)
## dimension, dt, and superpaticle
xBoxsize = 50e-6 #window size
yBoxsize = 80e-6 #window size
xbind = 10
ybind = 1
dx = 4e-8 #cell size
dy = 4e-7 #cell size
dz = 1e-6 #assume to be same as dy
dt = 1.3209965456e-16
sptcl = 1.6e10
xsub = 0e-6
xmax = dt*step*c
xmin = xmax - xBoxsize
ysub = 1e-7
ymin = ysub #to make our view window
ymax = yBoxsize - ysub
xbins = int((xmax - xmin)/(dx*xbind))
ybins = int((ymax - ymin)/(dy*ybind))
#zbins = int((zmax - zmin)/dz) #option for 3D
# To make or define "data_arr" as a matrix with 2D array size 'xbins x ybins'
data_arr = np.zeros((2,xbins,ybins), dtype=np.float)
for line in fdata:
x = int((line[0]-xmin)/(dx*xbind))
y = int((line[1]-ymin)/(dy*ybind))
#z = int((line[2]-zmin)/dz)
if x >= xbins: x = xbins - 1
if y >= ybins: y = ybins - 1
#if z >= zbins: z = zbins - 1
data_arr[0, x, y] = data_arr[0,x, y] + 1 #cummulative adding up the number of particles
energy_total = np.sqrt(1+ line[2]*line[2]/(c*c)+line[3]*line[3]/(c*c))/0.511
data_arr[1, x, y] += energy_total
#array 1 tells us the energy while array 0 tells us the particles
## make average energy , total energy/particle number
np.errstate(divide='ignore',invalid='ignore')
en_arr = np.true_divide(data_arr[1],data_arr[0]) # total energy/number of particles
en_arr[en_arr == np.inf] = 0
en_arr = np.nan_to_num(en_arr)
en_arr = en_arr.T
## This part is real density of the distribution
data_arr[0]= data_arr[0] * sptcl/dx/dy #in m-3
d = data_arr[0].T
## Plot and save density and energy distribution figures
den_dist=plt.figure(1)
plt.imshow(d,origin='lower', aspect = 'auto',cmap =plt.get_cmap('gnuplot'),extent =(xmin/1e-3,xmax/1e-3,ymin/1e-6,ymax/1e-6))
plt.title('Density_dist [m-3]_{}'.format(step))
plt.xlabel('distance[mm]')
plt.ylabel('y [um]')
plt.colorbar()
plt.show()
den_dist.savefig("./Qen_distribution_{}.png".format(step),format ='png')
#note:cmap: rainbow, hot,jet,gnuplot,plasma
energy_dist=plt.figure(2)
plt.imshow(en_arr, origin ='lower',aspect = 'auto', cmap =plt.get_cmap('jet'),extent =(xmin/1e-3,xmax/1e-3,ymin/1e-6,ymax/1e-6))
plt.title ('Energy_dist [MeV]_{} '.format(step))
plt.xlabel('distance[mm]')
plt.ylabel('y [um]')
plt.colorbar()
plt.show()
energy_dist.savefig("./Qenergy_distribution_{}.png".format(step),format ='png')

Implementing initial conditions for a numerically solved differential equation

Imagine someone jumping off a balcony under a certain angle theta and velocity v0 (the height of the balcony is denoted as ystar). Looking at this problem in 2D and considering drag you get a system of differential equations which can be solved with a Runge-Kutta method (I choose explicit-midpoint, not sure what the butcher tableu for this one is). I implemented this and it works perfectly fine, for some given initial conditions I get the trajectory of the moving particle.
My problem is that I want to fix two of the initial conditions (starting point on the x-axis is zero and on the y-axis is ystar) and make sure that the trajectory goes trough a certain point on the x-axis (let's call it xstar). For this of course exist multiple combinations of the other two initial conditions, which in this case are the velocities in the x- and y-direction. The problem is that I don't know how to implement that.
The code that I used to solve the problem up to this point:
1) Implementation of the Runge-Kutta method
import numpy as np
import matplotlib.pyplot as plt
def integrate(methode_step, rhs, y0, T, N):
star = (int(N+1),y0.size)
y= np.empty(star)
t0, dt = 0, 1.* T/N
y[0,...] = y0
for i in range(0,int(N)):
y[i+1,...]=methode_step(rhs,y[i,...], t0+i*dt, dt)
t = np.arange(N+1) * dt
return t,y
def explicit_midpoint_step(rhs, y0, t0, dt):
return y0 + dt * rhs(t0+0.5*dt,y0+0.5*dt*rhs(t0,y0))
def explicit_midpoint(rhs,y0,T,N):
return integrate(explicit_midpoint_step,rhs,y0,T,N)
2) Implementation of the right-hand-side of the differential equation and the nessecery parameters
A = 1.9/2.
cw = 0.78
rho = 1.293
g = 9.81
# Mass and referece length
l = 1.95
m = 118
# Position
xstar = 8*l
ystar = 4*l
def rhs(t,y):
lam = cw * A * rho /(2 * m)
return np.array([y[1],-lam*y[1]*np.sqrt(y[1]**2+y[3]**2),y[3],-lam*y[3]*np.sqrt(y[1]**2+y[3]**2)-g])
3) solving the problem with it
# Parametrize the two dimensional velocity with an angle theta and speed v0
v0 = 30
theta = np.pi/6
v0x = v0 * np.cos(theta)
v0y = v0 * np.sin(theta)
# Initial condintions
z0 = np.array([0, v0x, ystar, v0y])
# Calculate solution
t, z = explicit_midpoint(rhs, z0, 5, 1000)
4) Visualization
plt.figure()
plt.plot(0,ystar,"ro")
plt.plot(x,0,"ro")
plt.plot(z[:,0],z[:,1])
plt.grid(True)
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.show()
To make the question concrete: With this set up in mind, how do I find all possible combinations of v0 and theta such that z[some_element,0]==xstar
I tried of course some things, mainly the brute force method of fixing theta and then trying out all the possible velocities (in an intervall that makes sense) but finally didn't know how to compare the resulting arrays with the desired result...
Since this is mainly a coding issue I hope stack overflow is the right place to ask for help...
EDIT:
As requested here is my try to solve the problem (replacing 3) and 4) from above)..
theta = np.pi/4.
xy = np.zeros((50,1001,2))
z1 = np.zeros((1001,2))
count=0
for v0 in range(0,50):
v0x = v0 * np.cos(theta)
v0y = v0 * np.sin(theta)
z0 = np.array([0, v0x, ystar, v0y])
# Calculate solution
t, z = explicit_midpoint(rhs, z0, 5, 1000)
if np.around(z[:,0],3).any() == round(xstar,3):
z1[:,0] = z[:,0]
z1[:,1] = z[:,2]
break
else:
xy[count,:,0] = z[:,0]
xy[count,:,1] = z[:,2]
count+=1
plt.figure()
plt.plot(0,ystar,"ro")
plt.plot(xstar,0,"ro")
for k in range(0,50):
plt.plot(xy[k,:,0],xy[k,:,1])
plt.plot(z[:,0],z[:,1])
plt.grid(True)
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.show()
I'm sure that I'm using the .any() function wrong, the idea there is to round the values of z[:,0] to three digits and than compare them to xstar, if it matches the loop should terminate and retrun the current z, if not it should save it in another array and then increase v0.
Edit 2018-07-16
Here I post a corrected answer taking into account the drag by air.
Below is a python script to compute the set of (v0,theta) values so that the air-dragged trajectory passes through (x,y) = (xstar,0) at some time t=tstar. I used the trajectory without air-drag as the initial guess and also to guess the dependence of x(tstar) on v0 for the first refinement. The number of iterations needed to arrive at the correct v0 was typically 3 to 4. The script finished in 0.99 seconds on my laptop, including the time for generating figures.
The script generates two figures and one text file.
fig_xdrop_v0_theta.png
The black dots indicates the solution set (v0,theta)
The yellow line indicates the reference (v0,theta) which would be a solution if there were no air drag.
fig_traj_sample.png
Checking that the trajectory (blue solid line) passes through (x,y)=(xstar,0) when (v0,theta) is sampled from the solution set.
The black dashed line shows a trajectory without drag by air as a reference.
output.dat
contains the numerical data of (v0,theta) as well as the landing time tstar and number of iteration needed to find v0.
Here begins script.
#!/usr/bin/env python3
import numpy as np
import scipy.integrate
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.image as img
mpl.rcParams['lines.linewidth'] = 2
mpl.rcParams['lines.markeredgewidth'] = 1.0
mpl.rcParams['axes.formatter.limits'] = (-4,4)
#mpl.rcParams['axes.formatter.limits'] = (-2,2)
mpl.rcParams['axes.labelsize'] = 'large'
mpl.rcParams['xtick.labelsize'] = 'large'
mpl.rcParams['ytick.labelsize'] = 'large'
mpl.rcParams['xtick.direction'] = 'out'
mpl.rcParams['ytick.direction'] = 'out'
############################################
len_ref = 1.95
xstar = 8.0*len_ref
ystar = 4.0*len_ref
g_earth = 9.81
#
mass = 118
area = 1.9/2.
cw = 0.78
rho = 1.293
lam = cw * area * rho /(2.0 * mass)
############################################
ngtheta=51
theta_min = -0.1*np.pi
theta_max = 0.4*np.pi
theta_grid = np.linspace(theta_min, theta_max, ngtheta)
#
ngv0=100
v0min =6.0
v0max =18.0
v0_grid=np.linspace(v0min, v0max, ngv0)
# .. this grid is used for the initial coarse scan by reference trajecotry
############################################
outf=open('output.dat','w')
print('data file generated: output.dat')
###########################################
def calc_tstar_ref_and_x_ref_at_tstar_ref(v0, theta, ystar, g_earth):
'''return the drop time t* and drop point x(t*) of a reference trajectory
without air drag.
'''
vx = v0*np.cos(theta)
vy = v0*np.sin(theta)
ts_ref = (vy+np.sqrt(vy**2+2.0*g_earth*ystar))/g_earth
x_ref = vx*ts_ref
return (ts_ref, x_ref)
def rhs_drag(yvec, time, g_eath, lamb):
'''
dx/dt = v_x
dy/dt = v_y
du_x/dt = -lambda v_x sqrt(u_x^2 + u_y^2)
du_y/dt = -lambda v_y sqrt(u_x^2 + u_y^2) -g
yvec[0] .. x
yvec[1] .. y
yvec[2] .. v_x
yvec[3] .. v_y
'''
vnorm = (yvec[2]**2+yvec[3]**2)**0.5
return [ yvec[2], yvec[3], -lamb*yvec[2]*vnorm, -lamb*yvec[3]*vnorm -g_earth]
def try_tstar_drag(v0, theta, ystar, g_earth, lamb, tstar_search_grid):
'''one trial run to find the drop point x(t*), y(t*) of a trajectory
under the air drag.
'''
tinit=0.0
tgrid = [tinit]+list(tstar_search_grid)
yvec_list = scipy.integrate.odeint(rhs_drag,
[0.0, ystar, v0*np.cos(theta), v0*np.sin(theta)],
tgrid, args=(g_earth, lam))
y_drag = [yvec[1] for yvec in yvec_list]
x_drag = [yvec[0] for yvec in yvec_list]
if y_drag[0]<0.0:
ierr=-1
jtstar=0
tstar_braket=None
elif y_drag[-1]>0.0:
ierr=1
jtstar=len(y_drag)-1
tstar_braket=None
else:
ierr=0
for jt in range(len(y_drag)-1):
if y_drag[jt+1]*y_drag[jt]<=0.0:
tstar_braket=[tgrid[jt],tgrid[jt+1]]
if abs(y_drag[jt+1])<abs(y_drag[jt]):
jtstar = jt+1
else:
jtstar = jt
break
tstar_est = tgrid[jtstar]
x_drag_at_tstar_est = x_drag[jtstar]
y_drag_at_tstar_est = y_drag[jtstar]
return (tstar_est, x_drag_at_tstar_est, y_drag_at_tstar_est, ierr, tstar_braket)
def calc_x_drag_at_tstar(v0, theta, ystar, g_earth, lamb, tstar_est,
eps_y=1.0e-3, ngt_search=20,
rel_range_lower=0.8, rel_range_upper=1.2,
num_try=5):
'''compute the dop point x(t*) of a trajectory under the air drag.
'''
flg_success=False
tstar_est_lower=tstar_est*rel_range_lower
tstar_est_upper=tstar_est*rel_range_upper
for jtry in range(num_try):
tstar_search_grid = np.linspace(tstar_est_lower, tstar_est_upper, ngt_search)
tstar_est, x_drag_at_tstar_est, y_drag_at_tstar_est, ierr, tstar_braket \
= try_tstar_drag(v0, theta, ystar, g_earth, lamb, tstar_search_grid)
if ierr==-1:
tstar_est_upper = tstar_est_lower
tstar_est_lower = tstar_est_lower*rel_range_lower
elif ierr==1:
tstar_est_lower = tstar_est_upper
tstar_est_upper = tstar_est_upper*rel_range_upper
else:
if abs(y_drag_at_tstar_est)<eps_y:
flg_success=True
break
else:
tstar_est_lower=tstar_braket[0]
tstar_est_upper=tstar_braket[1]
return (tstar_est, x_drag_at_tstar_est, y_drag_at_tstar_est, flg_success)
def find_v0(xstar, v0_est, theta, ystar, g_earth, lamb, tstar_est,
eps_x=1.0e-3, num_try=6):
'''solve for v0 so that x(t*)==x*.
'''
flg_success=False
v0_hist=[]
x_drag_at_tstar_hist=[]
jtry_end=None
for jtry in range(num_try):
tstar_est, x_drag_at_tstar_est, y_drag_at_tstar_est, flg_success_x_drag \
= calc_x_drag_at_tstar(v0_est, theta, ystar, g_earth, lamb, tstar_est)
v0_hist.append(v0_est)
x_drag_at_tstar_hist.append(x_drag_at_tstar_est)
if not flg_success_x_drag:
break
elif abs(x_drag_at_tstar_est-xstar)<eps_x:
flg_success=True
jtry_end=jtry
break
else:
# adjust v0
# better if tstar_est is also adjusted, but maybe that is too much.
if len(v0_hist)<2:
# This is the first run. Use the analytical expression of
# dx(tstar)/dv0 of the refernece trajectory
dx = xstar - x_drag_at_tstar_est
dv0 = dx/(tstar_est*np.cos(theta))
v0_est += dv0
else:
# use linear interpolation
v0_est = v0_hist[-2] \
+ (v0_hist[-1]-v0_hist[-2]) \
*(xstar -x_drag_at_tstar_hist[-2])\
/(x_drag_at_tstar_hist[-1]-x_drag_at_tstar_hist[-2])
return (v0_est, tstar_est, flg_success, jtry_end)
# make a reference table of t* and x(t*) of a trajectory without air drag
# as a function of v0 and theta.
tstar_ref=np.empty((ngtheta,ngv0))
xdrop_ref=np.empty((ngtheta,ngv0))
for j1 in range(ngtheta):
for j2 in range(ngv0):
tt, xx = calc_tstar_ref_and_x_ref_at_tstar_ref(v0_grid[j2], theta_grid[j1], ystar, g_earth)
tstar_ref[j1,j2] = tt
xdrop_ref[j1,j2] = xx
# make an estimate of v0 and t* of a dragged trajectory for each theta
# based on the reference trajectroy's landing position xdrop_ref.
tstar_est=np.empty((ngtheta,))
v0_est=np.empty((ngtheta,))
v0_est[:]=-1.0
# .. null value
for j1 in range(ngtheta):
for j2 in range(ngv0-1):
if (xdrop_ref[j1,j2+1]-xstar)*(xdrop_ref[j1,j2]-xstar)<=0.0:
tstar_est[j1] = tstar_ref[j1,j2]
# .. lazy
v0_est[j1] \
= v0_grid[j2] \
+ (v0_grid[j2+1]-v0_grid[j2])\
*(xstar-xdrop_ref[j1,j2])/(xdrop_ref[j1,j2+1]-xdrop_ref[j1,j2])
# .. linear interpolation
break
print('compute v0 for each theta under air drag..')
# compute v0 for each theta under air drag
theta_sol_list=[]
tstar_sol_list=[]
v0_sol_list=[]
outf.write('# theta v0 tstar numiter_v0\n')
for j1 in range(ngtheta):
if v0_est[j1]>0.0:
v0, tstar, flg_success, jtry_end \
= find_v0(xstar, v0_est[j1], theta_grid[j1], ystar, g_earth, lam, tstar_est[j1])
if flg_success:
theta_sol_list.append(theta_grid[j1])
v0_sol_list.append(v0)
tstar_sol_list.append(tstar)
outf.write('%26.16e %26.16e %26.16e %10i\n'
%(theta_grid[j1], v0, tstar, jtry_end+1))
theta_sol = np.array(theta_sol_list)
v0_sol = np.array(v0_sol_list)
tstar_sol = np.array(tstar_sol_list)
### Check a sample
jsample=np.size(v0_sol)//3
theta_sol_sample= theta_sol[jsample]
v0_sol_sample = v0_sol[jsample]
tstar_sol_sample= tstar_sol[jsample]
ngt_chk = 50
tgrid = np.linspace(0.0, tstar_sol_sample, ngt_chk)
yvec_list = scipy.integrate.odeint(rhs_drag,
[0.0, ystar,
v0_sol_sample*np.cos(theta_sol_sample),
v0_sol_sample*np.sin(theta_sol_sample)],
tgrid, args=(g_earth, lam))
y_drag_sol_sample = [yvec[1] for yvec in yvec_list]
x_drag_sol_sample = [yvec[0] for yvec in yvec_list]
# compute also the trajectory without drag starting form the same initial
# condiiton by setting lambda=0.
yvec_list = scipy.integrate.odeint(rhs_drag,
[0.0, ystar,
v0_sol_sample*np.cos(theta_sol_sample),
v0_sol_sample*np.sin(theta_sol_sample)],
tgrid, args=(g_earth, 0.0))
y_ref_sample = [yvec[1] for yvec in yvec_list]
x_ref_sample = [yvec[0] for yvec in yvec_list]
#######################################################################
# canvas setting
#######################################################################
f_size = (8,5)
#
a1_left = 0.15
a1_bottom = 0.15
a1_width = 0.65
a1_height = 0.80
#
hspace=0.02
#
ac_left = a1_left+a1_width+hspace
ac_bottom = a1_bottom
ac_width = 0.03
ac_height = a1_height
###########################################
############################################
# plot
############################################
#------------------------------------------------
print('plotting the solution..')
fig1=plt.figure(figsize=f_size)
ax1 =plt.axes([a1_left, a1_bottom, a1_width, a1_height], axisbg='w')
im1=img.NonUniformImage(ax1,
interpolation='bilinear', \
cmap=mpl.cm.Blues, \
norm=mpl.colors.Normalize(vmin=0.0,
vmax=np.max(xdrop_ref), clip=True))
im1.set_data(v0_grid, theta_grid/np.pi, xdrop_ref )
ax1.images.append(im1)
plt.contour(v0_grid, theta_grid/np.pi, xdrop_ref, [xstar], colors='y')
plt.plot(v0_sol, theta_sol/np.pi, 'ok', lw=4, label='Init Cond with Drag')
plt.legend(loc='lower left')
plt.xlabel(r'Initial Velocity $v_0$', fontsize=18)
plt.ylabel(r'Angle of Projection $\theta/\pi$', fontsize=18)
plt.yticks([-0.50, -0.25, 0.0, 0.25, 0.50])
ax1.set_xlim([v0min, v0max])
ax1.set_ylim([theta_min/np.pi, theta_max/np.pi])
axc =plt.axes([ac_left, ac_bottom, ac_width, ac_height], axisbg='w')
mpl.colorbar.Colorbar(axc,im1)
axc.set_ylabel('Distance from Blacony without Drag')
# 'Distance from Blacony $x(t^*)$'
plt.savefig('fig_xdrop_v0_theta.png')
print('figure file genereated: fig_xdrop_v0_theta.png')
plt.close()
#------------------------------------------------
print('plotting a sample trajectory..')
fig1=plt.figure(figsize=f_size)
ax1 =plt.axes([a1_left, a1_bottom, a1_width, a1_height], axisbg='w')
plt.plot(x_drag_sol_sample, y_drag_sol_sample, '-b', lw=2, label='with drag')
plt.plot(x_ref_sample, y_ref_sample, '--k', lw=2, label='without drag')
plt.axvline(x=xstar, color=[0.3, 0.3, 0.3], lw=1.0)
plt.axhline(y=0.0, color=[0.3, 0.3, 0.3], lw=1.0)
plt.legend()
plt.text(0.1*xstar, 0.6*ystar,
r'$v_0=%5.2f$'%(v0_sol_sample)+'\n'+r'$\theta=%5.2f \pi$'%(theta_sol_sample/np.pi),
fontsize=18)
plt.text(xstar, 0.5*ystar, 'xstar', fontsize=18)
plt.xlabel(r'Horizontal Distance $x$', fontsize=18)
plt.ylabel(r'Height $y$', fontsize=18)
ax1.set_xlim([0.0, 1.5*xstar])
ax1.set_ylim([-0.1*ystar, 1.5*ystar])
plt.savefig('fig_traj_sample.png')
print('figure file genereated: fig_traj_sample.png')
plt.close()
outf.close()
Here is the figure fig_xdrop_v0_theta.png.
Here is the figure fig_traj_sample.png.
Edit 2018-07-15
I realized that I overlooked that the question considers the drag by air. What a shame on me. So, my answer below is not correct. I'm afraid that deleting my answer by myself looks like hiding a mistake, and I leave it below for now. If people think it's annoying that an incorrect answer hanging around, I'm O.K. someone delete it.
The differential equation can actually be solved by hand,
and it does not require much computational resource
to map out how far the person reach from the balcony
on the ground as a function of the initial velocity v0 and the
angle theta. Then, you can select the condition (v0,theta)
such that distance_from_balcony_on_the_ground(v0,theta) = xstar
from this data table.
Let's write the horizontal and vertical coordinates of the
person at time t is x(t) and y(t), respectively.
I think you took x=0 at the wall of the building and y=0
as the ground level, and I do so here, too. Let's say the
horizontal and vertical velocity of the person at time t
are v_x(t) and v_y(t), respectively.
The initial conditions at t=0 are given as
x(0) = 0
y(0) = ystar
v_x(0) = v0 cos theta
v_y(0) = v0 sin theta
The Newton eqution you are solving is,
dx/dt = v_x .. (1)
dy/dt = v_y .. (2)
m d v_x /dt = 0 .. (3)
m d v_y /dt = -m g .. (4)
where m is the mass of the person,
and g is the constant which I don't know the English name of,
but we all know what it is.
From eq. (3),
v_x(t) = v_x(0) = v0 cos theta.
Using this with eq. (1),
x(t) = x(0) + \int_0^t dt' v_x(t') = t v0 cos theta,
where we also used the initial condition. \int_0^t means
integral from 0 to t.
From eq. (4),
v_y(t)
= v_y (0) + \int_0^t dt' (-g)
= v0 sin theta -g t,
where we used the initial condition.
Using this with eq. (3) and also using the initial condition,
y(t)
= y(0) + \int_0^t dt' v_y(t')
= ystar + t v0 sin theta -t^2 (g/2).
where t^2 means t squared.
From the expression for y(t), we can get the time tstar
at which the person hits the ground. That is, y(tstar) =0.
This equation can be solved by quadratic formula
(or something similar name) as
tstar = (v0 sin theta + sqrt((v0 sin theta)^2 + 2g ystar)/g,
where I used a condition tstar>0. Now we know
the distance from the balcony the person reached when he hit
the ground as x(tstar). Using the expression for x(t) above,
x(tstar) = (v0 cos theta) (v0 sin theta + sqrt((v0 sin theta)^2 + 2g ystar))/g.
.. (5)
Actually x(tstar) depends on v0 and theta as well as g and ystar.
You hold g and ystar as constants, and you want to find
all (v0,theta) such that x(tstar) = xstar for a given xstar value.
Since the right hand side of eq. (5) can be computed cheaply,
you can set up grids for v0 and theta and compute xstar
on this 2D grid. Then, you can see where roughly is the solution set
of (v0,theta) lies. If you need precise solution, you can pick up
a segment which encloses the solution from this data table.
Below is a python script that demonstrates this idea.
I also attach here a figure generated by this script.
The yellow curve is the solution set (v0,theta) such that the
person hit the ground at xstar from the wall
when xstar = 8.0*1.95 and ystar=4.0*1.95 as you set.
The blue color coordinate indicates x(tstar), i.e., how far the
person jumped from the balcony horizontally.
Note that at a given v0 (higher than a threshold value aruond v0=9.9),
the there are two theta values (two directions for the person
to project himself) to reach the aimed point (x,y) = (xstar,0).
The smaller branch of the theta value can be negative, meaning that the person can jump downward to reach the aimed point, as long as the initial velocity is sufficiently high.
The script also generates a data file output.dat, which has
the solution-enclosing segments.
#!/usr/bin/python3
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.image as img
mpl.rcParams['lines.linewidth'] = 2
mpl.rcParams['lines.markeredgewidth'] = 1.0
mpl.rcParams['axes.formatter.limits'] = (-4,4)
#mpl.rcParams['axes.formatter.limits'] = (-2,2)
mpl.rcParams['axes.labelsize'] = 'large'
mpl.rcParams['xtick.labelsize'] = 'large'
mpl.rcParams['ytick.labelsize'] = 'large'
mpl.rcParams['xtick.direction'] = 'out'
mpl.rcParams['ytick.direction'] = 'out'
############################################
len_ref = 1.95
xstar = 8.0*len_ref
ystar = 4.0*len_ref
g_earth = 9.81
############################################
ngv0=100
v0min =0.0
v0max =20.0
v0_grid=np.linspace(v0min, v0max, ngv0)
############################################
outf=open('output.dat','w')
print('data file generated: output.dat')
###########################################
def x_at_tstar(v0, theta, ystar, g_earth):
vx = v0*np.cos(theta)
vy = v0*np.sin(theta)
return (vy+np.sqrt(vy**2+2.0*g_earth*ystar))*vx/g_earth
ngtheta=100
theta_min = -0.5*np.pi
theta_max = 0.5*np.pi
theta_grid = np.linspace(theta_min, theta_max, ngtheta)
xdrop=np.empty((ngv0,ngtheta))
# x(t*) as a function of v0 and theta.
for j1 in range(ngv0):
for j2 in range(ngtheta):
xdrop[j1,j2] = x_at_tstar(v0_grid[j1], theta_grid[j2], ystar, g_earth)
outf.write('# domain [theta_lower, theta_upper] that encloses the solution\n')
outf.write('# theta such that x_at_tstart(v0,theta, ystart, g_earth)=xstar\n')
outf.write('# v0 theta_lower theta_upper x_lower x_upper\n')
for j1 in range(ngv0):
for j2 in range(ngtheta-1):
if (xdrop[j1,j2+1]-xstar)*(xdrop[j1,j2]-xstar)<=0.0:
outf.write('%26.16e %26.16e %26.16e %26.16e %26.16e\n'
%(v0_grid[j1], theta_grid[j2], theta_grid[j2+1],
xdrop[j1,j2], xdrop[j1,j2+1]))
print('See output.dat for the segments enclosing solutions.')
print('You can hunt further for precise solutions using this data.')
#######################################################################
# canvas setting
#######################################################################
f_size = (8,5)
#
a1_left = 0.15
a1_bottom = 0.15
a1_width = 0.65
a1_height = 0.80
#
hspace=0.02
#
ac_left = a1_left+a1_width+hspace
ac_bottom = a1_bottom
ac_width = 0.03
ac_height = a1_height
###########################################
############################################
# plot
############################################
print('plotting..')
fig1=plt.figure(figsize=f_size)
ax1 =plt.axes([a1_left, a1_bottom, a1_width, a1_height], axisbg='w')
im1=img.NonUniformImage(ax1,
interpolation='bilinear', \
cmap=mpl.cm.Blues, \
norm=mpl.colors.Normalize(vmin=0.0,
vmax=np.max(xdrop), clip=True))
im1.set_data(v0_grid, theta_grid/np.pi, np.transpose(xdrop))
ax1.images.append(im1)
plt.contour(v0_grid, theta_grid/np.pi, np.transpose(xdrop), [xstar], colors='y')
plt.xlabel(r'Initial Velocity $v_0$', fontsize=18)
plt.ylabel(r'Angle of Projection $\theta/\pi$', fontsize=18)
plt.yticks([-0.50, -0.25, 0.0, 0.25, 0.50])
ax1.set_xlim([v0min, v0max])
ax1.set_ylim([theta_min/np.pi, theta_max/np.pi])
axc =plt.axes([ac_left, ac_bottom, ac_width, ac_height], axisbg='w')
mpl.colorbar.Colorbar(axc,im1)
# 'Distance from Blacony $x(t^*)$'
plt.savefig('fig_xdrop_v0_theta.png')
print('figure file genereated: fig_xdrop_v0_theta.png')
plt.close()
outf.close()
So after some trying out I found a way to achieve what I wanted... It is the brute force method that I mentioned in my starting post, but at least now it works...
The idea is quite simple: define a function find_v0 which finds for a given theta a v0. In this function you take a starting value for v0 (I choose 8 but this was just a guess from me), then take the starting value and check with the difference function how far away the interesting point is from (xstar,0). The interesting point in this case can be determined by setting all points on the x-axis that are bigger than xstar to zero (and their corresponding y-values) and then trimming of all the zeros with trim_zeros, now the last element of correspond to the desired output. If the output of the difference function is smaller than a critical value (in my case 0.1) pass the current v0 on, if not, increase it by 0.01 and do the same thing again.
The code for this looks like this (again replacing 3) and 4) ):
th = np.linspace(0,np.pi/3,100)
def find_v0(theta):
v0=8
while(True):
v0x = v0 * np.cos(theta)
v0y = v0 * np.sin(theta)
z0 = np.array([0, v0x, ystar, v0y])
# Calculate solution
t, z = explicit_midpoint(rhs, z0, 5, 1000)
for k in range(1001):
if z[k,0] > xstar:
z[k,0] = 0
z[k,2] = 0
x = np.trim_zeros(z[:,0])
y = np.trim_zeros(z[:,2])
diff = difference(x[-1],y[-1])
if diff < 0.1:
break
else: v0+=0.01
return v0#,x,y[0:]
v0 = np.zeros_like(th)
from tqdm import tqdm
count=0
for k in tqdm(th):
v0[count] = find_v0(k)
count+=1
v0_interp = interpolate.interp1d(th,v0)
plt.figure()
plt.plot(th,v0_interp(th),"g")
plt.grid(True)
plt.xlabel(r"$\theta$")
plt.ylabel(r"$v_0$")
plt.show()
The problem with this thing is that it takes forever to compute (with the current settings around 5-6 mins). If anyone has some hints how to improve the code to get a little bit faster or has a different approach it would be still appreciated.
Assuming that the velocity in x direction never goes down to zero, you can take x as independent parameter instead of the time. The state vector is then time, position, velocity and the vector field in this state space is scaled so that the vx component is always 1. Then integrate from zero to xstar to compute the state (approximation) where the trajectory meets xstar as x-value.
def derivs(u,x):
t,x,y,vx,vy = u
v = hypot(vx,vy)
ax = -lam*v*vx
ay = -lam*v*vy - g
return [ 1/vx, 1, vy/vx, ax/vx, ay/vx ]
odeint(derivs, [0, x0, y0, vx0, vy0], [0, xstar])
or with your own integration method. I used odeint as documented interface to show how this derivatives function is used in the integration.
The resulting time and y-value can be extreme

Categories

Resources