Charged Particle Trajectories in Magnetic Fields - python-3.x

I've been trying to plot the trajectories of charged particles in the field of a magnetic dipole in an attempt to give a rough pictorial representation of the northern lights. While the spiraling appears to be what I would have expected, it looks as though the spirals start out tight and get wider, as if the particles are somehow gaining energy. I'm not sure what the issue is in the code, and I would appreciate any pointers!
Shown below are the contents of the main loop (initial conditions and all were set outside).
# polar to cartesian
r = np.sqrt(X*X+Y*Y+Z*Z)
theta = np.arccos(Z/r)
phi = np.arctan(Y/X)
# magnetic field/mass
Bx = K*(1/(r**(3)))*(2*np.cos(theta)*np.sin(theta)*np.cos(phi)+np.sin(theta)*np.cos(theta)*np.cos(phi))
By = K*(1/(r**(3)))*(2*np.cos(theta)*np.sin(theta)*np.sin(phi)+np.sin(theta)*np.cos(theta)*np.sin(phi))
Bz = K*(1/(r**(3)))*(2*np.cos(theta)*np.cos(theta)-np.sin(theta)*np.sin(theta))
# acceleration components
ax = (1.6*10**(-19))*(vy*Bz - vz*By)
ay = (1.6*10**(-19))*(vz*Bx - vx*Bz)
az = (1.6*10**(-19))*(vx*By - vy*Bx)
# velocity components
vx = vx + ax*dt
vy = vy + ay*dt
vz = vz + az*dt
# position components
X = X + vx*dt + 0.5*ax*dt*dt
Y = Y + vy*dt + 0.5*ay*dt*dt
Z = Z + vz*dt + 0.5*az*dt*dt
# add position values to position vectors
x1.append(X)
y1.append(Y)
z1.append(Z)
And a figure of what the current trajectories look like (the particles are starting out at the top in tighter spirals before gradually increasing their radii):
figure
(I'm using Python 3.6 for this project)

Turns out it was just a matter of the timestep being too large. After decreasing the time step size, the trajectories are much more reasonable.

Related

How to start this "Number Density of Particles" homework in Python?

Part 2 - Determination of Number Density of Particles
If we say that q is the production rate of particles of a specific size, then in an interval dt the total number of particles produced is just q dt. To make things concrete in what follows, please adopt the case:
a = 0.9amax
q = 100000
Consider this number of particles at some distance r from the nucleus. The number density of particles will be number divided by volume, so to find number density we must compute the volume of a shell of radius r with a thickness that corresponds to how far the particles will travel in our time interval dt. Obviously that’s just the velocity of the particle at radius r times the time interval v(r) dt, so the volume of our shell is:
Volume = Shell Surface Area×Shell Thickness = 4πr2v(r)dt
Therefore, the number density, n, at radius r is:
n(r) = q dt /4πr2v(r)dt = q /4πr2v(r) (equation5)
You will note that our expression above will have a singularity for the number density of particles right at the surface of the nucleus, since at that position the outward velocity, v(R), is 0. Obviously this is an indication that we expect the particle density n to drop very rapidly as the dust is accelerated away from the surface. For now, let’s not worry about this point — we don’t need it later — and just graph how the number density varies with distance from the nucleus, starting with the 1st point after the surface value
• Evaluate Eqaution 5 for all calculated points using the parameters for q and a given above.
• Make a log-log graph of the number density versus radius. You should find that, after terminal velocity is achieved, the number density decreases as r−2, corresponding to a slope of -2 on a log-log plot
Current code:
% matplotlib inline
import numpy as np
import matplotlib.pyplot as pl
R = 2000 #Nucleus Radius (m)
GM_n = 667 #Nucleus Mass (m^3 s^-2)
Q = 7*10**27 #Gas Production Rate (molecules s^-1)
V_g = 1000 #Gas Velocity (m s^-1)
C_D = 4 #Drag Coefficient Dimensionless
p_d = 500 #Grain Density (kg m^-3)
M_h2o = .01801528/(6.022*10**23) #Mass of a water molecule (g/mol)
pi = np.pi
p_g_R = M_h2o*Q/(4*np.pi*R**2*V_g)
print ('Gas Density at the comets nucleus: ', p_g_R)
a_max = (3/8)*C_D*(V_g**2)*p_g_R*(1/p_d)*((R**2)/GM_n)
print ('Radius of Maximum Size Particle: ', a_max)
def drag_force(C_D,V_g,p_g_R,pi,a,v):
drag = .5*C_D*((V_g - v)**2)*p_g_R*pi*a**2
return drag
def grav_force(GM_n,M_d,r):
grav = -(GM_n*M_d)/(r**2)
return grav
def p_g_r(p_g_R,R,r):
p_g_r = p_g_R*(R**2/r**2)
return p_g_r
dt = 1
tfinal = 100000
v0 = 0
t = np.arange(0.,tfinal+dt,dt)
npoints = len(t)
r = np.zeros(npoints)
v = np.zeros(npoints)
r[0]= R
v[0]= v0
a = np.array([0.9,0.5,0.1,0.01,0.001])*a_max
for j in range(len(a)):
M_d = 4/3*pi*a[j]**3*p_d
for i in range(len(t)-1):
rmid = r[i] + v[i]*dt/2.
vmid = v[i] + (grav_force(GM_n,M_d,r[i])+drag_force(C_D,V_g,p_g_r(p_g_R,R,r[i]),pi,a[j],v[i]))*dt/2.
r[i+1] = r[i] + vmid*dt
v[i+1] = v[i] + (grav_force(GM_n,M_d,rmid)+drag_force(C_D,V_g,p_g_r(p_g_R,R,rmid),pi,a[j],vmid))*dt
pl.plot(r,v)
pl.show()
a_2= 0.9*a_max
q = 100000
I have never programmed anything like this before, my class is very difficult for me and I don't understand it. I have developed the above code with the help of the professor, and I am nearly out of time to finish this project. I just want help understanding the problem.
How do I find v(r) when I only have v(t), r(t)?
What do I do to calculate the r values and what r values do I even use?
You have v as a known function of time and also r as another known function of time. You can invert these to get t vs. v and t vs. r. To get v as a function of r, eliminate t.

Simple bouncing ball in haskell keeps bouncing higher

So, I did a simple simulation of a bouncing ball to learn haskell. The ball is supposed to bounce against the image borders and to be accelerated downward by gravity.
The problem is that the ball is "magically" bouncing higher as time passes. I would expect it to keep the same maximum height instead.
I suspect there is something wrong with bounce and not with move because bounce "teleports" the ball back inside the frame, which is not physically accurate. I tried different way to simulate a correct behavior but could not find anything working right.
What would be a correct way to do this?
Here is the code, running on CodeWorld:
main = simulationOf initial step draw
data World = Ball Point Vector
radius = 40
border = 250 - radius
g = -500
initial (x:y:vx:vy:_) = Ball (400*x - 200, 400*y - 200)
(400*vx - 200, 400*vy - 200)
step t world = bounce (move t world)
move t (Ball (x,y) (vx,vy)) = Ball (x + vx*t, y + vy*t) (vx, vy + g*t)
bounce (Ball (x,y) (vx,vy)) = Ball (nx,ny) (nvx, nvy)
where nx = fence (-border) border x
ny = fence (-border) border y
nvx = if nx /= x then -vx else vx
nvy = if ny /= y then -vy else vy
fence lo hi x = max lo (min hi x)
draw (Ball (x,y) _) = translate x y (solidCircle radius)
This is a well-known artifact of the algorithm you're using to integrate the movement's differential equation.
The "real physics" is (I will only discuss the y component)
∂y/∂t = v(t)
∂v/∂t = g
You model this by a discrete sequence of heights and velocities
yi = yi − 1 + vi − 1 ⋅ Δt
vi = vi − 1 + g ⋅ Δt
This sure resembles the differential equations, just written as difference quotients – but it's not the same thing (except in the limit Δt → 0): in reality, the velocity itself changes during the time step, so it's not quite correct to alter the position just according the constant v value before that time step. Simply ignoring that complication is an approximation called Euler's method, and it's known to suck rather badly.
The much more accurate standard alternative is the fourth-order Runge-Kutta method, give that a try.
n.m.'s points about the bouncing is also valid, though to do it really properly you should calculate the exact time of impact, to neither neglect too much acceleration nor get too much that actually never happened.
Suppose the ball hits the ground exactly in the middle of the quantum of time. In reality (or rather in the frictionless "reality") the absolute value of velocity stays the same in the beginning and in the end of the quantum, but in your model it increases.
One should handle acceleration in bounce. The simplest possible way to do that is to do this:
move t (Ball (x,y) (vx,vy)) = Ball (x + vx*t, y + vy*t) (vx, vy)
step t world = bounce (move t world) t
bounce (Ball (x,y) (vx,vy)) t = Ball (nx,ny) (nvx, nvy)
...
nvy = if ny /= y then -vy else vy + g * t
The velocity won't increase during the bounce.
This is still not entirely accurate, the velocity still creeps up, but slower.

find point where barycentric weights have a specific value

I have triangle: a, b, c. Each vertex has a value: va, vb, vc. In my software the user drags point p around inside and outside of this triangle. I use barycentric coordinates to determine the value vp at p based on va, vb, and vc. So far, so good.
Now I want to limit p so that vp is within range min and max. If a user chooses p where vp is < min or > max, how can I find the point closest to p where vp is equal to min or max, respectively?
Edit: Here is an example where I test each point. Light gray is within min/max. How can I find the equations of the lines that make up the min/max boundary?
a = 200, 180
b = 300, 220
c = 300, 300
va = 1
vb = 1.4
vc = 3.2
min = 0.5
max = 3.5
Edit: FWIW, so far first I get the barycentric coordinates v,w for p using the triangle vertices a, b, c (standard stuff I think, but looks like this). Then to get vp:
u = 1 - w - v
vp = va * u + vb * w + vc * v
That is all fine. My trouble is that I need the line equations for min/max so I can choose a new position for p when vp is out of range. The new position for p is the point closest to p on the min or max line.
Note that p is an XY coordinate and vp is a value for that coordinate determined by the triangle and the values at each vertex. min and max are also values. The two line equations I need will give me XY coordinates for which the values determined by the triangle are min or max.
It doesn't matter if barycentric coordinates are used in the solution.
The trick is to use the ratio of value to cartesian distance to extend each triangle edge until it hits min or max. Easier to see with a pic:
The cyan lines show how the triangle edges are extended, the green Xs are points on the min or max lines. With just 2 of these points we know the slope if the line. The yellow lines show connecting the Xs aligns with the light gray.
The math works like this, first get the value distance between vb and vc:
valueDistBtoC = vc - vb
Then get the cartesian distance from b to c:
cartesianDistBtoC = b.distance(c)
Then get the value distance from b to max:
valueDistBtoMax = max - vb
Now we can cross multiply to get the cartesian distance from b to max:
cartesianDistBtoMax = (valueDistBtoMax * cartesianDistBtoC) / valueDistBtoC
Do the same for min and also for a,b and c,a. The 6 points are enough to restrict the position of p.
Consider your triangle to actually be a 3D triangle, with points (ax,ay,va), (bx,by,vb), and (cx,cy,vc). These three points define a plane, containing all the possible p,vp triplets obtainable through barycentric interpolation.
Now think of your constraints as two other planes, at z>=max and z<=min. Each of these planes intersects your triangle's plane along an infinite line; the infinite beam between them, projected back down onto the xy plane, represents the area of points which satisfy the constraints. Once you have the lines (projected down), you can just find which (if either) is violated by a particular point, and move it onto that constraint (along a vector which is perpendicular to the constraint).
Now I'm not sure about your hexagon, though. That's not the shape I would expect.
Mathematically speaking the problem is simply a change of coordinates. The more difficult part is finding a good notation for the quantities involved.
You have two systems of coordinates: (x,y) are the cartesian coordinates of your display and (v,w) are the baricentric coordinates with respect to the vectors (c-a),(b-a) which determine another (non orthogonal) system.
What you need is to find the equation of the two lines in the (x,y) system, then it will be easy to project the point p on these lines.
To achieve this you could explicitly find the matrix to pass from (x,y) coordinates to (v,w) coordinates and back. The function you are using toBaryCoords makes this computation to find the coordinates (v,w) from (x,y) and we can reuse that function.
We want to find the coefficients of the transformation from world coordinates (x,y) to barycentric coordinates (v,w). It must be in the form
v = O_v + x_v * x + y_v * y
w = O_w + x_w * x + y_w * y
i.e.
(v,w) = (O_v,O_w) + (x_v,y_y) * (x,y)
and you can determine (O_v,O_w) by computing toBaryCoord(0,0), then find (x_v,x_w) by computing the coordinates of (1,0) and find (y_v,y_w)=toBaryCoord(1,0) - (O_v,O_w) and then find (y_v,y_w) by computing (y_v,y_w) = toBaryCoord(0,1)-(O_v,O_w).
This computation requires calling toBaryCoord three times, but actually the coefficients are computed inside that routine every time, so you could modify it to compute at once all six values.
The value of your function vp can be computed as follows. I will use f instead of v because we are using v for a baricenter coordinate. Hence in the following I mean f(x,y) = vp, fa = va, fb = vb, fc = vc.
You have:
f(v,w) = fa + (fb-fa)*v + (fc-fa)*w
i.e.
f(x,y) = fa + (fb-fa) (O_v + x_v * x + y_v * y) + (fc-fa) (O_w + x_w * x + y_w * y)
where (x,y) are the coordinates of your point p. You can check the validity of this equation by inserting the coordinates of the three vertices a, b, c and verify that you obtain the three values fa, fb and fc. Remember that the barycenter coordinates of a are (0,0) hence O_v + x_v * a_x + y_v * a_y = 0 and so on... (a_x and a_y are the x,y coordinates of the point a).
If you let
q = fa + (fb_fa)*O_v + (fc-fa)*O_w
fx = (fb-fa)*x_v + (fc-fa) * x_w
fy = (fb-fa)*y_v + (fc-fa) * y_w
you get
f(x,y) = q + fx*x + fy * y
Notice that q, fx and fy can be computed once from a,b,c,fa,fb,fc and you can reuse them if you only change the coordinates (x,y) of the point p.
Now if f(x,y)>max, you can easily project (x,y) on the line where max is achieved. The coordinates of the projection are:
(x',y') = (x,y) - [(x,y) * (fx,fy) - max + q]/[(fx,fy) * (fx,fy)] (fx,fy)
Now. You would like to have the code. Well here is some pseudo-code:
toBarycoord(Vector2(0,0),a,b,c,O);
toBarycoord(Vector2(1,0),a,b,c,X);
toBarycoord(Vector2(0,1),a,b,c,Y);
X.sub(O); // X = X - O
Y.sub(O); // Y = Y - O
V = Vector2(fb-fa,fc-fa);
q = fa + V.dot(O); // q = fa + V*O
N = Vector2(V.dot(X),V.dot(Y)); // N = (V*X,V*Y)
// p is the point to be considered
f = q + N.dot(p); // f = q + N*p
if (f > max) {
Vector2 tmp;
tmp.set(N);
tmp.multiply((N.dot(p) - max + q)/(N.dot(N))); // scalar multiplication
p.sub(tmp);
}
if (f < min) {
Vector2 tmp;
tmp.set(N);
tmp.multiply((N.dot(p) - min + q)/(N.dot(N))); // scalar multiplication
p.sum(tmp);
}
We think of the problem as follows: The three points are interpreted as a triangle floating in 3D space with the value being the Z-axis and the cartesian coordinates mapped to the X- and Y- axes respectively.
Then the question is to find the gradient of the plane that is defined by the three points. The lines where the plane intersects with the z = min and z = max planes are the lines you want to restrict your points to.
If you have found a point p where v(p) > max or v(p) < min we need to go in the direction of the steepest slope (the gradient) until v(p + k * g) = max or min respectively. g is the direction of the gradient and k is the factor we need to find. The coordinates you are looking for (in the cartesian coordinates) are the corresponding components of p + k * g.
In order to determine g we calculate the orthonormal vector that is perpendicular to the plane that is determined by the three points using the cross product:
// input: px, py, pz,
// output: p2x, p2y
// local variables
var v1x, v1y, v1z, v2x, v2y, v2z, nx, ny, nz, tp, k,
// two vectors pointing from b to a and c respectively
v1x = ax - bx;
v1y = ay - by;
v1z = az - bz;
v2x = cx - bx;
v2y = cy - by;
v2z = cz - bz;
// the cross poduct
nx = v2y * v1z - v2z * v1y;
ny = v2z * v1x - v2x * v1z;
nz = v2x * v1y - v2y * v1x;
// using the right triangle altitude theorem
// we can calculate the vector that is perpendicular to n
// in our triangle we are looking for q where p is nz, and h is sqrt(nx*nx+ny*ny)
// the theorem says p*q = h^2 so p = h^2 / q - we use tp to disambiguate with the point p - we need to negate the value as it points into the opposite Z direction
tp = -(nx*nx + ny*ny) / nz;
// now our vector g = (nx, ny, tp) points into the direction of the steepest slope
// and thus is perpendicular to the bounding lines
// given a point p (px, py, pz) we can now calculate the nearest point p2 (p2x, p2y, p2z) where min <= v(p2z) <= max
if (pz > max){
// find k
k = (max - pz) / tp;
p2x = px + k * nx;
p2y = py + k * ny;
// proof: p2z = v = pz + k * tp = pz + ((max - pz) / tp) * tp = pz + max - pz = max
} else if (pz < min){
// find k
k = (min - pz) / tp;
p2x = px + k * nx;
p2y = py + k * ny;
} else {
// already fits
p2x = px;
p2y = py;
}
Note that obviously if the triangle is vertically oriented (in 2D it's not a triangle anymore actually), nz becomes zero and tp cannot be calculated. That's because there are no more two lines where the value is min or max respectively. For this case you will have to choose another value on the remaining line or point.

How to project night sky into camera?

I want to write something like a virtual telescope that looks into the night sky.
Till now I've a star catalog and I want to project them into a plane to get a mock picture.
I speculate the projection to be a gnomonic projection, which can be found here and here.
In the second link, an alg on calculating the pixel position of stars.
Forward:
Define
scale: number of pixels per degree in the map
alpha, delta: Equatorial coordinates of a given position
alpha0, delta0: Equatorial coordinates of the map center
A = cos(delta) x cos(alpha - alpha0)
F = scale x (180/pi)/[sin(delta0) x sin(delta) + A x cos(delta0)]
then the pixel coordinates in the image are
LINE = -F x [cos(delta0) x sin(delta) - A x sin(delta0)]
SAMPLE = -F x cos(delta) x sin(alpha - alpha0)
Reverse:
Define
X = SAMPLE/(scale x 180/pi)
Y = LINE/(scale x 180/pi)
D = arctan[(X^2 + Y^2)^0.5]
B = arctan(-X/Y)
XX = sin(delta0) x sin(D) x cos(B) + cos(delta0) x cos(D)
YY = sin(D) x sin(B)
then the right ascension and declination are
alpha = alpha0 + arctan(YY/XX)
delta = arcsin[sin(delta0) x cos(D) - cos(delta0) x sin(D) x cos(B)]
NOTE: The arctangent functions for B and alpha must be four-quadrant arctangents.
However I don't know whether the angles should be in deg or rad, and what's the meaning of SAMPLE and LINE.
And I'm neither sure about using gnomonic projection.
Any help or discussion is welcome.
Yeah, just perform an ordinary camera projecion.

Determining "fall-line" vector using 3-axis accelerometer

I am building a tilt-based Arduino device that needs to detect the "fall-line" vector of the device once it is tilted in a particular orientation. By "fall-line" I'll use the following example:
Imagine a frictionless plane with a point mass in the the middle of it and a 3-axis accelerometer mounted in the plane so that the x and y axes of the accelerometer are parallel to the plane. At rest, the plane is flat and the point mass does not move. Once the plane is tilted, the point mass will move in a particular direction at a given acceleration due to gravity. I need to calculate the angle in the x-y plane that the mass will move toward and a magnitude measure corresponding to the acceleration in that direction.
I realise this is probably simple Newtonian mechanics, but I have no idea how to work this out.
The direction of the "fall-line" and the magnitude of the acceleration are both determined by the projection of the gravitational pull vector onto the plane. If the plane has a normal vector n, then the projector operator is P( n ) = 1 - nn, where 1 is the identity operator and nn is the outer (tensor) product of the normal vector with itself. The projection of the gravitational pull vector g is simply g' = P( n ).g = (1 - nn) g = g - (n . g) n, where the dot denotes inner (dot) product. Now you only have to choose a suitable orthonormal reference frame (ex, ey, ez), where ei is a unit vector along direction i. In this reference frame:
n = nx ex + ny ey + nz ez
g = gx ex + gy ey + gz ez
The dot product n . g is then:
n . g = nx * gx + ny * gy + nz * gz
A very suitable choice of a reference frame is one where ez is collinear with n. Then nx = 0 and ny = 0 and nz = ||n|| = 1, because normal vectors are of unit length. In this frame n . g is simply gz. The components of the projection of g are then:
g'x = gx
g'y = gy
g'z = 0
The direction of g' in the XY plane can be determined by the fact that for the dot product in orthonormal reference frames a . b = ||a|| ||b|| cos(a, b), where ||a|| denotes the norm (length) of a and cos(a, b) is the cosine of the angle between a and b. If you measure the angle from the X direction, then:
g' . ex = (gx ex + gy ey) . ex = gx = ||g'|| ||ex|| cos(g', ex) = g' cos(g', ex)
where g' = ||g'|| = sqrt(gx^2 + gy^2). The angle is simply arccos(gx/g'), i.e. arc-cosine of the ratio between the X component of the gravity pull vector and the magnitude of its projection onto the XY plane:
angle = arccos[gx / sqrt(gx^2 + gy^2)]
The magnitude of the acceleration is proportional to the magnitude of g', which is (once again):
g' = ||g'|| = sqrt(gx^2 + gy^2)
Now the nice thing is that all accelerometers measure the components of the gravity field in a reference frame that usually have ex aligned with the height (or the width) of the device, the ex aligned with the width (or the height) of the device and ez is perpendicular to the surface of the device, which matches exactly the reference frame, where ez is collinear with the plane normal. If this is not the case with your Arduino device, simply rotate the accelerometer and align it as needed.

Resources