So I'm coding a snooker game, and I decided the best way to figure out how to make the balls collide with one another was to do so in a separate program then copy it in. I'm a pretty competent mathematician, so I've sat down, drawn the event and gone through the maths of what's actually happening.
My approach is to break each balls' initial velocity into xr- and yr-components, in a frame of reference in which the xr-component is in line with the vector through the center of each ball, and the yr-component is perpendicular to this. I then do a simple switch of the xr components of the balls and leave the yr components as they are, then calculate what the x- and y- components of the velocity are back in the standard reference frame.
For some reason, whether it be through mathematical or programming error, I can't seem to get it to work. The following is what I have so far and I have looked over virtually every related page on the internet I could find and all the similar-ish questions asked on this site. I'm not exactly a well versed programmer either.
from visual import *
dt = 0.01
r = 5
red = sphere(pos=(-25,25,0),radius = r,color=color.red)
green = sphere(pos=(25,-25,0),radius = r,color=color.green)
red.velocity = vector(10,-10,0)
green.velocity = vector(-10,10,0)
def posupdate(ball):
ball.pos = ball.pos + ball.velocity*dt
def ballhit(ball1,ball2):
v1 = ball1.velocity
v1x = ball1.velocity.x
v1y = ball1.velocity.y
v2 = ball2.velocity
v2x = ball2.velocity.x
v2y = ball2.velocity.y
xaxis = vector(1,0,0)
btb = ball2.pos - ball1.pos
nbtb = btb/abs(btb)
if abs(btb) < 2*r:
phi = acos(dot(nbtb,xaxis)/abs(nbtb)*abs(xaxis))
ang1 = acos(dot(v1,xaxis)/abs(v1)*abs(xaxis))
ang2 = acos(dot(v2,xaxis)/abs(v2)*abs(xaxis))
v1xr = abs(v1)*cos((ang1-phi))
v1yr = abs(v1)*sin((ang1-phi))
v2xr = abs(v2)*cos((ang2-phi))
v2yr = abs(v2)*sin((ang2-phi))
v1fxr = v2xr
v2fxr = v1xr
v1fyr = v1yr
v2fyr = v2yr
v1fx = cos(phi)*v1fxr+cos(phi+pi/2)*v1fyr
v1fy = sin(phi)*v1fxr+sin(phi+pi/2)*v1fyr
v2fx = cos(phi)*v2fxr+cos(phi+pi/2)*v2fyr
v2fy = sin(phi)*v2fxr+sin(phi+pi/2)*v2fyr
ball1.velocity.x = v1fx
ball1.velocity.y = v1fy
ball2.velocity.x = v2fx
ball2.velocity.y = v2fy
return ball1.velocity, ball2.velocity
while 1==1:
rate(100)
posupdate(red)
posupdate(green)
ballhit(red,green)
Thanks in advance for any help you can give.
Edit: The collision detection is no issue, just the calculation of the velocity vectors of the balls after the collision. Apologies, I should have made that clearer.
Check out Physics of Billiards:
Conservation of momentum applies and, assuming inelastic collisions, kinetic energy is conserved. So you get the following vector equations (subscripts _0 and _1 indicate before and after collision):
m1*v1_0 + m2*v2_0 = M1*v1_1 + m2*v2_1
0.5*m1*v1_0**2 + 0.5*m2*v2_0**2 = 0.5*m1*v1_1**2 + 0.5*m2*v2_1**2
Usually m1 == m2, so these simplify to:
v1_0 + v2_0 = v1_1 + v2_1
v1_0**2 + v2_0**2 = v1_1**2 + v2_1**2
Related
I am working on a project related to charge distribution on the sphere and I decided to simulate the problem using vpython and Coulomb's law. I ran into an issue when I created a sphere because I am trying to evenly place out like 1000 points (charges) on the sphere and I can't seem to succeed, I have tried several ways but can't seem to make the points be on the sphere.
I defined an arbitrary value SOYDNR as a number to divide the diameter of the sphere into smaller segments. This would allow me to create a smaller rings of charges and fill out the surface of the spahre with charges. Then I make a list with 4 values that represent different parts of the radius to create the charge rings on the surface. Then I run into a problem and I am not sure how to deal with it. I have tried calculating the radius at those specific heights but yet I couldn't implement it. This is how it looks visually:![Sphere with charges on the surface].(https://i.stack.imgur.com/3N4x6.png) If anyone has any suggestions, I would be greatful, thanks!
SOYDNR = 10 #can be changed
SOYD = 2*radi/SOYDNR # strips of Y direction; initial height + SOYD until = 2*radi
theta = 0
dtheta1 = 2*pi/NCOS
y_list = np.arange(height - radi + SOYD, height, SOYD).tolist()
print(y_list)
for i in y_list:
while Nr<NCOS and theta<2*pi:
position = radi*vector(cos(theta),i*height/radi,sin(theta))
points_on_sphere = points_on_sphere + [sphere(pos=position, radius=radi/50, color=vector(1, 0, 0))]
Nr = Nr + 1
theta = theta + dtheta1
Nr = 0
theta = 0
I found a great way to do it, it creates a bunch of spheres in the area that is described by an if statement this is the code I am using for my simulation that creates the sphere with points on it.
def SOSE (radi, number_of_charges, height):
Charged_Sphere = sphere(pos=vector(0,height,0), radius=radi, color=vector(3.5, 3.5, 3.5), opacity=(0.2))
points_on_sphere = []
NCOS = number_of_charges
theta = 0
dtheta = 2*pi/NCOS
dr = radi/60
direcVector = vector(0, height, 0)
while theta<2*pi:
posvec1 = radi*vector(1-radi*random(),1-radi*random()/radi,1-radi*random())
posvec2 = radi*vector(1-radi*random(),-1+radi*random()/radi,1-radi*random())
if mag(posvec1)<radi and mag(posvec1)>(radi-dr):
posvec1 = posvec1+direcVector
points_on_sphere=points_on_sphere+[sphere(pos=posvec1,radius=radi/60,color=vector(1, 0, 0))]
theta=theta + dtheta
if mag(posvec2)<radi and mag(posvec2)>(radi-dr):
posvec2 = posvec2+direcVector
points_on_sphere=points_on_sphere+[sphere(pos=posvec2,radius=radi/60,color=vector(1, 0, 0))]
theta=theta + dtheta
This code can be edited to add more points and I have two if statements because I want to change the height at which the sphere is present, and if I have just one statement I only see half of the sphere. :)
May seem like a silly question is there any way to create variables depending on the number of objects in a list. The context to this is I am trying to create an n body simulation. One in which the user can manually chose the planets they want in the solar system and then run the script with only those planets. So prior to them running the chosing the planets and running the script I will not know how many variables to create. This problem of how many variables to create is show by:
The mass is created by a class objects:
class Objects():
position = np.full((1, 3), 0)
velocity = np.full((1, 3), 0)
acceleration = np.full((1, 3), 0)
name = ""
mass = np.full((1, 1), 0)
planets_init = np.full((1, 3), 0)
def __init__(self, Name, Mass, initPosition, initVelocity, initAcceleration):
au = 149597870.700e3
v_factor = 1731460
self.name = Name
self.mass = Mass
The function is solved by using scipy.integrate.solve_ivp by:
three_body_sol = sci.integrate.solve_ivp(fun=Objects.ThreeBodyEquations,t_span=domain,y0=init_params,args=(G,planets_mass,N), max_step=max_step)
Where the function is:
def ThreeBodyEquations(t,w,G,mass,N):
# N being the number of objects in the system so in this case 2
m1, m2 = mass
#Unpack all the variables from the array "w"
r1=w[:3]
r2=w[3:6]
v1=w[6:9]
v2=w[9:12]
# Harry's attempt
G = G
planets_pos = np.vstack((r1, r2))
planets_mass = mass # np.vstack((m1, m2))
# positions r = [x,y,z] for all particles
x = planets_pos[:,0:1]
y = planets_pos[:,1:2]
z = planets_pos[:,2:3]
# matrix that stores all pairwise particle separations: r_j - r_i
dx = x.T - x
dy = y.T - y
dz = z.T - z
# matrix that stores 1/r^3 for all particle pairwise particle separations
inv_r3 = (dx**2 + dy**2 + dz**2)
inv_r3[inv_r3>0] = inv_r3[inv_r3>0]**(-1.5)
ax = G * (dx * inv_r3) # planets_mass
ay = G * (dy * inv_r3) # planets_mass
az = G * (dz * inv_r3) # planets_mass
# planets_acceleration = np.sqrt(ax**2 + ay**2 + az**2)
planets_acceleration = np.vstack((ax,ay,az))
planets_acceleration = planets_acceleration.flatten()
dv1bydt=planets_acceleration[0::N]
dv2bydt=planets_acceleration[1::N]
# print(planets_acceleration)
dr1bydt=v1
dr2bydt=v2
#Package the derivatives into one final size-18 array
r12_derivs=np.concatenate((dr1bydt,dr2bydt))
r_derivs = r12derivs
v12_derivs=np.concatenate((dv1bydt,dv2bydt))
v_derivs= v12_derivs
derivs=np.concatenate((r_derivs,v_derivs))
return derivs
My main question centres around this function. When the user defines what planets they want to use I have no idea what the number of planets will be. I know the range which is might be but that’s all. Is there a feasible way to do this or is this a lost cause?
I hope this clarifys the question with some additional code. Sorry about the previous lack of code its, I didn’t realise it was valuable at first and didn’t want to burden with too much code.
I would create a function that calculates the mass first, such as
mass = 0
for planet in planet_list:
mass = mass+ planet.mass
return mass
total_mass = calculate_combined_mass([earth, Mars, jupiter] )
three_body_equations(t,w,G,total_mass,N)
I've been following along with a tutorial, which models Earth, Venus, and the Sun. The code runs just fine. However, I'm trying to model a satellite's orbit about the earth, where the earth is the center of its "universe" at position (0,0). I'm running into issues as my satellite flies off on a tangent but I'm not sure why.
import numpy as np
import numpy as np
class Planet():
def __init__(self,vx=0.0,vy=0.0,px=0.0,py=0.0,mass=0.0):
self.vx=vx
self.vy=vy
self.px=px
self.py=py
self.mass = mass
def attract(p1,p2,grav=6.67428e-11):
dx = p1.px - p2.px
dy = p1.py - p2.py
d = np.sqrt(dx**2 + dy**2)
force = (grav * p1.mass * p2.mass)/d**2
theta = math.atan2(dy,dx)
fx = math.cos(theta)*force
fy = math.sin(theta)*force
return fx,fy
def loop(bodies,epochs=10,timestep=10):
# compute forces
forces = {}
for i in range(0,len(bodies)):
total_fx = 0
total_fy = 0
for j in range(0,len(bodies)):
if i == j:
continue
fx,fy = attract(bodies[i],bodies[j])
total_fx += fx
total_fy += fy
forces[i] = (total_fx,total_fy)
#apply forces
sat_tups = []
for e in range(epochs):
for i in range(len(bodies)):
if bodies[i] == satellite:
sat_tups.append((bodies[i].px,bodies[i].py))
fx,fy = forces[i]
bodies[i].vx += (fx/bodies[i].mass*timestep)
bodies[i].vy += (fy/bodies[i].mass*timestep)
bodies[i].px += bodies[i].vx*timestep
bodies[i].py += bodies[i].vy*timestep
return sat_tups
earth = Planet(mass = 5.9742 * 10**24)
satellite = Planet(mass= 3000.0, px= 1414.0, py= 1414.0, vx=2300.0,vy=2300.0)
tups = loop(bodies=[earth,satellite])
tups
>>>
[(1414.0, 1414.0),
(7050856421.79636, 7050856421.79636),
(21152543437.38908, 21152543437.38908),
(42305062460.77817, 42305062460.77817),
(70508413491.96361, 70508413491.96361),
(105762596530.9454, 105762596530.9454),
(148067611577.72357, 148067611577.72357),
(197423458632.2981, 197423458632.2981),
(253830137694.66898, 253830137694.66898),
(317287648764.83624, 317287648764.83624)]
I'm not sure what the issue is. Perhaps, centering Earth at (0,0) simply isn't going to work. Or perhaps these laws of physics are only applicable when bodies are of relatively similar sizes (else more intricate rules are at play.)
To answer this question, please address what is causing the satellite to fly off? (I would have guessed that it would crash into earth, not sling-shot away).
I'm plotting in a simple 2D plane the path taken by a body in motion past another gravitationally attractive body.
Every loop, the time is incremented by a second and the body's new position is calculated and printed out.
I then paste the results into a spreadsheet and graph it.
Things look ok until the body's x component becomes negative - and then the trajectory goes linear, and scoots off to the top left.
This is all for my own solo entertainment, I'm no student. So after scratching my head for a bit I've finally lumped for asking someone for help.
I've probably missed something obvious. I suspect my trigonometry is lacking something.
I'm using Python 2.7.10
import sys
import os
import math
mu = 4.0*(10**14)
massAst = 1
earthRadius = 6371000.
alt = 100000.
r = earthRadius+ alt
rTheta = 270.
rAngtoX = math.radians(rTheta)
tInc = 1 ## increment time by 1 seconds - one update of pos&velocity per second of travel
calcTime = 1100 ## simulation runtime (86400 seconds = 1 day) 10 mins
t = 1 ## integral of time t to use in the calcs in the loop.
printbell = 120 ## print results now
printclock = 0
hourCount = 0
## Initialise velocity vectors for Asteroid:
uAstX = 1500.
uAstY = 0.
vAstX = 0.
vAstY = 0.
## Displacement
dAstX = r*math.cos(rAngtoX)
dAstY = r*math.sin(rAngtoX)
for i in range(0, calcTime):
acc = -1*(mu/r**2)
accX = acc*math.cos(rAngtoX)
accY = acc*math.sin(rAngtoX)
vAstX = uAstX + accX*t ## new value for velocity in X direction
vAstY = uAstY + accY*t ## and in Y
deltaDAstX = uAstX*t + 0.5*accX*(t**2) ## change in position over this time interval
deltaDAstY = uAstY*t + 0.5*accY*(t**2)
dAstX = dAstX + deltaDAstX
dAstY = dAstY + deltaDAstY
uAstX = vAstX
uAstY = vAstY
## Now calculate new angle and range
## tan(theta) = dAstY/dAstX, so:
rAngtoX = math.atan(dAstY/dAstX) ##+(2*3.141592654)
##print 'theta:', math.degrees(rAngtoX)
r = dAstY/math.sin(rAngtoX)
## if i == print
print dAstX, ' ', dAstY
As dAstX approaches zero, dAstY/dAstX will approach a division-by-zero... Which will cause all sorts of problems (roundoff issues at the very least).
I'd recommend keeping the x/y components of distance/velocity/acceleration separate. The distance between the objects is important, of course, but that can be calculated using r=sqrt(dAstX**2 + dAstY**2).
I'm not familiar with the math, but I took your code, modified it to run on my machine, plottet the data using the seaborn library and I came up with this:
This is the code I used:
import math
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
def calc(calcTime):
mu = 4.0*(10**14)
earthRadius = 6371000.0
alt = 100000.0
r = earthRadius+ alt
rTheta = 270.0
rAngtoX = math.radians(rTheta)
t = 1 # integral of time t to use in the calcs in the loop.
# Initialise velocity vectors for Asteroid:
uAstX = 1500.0
uAstY = 0.0
# Displacement
dAstX = r*math.cos(rAngtoX)
dAstY = r*math.sin(rAngtoX)
point_list = []
for i in range(0, calcTime):
acc = -1*(mu/r**2)
accX = acc*math.cos(rAngtoX)
accY = acc*math.sin(rAngtoX)
vAstX = uAstX + accX*t # new value for velocity in X direction
vAstY = uAstY + accY*t # and in Y
deltaDAstX = uAstX*t + 0.5*accX*(t**2) # change in pos over time interval
deltaDAstY = uAstY*t + 0.5*accY*(t**2)
dAstX = dAstX + deltaDAstX
dAstY = dAstY + deltaDAstY
uAstX = vAstX
uAstY = vAstY
# Now calculate new angle and range
# tan(theta) = dAstY/dAstX, so:
rAngtoX = math.atan(dAstY/dAstX) #+(2*3.141592654)
# print 'theta:', math.degrees(rAngtoX)
r = dAstY/math.sin(rAngtoX)
# if i == print
if i % 5 == 0:
print('{:05d} | {:15.2f} | {:15.2f}'.format(i, dAstX, dAstY))
point_list.append((i, dAstX, dAstY))
df = pd.DataFrame(data=point_list, columns=['i', 'x', 'y'])
return df
if __name__ == '__main__':
df = calc(950)
sns.scatterplot(data=df, x='x', y='y')
plt.show()
My analysis: the spaces between dots gets bigger each time (note: I only plottet every 5th point to make the image more readabl,e in my opinion). From a physics perspective, that would indicate that the object is gaining speed.
Isn't it possible that your calculations are correct and that the object is leaving the orbit because it gained enough speed (aka energy) to leave the gravitational field of the center mass (aka the earth) ?
As I said, I'm not familiar with the specific math, but to me it makes sense that the object could break free from the orbit with the speed it is gaining in the half turn.
I am still very new to Python. I am heading a project to map the building footprints within our county on the tax map.
I have found a previous question that may be very helpful for this project: https://gis.stackexchange.com/questions/6724/creating-line-of-varying-distance-from-origin-point-using-python-in-arcgis-deskt
Our Cama system generates views/table with the needed information. Below is an example:
PARID LLINE VECT X_COORD Y_COORD
1016649 0 R59D26L39U9L20U17 482547 1710874
180,59,270,26,0,39,90,9,0,20,90,17 (VECT column converted)
I have found some python examples to convert the VECT column, which are distance and direction calls to angles and distances separated by commas.
My question: Is there a way to implement a loop into the script below to utilize a table rather than static, user entered, numbers? This would be very valuable to the county as we have several thousand polygons to construct.
Below is the snippet to change the distances and angles to x, y points to be generated in ArcMap 10.2
#Using trig to deflect from a starting point
import arcpy
from math import radians, sin, cos
origin_x, origin_y = (400460.99, 135836.7)
distance = 800
angle = 15 # in degrees
# calculate offsets with light trig
(disp_x, disp_y) = (distance * sin(radians(angle)),\
distance * cos(radians(angle)))
(end_x, end_y) = (origin_x + disp_x, origin_y + disp_y)
output = "offset-line.shp"
arcpy.CreateFeatureClass_management("c:\workspace", output, "Polyline")
cur = arcpy.InsertCursor(output)
lineArray = arcpy.Array()
# start point
start = arcpy.Point()
(start.ID, start.X, start.Y) = (1, origin_x, origin_y)
lineArray.add(start)
# end point
end = arcpy.Point()
(end.ID, end.X, end.Y) = (2, end_x, end_y)
lineArray.add(end)
# write our fancy feature to the shapefile
feat = cur.newRow()
feat.shape = lineArray
cur.insertRow(feat)
# yes, this shouldn't really be necessary...
lineArray.removeAll()
del cur
Any suggestions would be greatly appreciated.
Thank you for your valuable time and knowledge.
You can create a dictionary of dictionaries from given table that would hold all the different values. Such as
d = {1:{"x":400460.99,"y":135836.7,"distance":800,"angle":15},
2:{"x":"etc","y":"etc","distance":"etc","angle":"etc"}}
for k in d.keys():
origin_x, d[k]["x"]
origin_y = d[k]["y"]
distance = d[k]["distance"]
angle = d[k]["angle"]
#rest of the code
#.....