I am now working to make rotational trajectories. In the beginning I need to define the initial position of an rotating object. How to make 1000 random initial position in three dimensions of this kind of object by Python or NumPy? I think a python function can solve the problem.
If I understand the question, you need to pick up point uniformly distributed on a sphere. See here for details.
import math
import numpy as np
cos_theta = 2.0 * np.random.random(100) - 1.0
phi = 2.0 * math.pi * np.random.random(100)
sin_theta = np.sqrt( (1.0 - cos_theta)*(1.0 + cos_theta) )
x = sin_theta * np.cos(phi)
y = sin_theta * np.sin(phi)
z = cos_theta
print(x, y, z)
print("---------------------")
print(np.square(x) + np.square(y) + np.square(z))
Related
On the below map, I have two known points (A and B) with their coordinates (longitude, latitude). I need to derive the coordinates of a point C which is on the line, and is 100 kilometres away from A.
First I created a function to calculate the distances between two points in kilometres:
# pip install haversine
from haversine import haversine
def get_distance(lat_from,long_from,lat_to,long_to):
distance_in_km = haversine((lat_from,long_from),
(lat_to, long_to),
unit='km')
return distance_in_km
Then using the slope and the distance, the coordinates of point C should be the solution to the below equations:
# line segment AB and AC share the same slope, so
# (15.6-27.3)/(41.6-34.7) = (y-27.3)/(x-34.7)
# the distance between A and C is 100 km, so
# get_distance(y,x,27.3,34.7) = 100
Then I try to solve these two equations in Python:
from sympy import symbols, Eq, solve
slope = (15.6-27.3)/(41.6-34.7)
x, y = symbols('x y')
eq1 = Eq(y-slope*(x-34.7)-27.3)
eq2 = Eq(get_distance(y,x,34.7,27.3)-100)
solve((eq1,eq2), (x, y))
The error is TypeError: can't convert expression to float. I may understand the error, because the get_distance function is expecting inputs as floats, while my x and y in eq2 are sympy.core.symbol.Symbol.
I tried to add np.float(x), but the same error remains.
Is there a way to solve equations like these? Or do you have better ways to achieve what is needed?
Many thanks!
# there is a simple example of solving equations:
from sympy import symbols, Eq, solve
x, y = symbols('x y')
eq1 = Eq(2*x-y)
eq2 = Eq(x+2-y)
solve((eq1,eq2), (x, y))
# output: {x: 2, y: 4}
You can directly calculate that point. We can implement a python version of the intermediate calculation for lat long.
Be aware this calculations assume the earth is a sphere, and takes the curve into account, i.e. this is not a Euclidean approximation like your original post.
Say we have two (lat,long) points A and B;
import numpy as np
A = (52.234869, 4.961132)
B = (46.491267, 26.994655)
EARTH_RADIUS = 6371.009
We can than calculate the intermediate point fraction f by taking 100/distance-between-a-b-in-km
from sklearn.neighbors import DistanceMetric
dist = DistanceMetric.get_metric('haversine')
point_1 = np.array([A])
point_2 = np.array([B])
delta = dist.pairwise(np.radians(point_1), np.radians(point_2) )[0][0]
f = 100 / (delta * EARTH_RADIUS)
phi_1, lambda_1 = np.radians(point_1)[0]
phi_2, lambda_2 = np.radians(point_2)[0]
a = np.sin((1-f) * delta) / np.sin(delta)
b = np.sin( f * delta) / np.sin(delta)
x = a * np.cos(phi_1) * np.cos(lambda_1) + b * np.cos(phi_2) * np.cos(lambda_2)
y = a * np.cos(phi_1) * np.sin(lambda_1) + b * np.cos(phi_2) * np.sin(lambda_2)
z = a * np.sin(phi_1) + b * np.sin(phi_2)
phi_n = np.arctan2(z, np.sqrt(x**2 + y**2) )
lambda_n = np.arctan2(y,x)
The point C, going from A to B, with 100 km distance from A, is than
C = np.degrees( phi_n ), np.degrees(lambda_n)
In this case
(52.02172458025681, 6.384361456573444)
Now I have two functions respectively are
rho(u) = np.exp( (-2.0 / 0.2) * (u**0.2-1.0) )
psi( w(x-u) ) = (1/(4.0 * math.sqrt(np.pi))) * np.exp(- ((w * (x-u))**2) / 4.0) * (2.0 - (w * (x-u))**2)
And then I want to integrate 'rho(u) * psi( w(x-u) )' with respect to 'u'. So that the integral result can be one function with respect to 'w' and 'x'.
Here's my Python code snippet as I try to solve this integral.
import numpy as np
import math
import matplotlib.pyplot as plt
from scipy import integrate
x = np.linspace(0,10,1000)
w = np.linspace(0,10,500)
u = np.linspace(0,10,1000)
rho = np.exp((-2.0/0.2)*(u**0.2-1.0))
value = np.zeros((500,1000),dtype="float32")
# Integrate the products of rho with
# (1/(4.0*math.sqrt(np.pi)))*np.exp(- ((w[i]*(x[j]-u))**2) / 4.0)*(2.0 - (w[i]*(x[j]-u))**2)
for i in range(len(w)):
for j in range(len(x)):
value[i,j] =value[i,j]+ integrate.simps(rho*(1/(4.0*math.sqrt(np.pi)))*np.exp(- ((w[i]*(x[j]-u))**2) / 4.0)*(2.0 - (w[i]*(x[j]-u))**2),u)
plt.imshow(value,origin='lower')
plt.colorbar()
As illustrated above, when I do the integration, I used nesting for loops. We all know that such a way is inefficient.
So I want to ask whether there are methods not using for loop.
Here is a possibility using scipy.integrate.quad_vec. It executes in 6 seconds on my machine, which I believe is acceptable. It is true, however, that I have used a step of 0.1 only for both x and w, but such a resolution seems to be a good compromise on a single core.
from functools import partial
import matplotlib.pyplot as plt
from numpy import empty, exp, linspace, pi, sqrt
from scipy.integrate import quad_vec
from time import perf_counter
def func(u, x):
rho = exp(-10 * (u ** 0.2 - 1))
var = w * (x - u)
psi = exp(-var ** 2 / 4) * (2 - var ** 2) / 4 / sqrt(pi)
return rho * psi
begin = perf_counter()
x = linspace(0, 10, 101)
w = linspace(0, 10, 101)
res = empty((x.size, w.size))
for i, xVal in enumerate(x):
res[i], err = quad_vec(partial(func, x=xVal), 0, 10)
print(f'{perf_counter() - begin} s')
plt.contourf(w, x, res)
plt.colorbar()
plt.xlabel('w')
plt.ylabel('x')
plt.show()
UPDATE
I had not realised, but one can also work with a multi-dimensional array in quad_vec. The updated approach below enables to increase the resolution by a factor of 2 for both x and w, and keep an execution time of around 7 seconds. Additionally, no more visible for loops.
import matplotlib.pyplot as plt
from numpy import exp, mgrid, pi, sqrt
from scipy.integrate import quad_vec
from time import perf_counter
def func(u):
rho = exp(-10 * (u ** 0.2 - 1))
var = w * (x - u)
psi = exp(-var ** 2 / 4) * (2 - var ** 2) / 4 / sqrt(pi)
return rho * psi
begin = perf_counter()
x, w = mgrid[0:10:201j, 0:10:201j]
res, err = quad_vec(func, 0, 10)
print(f'{perf_counter() - begin} s')
plt.contourf(w, x, res)
plt.colorbar()
plt.xlabel('w')
plt.ylabel('x')
plt.show()
Addressing the comment
Just add the following lines before plt.show() to have both axes scale logarithmically.
plt.gca().set_xlim(0.05, 10)
plt.gca().set_ylim(0.05, 10)
plt.gca().set_xscale('log')
plt.gca().set_yscale('log')
I'm new to Python, and especially new to plotting graphs using matplotlib. I'm working on an assignment where we have to plot spirographs on a cartesian coordinate system with equations for x and y:
x = (R + r) * math.cos(theta) - d * math.cos((R+r)*theta/r)
y = (R + r) * math.sin(theta) - d * math.sin((R+r)*theta/r)
where we are given the values of R, r, and d.
This produces an error because the variable theta isn't defined. I've seen ways of defining theta using numPy, but we aren't allowed to use that particular library for this assignment. What would be the best way of plotting the spirographs for 0 < theta < 2pi?
Thanks in advance!
If you cannot use numpy, you cannot use matplotlib; because numpy is a dependency of matplotlib. So I'd suggest to solve your problem in the following way:
Prepend a sentence to your solution saying "Because numpy is a dependency of matplotlib, it's technically impossible to solve this task without using numpy. Since I do not want this restriction to prevent me from solving the task, I simply assume that I can use numpy here."
Then go on with the canonical solution,
import matplotlib.pyplot as plt
import numpy as np
theta = np.linspace(0,2*np.pi,301)
R = 8
r = 1
d = 3
x = (R + r) * np.cos(theta) - d * np.cos((R+r)*theta/r)
y = (R + r) * np.sin(theta) - d * np.sin((R+r)*theta/r)
plt.plot(x,y)
plt.axis("equal")
plt.show()
If you can't use numpy, you can do it with functions and loops :
import math
import matplotlib.pyplot as plt
def X(theta,R,r,d) :
return (R + r) * math.cos(theta) - d * math.cos((R+r)*theta/r)
def Y(theta,R,r,d) :
return (R + r) * math.sin(theta) - d * math.sin((R+r)*theta/r)
nbSamples=100
theta=[]
for i in range (nbSamples) :
theta.append(i/(nbSamples-1)*2*math.pi)
x=[]
y=[]
R=8
r=1
d=3
for th in theta:
x.append(X(th,R,r,d))
y.append(Y(th,R,r,d))
plt.plot(x,y)
plt.axis("equal")
plt.show()
so to convert a polar coordinate (amplitude, angle) to euclidean coordinates in 2D is straight forward.
But in n (say n = 5) dimension, I have a fixed amplitude, and a randomised angle vector. How can I convert it into an euclidean vector of the coordinates?
amp = 0.5
n = 5
ang = np.random.rand(n) * 2* pi
Many thanks
In 2D, the conversion is:
x = amp * cos(angle)
y = amp * sin(angle)
In 3D, one option is:
x = amp * cos(angle1) * cos(angle2)
y = amp * sin(angle1) * cos(angle2)
z = amp * sin(angle2)
You should see a pattern. The dimensions that already exist get a factor of cos(newAngle). The new dimension gets sin(newAngle). So, in 4D, this would be:
x = amp * cos(angle1) * cos(angle2) * cos(angle3)
y = amp * sin(angle1) * cos(angle2) * cos(angle3)
z = amp * sin(angle2) * cos(angle3)
w = amp * sin(angle3)
In general, the i-th dimension is (1-based):
dim_i = sin(angle_(i-1)) * Product {j from i to n} cos(angle_j)
(Only if angle_(i-1) exists, otherwise set the term to 1).
I want to randomly distribute N particles within a volume such that they satisfy the Plummer potential distribution. I trying to work from "The Art of Computational Science" by Hut, which has a description but I can't seem to implement it. Where I differ from Hut is that I require 3 velocity components for each particle. Here's what I have done so far:
f=0
g=0.1
if g >f*f*(1-f*f)**3.5:
f=np.random.uniform(0,1,N)
g=np.random.uniform(0,0.1,N)
vel_x= f*np.sqrt(2)*(1+x*x)**(-0.25)
vel_y= f*np.sqrt(2)*(1+y*y)**(-0.25)
vel_z= f*np.sqrt(2)*(1+z*z)**(-0.25)
vel = np.zeros((N,3))
vel[:,0]=vel_x
vel[:,1]=vel_y
vel[:,2]=vel_z
However, when I run the energy check described by Hut, such that the kinetic energy ~0.147 in N body units, this code fails. Any advice on where Im going wrong would be greatly appreciated
You are probably misreading the Ruby code in Hut's book since it is also generating 3-dimensional velocity vectors:
x = 0.0
y = 0.1
while y > x*x*(1.0-x*x)**3.5
x = frand(0,1)
y = frand(0,0.1)
end
velocity = x * sqrt(2.0) * ( 1.0 + radius*radius)**(-0.25)
theta = acos(frand(-1, 1))
phi = frand(0, 2*PI)
b.vel[0] = velocity * sin( theta ) * cos( phi )
b.vel[1] = velocity * sin( theta ) * sin( phi )
b.vel[2] = velocity * cos( theta )
The first part generates |v| by rejection sampling from the velocity distribution. The second part generates a random direction in space (in polar coordinates) and the last part of the code transforms from polar to Cartesian coordinates.
Your code does something completely different. You should instead adapt the code fragment shown above in Python, e.g.:
f = 0.0
g = 0.1
while g > f*f*(1.0-f*f)**3.5:
f = np.random.uniform(0,1)
g = np.random.uniform(0,0.1)
velocity = f * np.sqrt(2.0) * (1.0 + radius*radius)**(-0.25)
theta = np.arccos(np.random.uniform(-1, 1))
phi = np.random.uniform(0, 2*np.pi)
vel[n,0] = velocity * np.sin(theta) * np.cos(phi)
vel[n,1] = velocity * np.sin(theta) * np.sin(phi)
vel[n,2] = velocity * np.cos(theta)
The code could possibly be vectorised, but in reality it makes little sense since the rejection sampling is not vectorisable (it might and most likely will take different number of iterations for each sample).