Python program to create sphere coordinates not working - python

I am trying to generate points that lies on the surface of a sphere centered on (0,0) in python.
# r - the radius of the sphere
def createSphere(r):
lst = []
for z in range(-r, r+1):
r_ = r - abs(z)
if r_ == 0:
lst.append((0,0,r*np.sign(z)))
else:
for d in range(r_):
lst.append((r_ * cos(d * (360/float(r_))), r_ * sin(d * 360/float(r_))), z) )
return lst
It will return a list [(x1,y1,z1),...].
This is how the result looks like:
The surface isn't smooth, and it looks kinda like a cube with extra sharp corners.
Does anyone know whats wrong?
Thanks

Use the standard spherical to cartesian coordinate transformation:
import math
pi = math.pi
sin = math.sin
cos = math.cos
def createSphere(r, N=10):
lst = []
thetas = [(2*pi*i)/N for i in range(N)]
phis = [(pi*i)/N for i in range(N)]
for theta in thetas:
for phi in phis:
x = r * sin(phi) * cos(theta)
y = r * sin(phi) * sin(theta)
z = r * cos(phi)
lst.append((x, y, z))
return lst
Per the comments below: If you wish to vary the number of points depending on the height (or phi), you could let thetas depend on phi:
def createSphere(r, N=10):
lst = []
for phi in [(pi*i)/(N-1) for i in range(N)]:
M = int(sin(phi)*(N-1))+1
for theta in [(2*pi*i)/M for i in range(M)]:
x = r * sin(phi) * cos(theta)
y = r * sin(phi) * sin(theta)
z = r * cos(phi)
lst.append((x, y, z))
return lst
Above, the key line is
M = int(sin(phi)*(N-1))+1
M will equal 1 when phi is 0 or pi, and it will equal N when phi equals pi/2 (at the "equator"). Note that this is just one possible way to define M. Instead of using sin, you could instead define a piecewise linear function with the same boundary values, for example...

Related

Python mayavi : How to draw spheres at random positions in 3D space

This is a code for generating random sized spheres with mayavi,
I want to make the spheres to be connected with each other by the surface or with a bond line:
Spheres must be at random positions in 3D space
Spheres must be with the same radius
from mayavi import mlab
import numpy as np
[phi,theta] = np.mgrid[0:2*np.pi:12j,0:np.pi:12j]
x = np.cos(phi)*np.sin(theta)
y = np.sin(phi)*np.sin(theta)
z = np.cos(theta)
def plot_sphere(p):
r,a,b,c = p
r=1
return mlab.mesh(r*x+a, r*y+b, r*z )
for k in range(8):
c = np.random.rand(4)
c[0] /= 10.
plot_sphere(c)
mlab.show()
From sphere equation:
So when passing arguments to mlab.mesh we would like to set [x_0, y_0, z_0] for each sphere such as they are at different positions from the axis.
The problem was that the numbers generated by np.random.rand(4) are random, but not distinct.
Let's modify so that the arguments [x_0, y_0, z_0] are random and distinct:
We use sample to get distinct index numbers in a cube
We convert using index_to_3d the index to an (x, y, z) coordinates
The radius, r, can be adjusted to have more or less spacing between the spheres.
Spheres at 3D space
Code:
import random
from itertools import product
from mayavi import mlab
import numpy as np
[phi, theta] = np.mgrid[0:2 * np.pi:12j, 0:np.pi:12j]
x = np.cos(phi) * np.sin(theta)
y = np.sin(phi) * np.sin(theta)
z = np.cos(theta)
def plot_sphere(x_0, y_0, z_0):
r = 0.5
return mlab.mesh(r * x + x_0, r * y + y_0, r * z + z_0)
SPHERES_NUMBER = 200
CUBE_SIZE = 10
def index_to_3d(i, SIZE):
z = i // (SIZE * SIZE)
i -= (z * SIZE * SIZE)
y = i // SIZE
x = i % SIZE
return x, y, z
random_tuples = [index_to_3d(i, CUBE_SIZE) for i in random.sample(range(CUBE_SIZE ** 3), SPHERES_NUMBER)]
for k in range(SPHERES_NUMBER):
x_0, y_0, z_0 = random_tuples[k]
plot_sphere(x_0, y_0, z_0)
mlab.show()
Output:
Spheres cluster
Let's utilize gauss to create coordinates for the cluster points.
Code:
import random
from itertools import product
from mayavi import mlab
import numpy as np
[phi, theta] = np.mgrid[0:2 * np.pi:12j, 0:np.pi:12j]
x = np.cos(phi) * np.sin(theta)
y = np.sin(phi) * np.sin(theta)
z = np.cos(theta)
def plot_sphere(x_0, y_0, z_0):
r = 0.5
return mlab.mesh(r * x + x_0, r * y + y_0, r * z + z_0)
SPHERES_NUMBER = 200
def create_cluster(CLUSTER_SIZE):
means_and_deviations = [(1, 1.5), (1, 1.5), (1, 1.5)]
def generate_point(means_and_deviations):
return tuple(random.gauss(mean, deviation) for mean, deviation in means_and_deviations)
cluster_points = set()
while len(cluster_points) < CLUSTER_SIZE:
cluster_points.add(generate_point(means_and_deviations))
return list(cluster_points)
cluster_points = create_cluster(SPHERES_NUMBER)
for k in range(SPHERES_NUMBER):
x_0, y_0, z_0 = cluster_points[k]
plot_sphere(x_0, y_0, z_0)
mlab.show()
Output:
What about just using the mayavi points3d function? By default the mode parameter is set to sphere and you can set the diameter by using the scale_factor parameter. You can also increase the resolution of the sphere by varying the resolution parameter.
Here is the code:
def draw_sphere(
center_coordinates,
radius,
figure_title,
color,
background,
foreground
):
sphere = mlab.figure(figure_title)
sphere.scene.background = background
sphere.scene.foreground = foreground
mlab.points3d(
center_coordinates[0],
center_coordinates[1],
center_coordinates[2],
color=color,
resolution=256,
scale_factor=2*radius,
figure=sphere
)
Regarding the connected with each other by the surface issue, your explanation is poor. Maybe you mean just tangent spheres, but I would need more details.

How to draw cycloid on curve of other function (other cycloid)?

I would like to draw cycloid that is going on other cycloid but I don't know exactly how to do this. Here is my code.
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import animation
#r = float(input('write r\n'))
#R = float(input('write R\n'))
r = 1
R = 1
x = []
y = []
x2 = []
y2 = []
x3 = []
y3 = []
length=[0]
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'r', animated=True)
f = np.linspace(0, 10*r*math.pi, 1000)
def init():
ax.set_xlim(-r, 12*r*math.pi)
ax.set_ylim(-4*r, 4*r)
return ln,
def update2(frame):
#parametric equations of cycloid
x0 = r * (frame - math.sin(frame))
y0 = r * (1 - math.cos(frame))
x.append(x0)
y.append(y0)
#derivative of cycloid
dx = r * (1 - math.cos(frame))
dy = r * math.sin(frame)
#center of circle
a = dy * dy + dx * dx
b = (-2 * x0 * dy) - (2 * frame * dy * dy) + (2 * y0 * dx) - (2 * frame * dx * dx)
c = (x0 * x0) + (2 * frame * x0 * dy) + (frame * frame * dy * dy) + (y0 * y0) - (2 * frame * y0 * dx) + (frame * frame * dx * dx) -1
t1 = (-b - math.sqrt(b * b - 4 * a * c)) / (2 * a)
#t2 = (-b + math.sqrt(b * b - 4 * a * c)) / (2 * a)
center1x=(x0-dy*(t1-x0))*R
center1y=(y0+dx*(t1-x0))*R
#center2x=(x0-dy*(t2-x0))*R
#center2y=(y0+dx*(t2-x0))*R
#length of cycloid
length.append(math.sqrt(x0*x0 + y0*y0))
dl=sum(length)
param = dl / R
W1x = center1x + R * math.cos(-param)
W1y = center1y + R * math.sin(-param)
#W2x = center2x + R * math.cos(-param)
#W2y = center2y + R * math.sin(-param)
x2.append(W1x)
y2.append(W1y)
#x3.append(W2x)
#y3.append(W2y)
ln.set_data([x, x2], [y, y2])
return ln,
ani = animation.FuncAnimation(fig, update2, frames=f,init_func=init, blit=True, interval = 0.1, repeat = False)
plt.show()
In my function update2 I created parametric equations of first cycloid and then tried to obtain co-ordinates of points of second cycloid that should go on the first one.
My idea is based on that that typical cycloid is moving on straight line, and cycloid that is moving on other curve must moving on tangent of that curve, so center of circle that's creating this cycloid is always placed on normal of curve. From parametric equations of normal I have tried to obtain center of circle that creating cycloid but I think that isn't good way.
My goal is to get something like this:
Here is one way. Calculus gives us the formulas to find the direction angles at any point on the cycloid and the arc lengths along the cycloid. Analytic Geometry tells us how to use that information to find your desired points.
By the way, a plot made by rolling a figure along another figure is called a roulette. My code is fairly simple and could be optimized, but it works now, can be used for other problems, and is broken up to make the math and algorithm easier to understand. To understand my code, use this diagram. The cycloid is the blue curve, the black circles are the rolling circle on the cycloid, point A is an "anchor point" (a point where the rim point touches the cycloid--I wanted to make this code general), and point F is the moving rim point. The two red arcs are the same length, which is what we mean by rolling the circle along the cycloid.
And here is my code. Ask if you need help with the source of the various formulas, but the direction angles and arc lengths use calculus.
"""Numpy-compatible routines for a standard cycloid (one caused by a
circle of radius r above the y-axis rolling along the positive x-axis
starting from the origin).
"""
import numpy as np
def x(t, r):
"""Return the x-coordinate of a point on the cycloid with parameter t."""
return r * (t - np.sin(t))
def y(t, r):
"""Return the y-coordinate of a point on the cycloid with parameter t."""
return r * (1.0 - np.cos(t))
def dir_angle_norm_in(t, r):
"""Return the direction angle of the vector normal to the cycloid at
the point with parameter t that points into the cycloid."""
return -t / 2.0
def dir_angle_norm_out(t, r):
"""Return the direction angle of the vector normal to the cycloid at
the point with parameter t that points out of the cycloid."""
return np.pi - t / 2.0
def arclen(t, r):
"""Return the arc length of the cycloid between the origin and the
point on the cycloid with parameter t."""
return 4.0 * r * (1.0 - np.cos(t / 2.0))
# Roulette problem
def xy_roulette(t, r, T, R):
"""Return the x-y coordinates of a rim point on a circle of radius
R rolling on a cycloid of radius r starting at the anchor point
with parameter T currently at the point with parameter t. (Such a
rolling curve on another curve is called a roulette.)
"""
# Find the coordinates of the contact point P between circle and cycloid
px, py = x(t, r), y(t, r)
# Find the direction angle of PC from the contact point to circle's center
a1 = dir_angle_norm_out(t, r)
# Find the coordinates of the center C of the circle
cx, cy = px + R * np.cos(a1), py + R * np.sin(a1)
# Find cycloid's arc distance AP between anchor and current contact points
d = arclen(t, r) - arclen(T, r) # equals arc PF
# Find the angle φ the circle turned while rolling from the anchor pt
phi = d / R
# Find the direction angle of CF from circle's center to rim point
a2 = dir_angle_norm_in(t, r) - phi # subtract: circle rolls clockwise
# Find the coordinates of the final point F
fx, fy = cx + R * np.cos(a2), cy + R * np.sin(a2)
# Return those coordinates
return fx, fy
import matplotlib.pyplot as plt
r = 1
R = 0.75
T = np.pi / 3
t_array = np.linspace(0, 2*np.pi, 201)
cycloid_x = x(t_array, r)
cycloid_y = y(t_array, r)
roulette_x, roulette_y = xy_roulette(t_array, r, T, R)
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
ax.plot(cycloid_x, cycloid_y)
ax.plot(roulette_x, roulette_y)
plt.show()
And here is the resulting graphic. You can pretty this up as you choose. Note that this only has the circle rolling along one arch of the cycloid. If you clarify what should happen at the cusps, this could be extended.
Or, if you want a smaller circle and a curve that ends at the cusps (here r = 1, T = 0 n = 6 (the number of little arches), and R = 4 * r / np.pi / n),
You can generate coordinates of the center of rolling circle using parallel curve definition. Parametric equations for this center are rather simple (if I did not make mistakes):
for big cycloid:
X = R(t - sin(t))
Y = R(1 - cos(t))
X' = R(1 - cos(t))
Y' = R*sin(t)
parallel curve (center of small circle):
Sqrt(X'^2+Y'^2)=R*Sqrt(1-2*cos(t)+cos^2(t)+sin^2(t)) =
R*Sqrt(2-2*cos(t))=
R*Sqrt(4*sin^2(t/2))=
2*R*sin(t/2)
x(t) = X(t) + r*R*sin(t)/(2R*sin(t/2)) =
R(t - sin(t)) + r*2*sin(t/2)*cos(t/2) / (2*sin(t/2)) =
R(t - sin(t)) + r*cos(t/2)
y(t) = Y(t) - r*R*(1-cos(t))/(2*R*sin(t/2)) =
R(1 - cos(t)) - r*(2*sin^2(t/2)/(2*sin(t/2)) =
R(1 - cos(t)) - r*sin(t/2)
But trajectory of point on the circumference is superposition of center position and rotation around it with angular velocity that depends on length of main cycloid plus rotation of main tangent.
Added from dicussion in comments:
cycloid arc length
L(t) = 4R*(1-cos(t/2))
to use it for small circle rotation, divide by r
tangent rotation derivation
fi(t) = atan(Y'/X') = atan(sin(t)/(1-cos(t)) =
atan(2*sin(t/2)*cos(t/2)/(2(sin^2(t/2))) =
atan(ctg(t/2)) = Pi/2 - t/2
so tangent direction change is proportional to big cycloid parameter
and final result is (perhaps some signs are not correct)
theta(t) = L(t)/r + t/2 + Phase
ox(t) = x(t) + r * cos(theta(t))
oy(t) = y(t) + r * sin(theta(t))
Thanks everyone. Somehow I have managed to accomplish that. Solution is maybe ugly but sufficient for me.
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import animation
r = float(input('write r\n'))
R = float(input('write R\n'))
#r=1
#R=0.1
x = []
y = []
x2 = []
y2 = []
x_1=0
x_2=0
lengthX=[0]
lengthY=[0]
lengthabs=[0]
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'r', animated=True)
f = np.linspace(0, 2*math.pi, 1000)
def init():
ax.set_xlim(-r, 4*r*math.pi)
ax.set_ylim(0, 4*r)
return ln,
def update2(frame):
#cycloid's equations
x0 = r * (frame - math.sin(frame))
y0 = r * (1 - math.cos(frame))
x.append(r * (frame - math.sin(frame)))
y.append(r * (1 - math.cos(frame)))
#arc's length
lengthabs.append(math.sqrt((x0-lengthX[-1])*(x0-lengthX[-1])+(y0-lengthY[-1])*(y0-lengthY[-1])))
lengthX.append(x0)
lengthY.append(y0)
dl=sum(lengthabs)
param = dl / R
#center of circle
center1x = r * (frame - math.sin(frame)) + R * math.cos((frame+2*math.pi) / 2)
center1y = r * (1 - math.cos(frame)) - R * math.sin((frame+2*math.pi) / 2)
if(frame<2*math.pi):
W1x = center1x + R * math.cos(-param)
W1y = center1y + R * math.sin(-param)
else:
W1x = center1x + R * math.cos(param)
W1y = center1y + R * math.sin(param)
x2.append(W1x)
y2.append(W1y)
ln.set_data([x,x2], [y,y2])
return ln,
ani = animation.FuncAnimation(fig, update2, frames=f,init_func=init, blit=True, interval = 0.1, repeat = False)
plt.show()

Generate random locations within a triangular domain

I want to generate x and y having a uniform distribution and limited by [xmin,xmax] and [ymin,ymax]
The points (x,y) should be inside a triangle.
How can I solve such a problem?
Here's some code that generates points uniformly on an arbitrary triangle in the plane.
import random
def point_on_triangle(pt1, pt2, pt3):
"""
Random point on the triangle with vertices pt1, pt2 and pt3.
"""
x, y = sorted([random.random(), random.random()])
s, t, u = x, y - x, 1 - y
return (s * pt1[0] + t * pt2[0] + u * pt3[0],
s * pt1[1] + t * pt2[1] + u * pt3[1])
The idea is to compute a weighted average of the three vertices, with the weights given by a random break of the unit interval [0, 1] into three pieces (uniformly over all such breaks). Here x and y represent the places at which we break the unit interval, and s, t and u are the length of the pieces following that break. We then use s, t and u as the barycentric coordinates of the point in the triangle.
Here's a variant of the above that avoids the need to sort, instead making use of an absolute value call:
def point_on_triangle2(pt1, pt2, pt3):
"""
Random point on the triangle with vertices pt1, pt2 and pt3.
"""
x, y = random.random(), random.random()
q = abs(x - y)
s, t, u = q, 0.5 * (x + y - q), 1 - 0.5 * (q + x + y)
return (
s * pt1[0] + t * pt2[0] + u * pt3[0],
s * pt1[1] + t * pt2[1] + u * pt3[1],
)
Here's an example usage that generates 10000 points in a triangle:
pt1 = (1, 1)
pt2 = (2, 4)
pt3 = (5, 2)
points = [point_on_triangle(pt1, pt2, pt3) for _ in range(10000)]
And a plot obtained from the above, demonstrating the uniformity. The plot was generated by this code:
import matplotlib.pyplot as plt
x, y = zip(*points)
plt.scatter(x, y, s=0.1)
plt.show()
Here's the image:
And since you tagged the question with the "numpy" tag, here's a NumPy version that generates multiple samples at once. Note that it uses the matrix multiplication operator #, introduced in Python 3.5 and supported in NumPy >= 1.10. You'll need to replace that with a call to np.dot on older Python or NumPy versions.
import numpy as np
def points_on_triangle(v, n):
"""
Give n random points uniformly on a triangle.
The vertices of the triangle are given by the shape
(2, 3) array *v*: one vertex per row.
"""
x = np.sort(np.random.rand(2, n), axis=0)
return np.column_stack([x[0], x[1]-x[0], 1.0-x[1]]) # v
# Example usage
v = np.array([(1, 1), (2, 4), (5, 2)])
points = points_on_triangle(v, 10000)
Ok, time to add another version, I guess. There is known algorithm to sample uniformly in triangle, see paper, chapter 4.2 for details.
Python code:
import math
import random
import matplotlib.pyplot as plt
def trisample(A, B, C):
"""
Given three vertices A, B, C,
sample point uniformly in the triangle
"""
r1 = random.random()
r2 = random.random()
s1 = math.sqrt(r1)
x = A[0] * (1.0 - s1) + B[0] * (1.0 - r2) * s1 + C[0] * r2 * s1
y = A[1] * (1.0 - s1) + B[1] * (1.0 - r2) * s1 + C[1] * r2 * s1
return (x, y)
random.seed(312345)
A = (1, 1)
B = (2, 4)
C = (5, 2)
points = [trisample(A, B, C) for _ in range(10000)]
xx, yy = zip(*points)
plt.scatter(xx, yy, s=0.2)
plt.show()
And result looks like
Uniform on the triangle?
import numpy as np
N = 10 # number of points to create in one go
rvs = np.random.random((N, 2)) # uniform on the unit square
# Now use the fact that the unit square is tiled by the two triangles
# 0 <= y <= x <= 1 and 0 <= x < y <= 1
# which are mapped onto each other (except for the diagonal which has
# probability 0) by swapping x and y.
# We use this map to send all points of the square to the same of the
# two triangles. Because the map preserves areas this will yield
# uniformly distributed points.
rvs = np.where(rvs[:, 0, None]>rvs[:, 1, None], rvs, rvs[:, ::-1])
Finally, transform the coordinates
xmin, ymin, xmax, ymax = -0.1, 1.1, 2.0, 3.3
rvs = np.array((ymin, xmin)) + rvs*(ymax-ymin, xmax-xmin)
Uniform marginals? The simplest solution would be to uniformly concentrate the mass on the line (ymin, xmin) - (ymax, xmax)
rvs = np.random.random((N,))
rvs = np.c_[ymin + (ymax-ymin)*rvs, xmin + (xmax-xmin)*rvs]
but that is not very interesting, is it?

How to generate random points in a circular distribution

I am wondering how i could generate random numbers that appear in a circular distribution.
I am able to generate random points in a rectangular distribution such that the points are generated within the square of (0 <= x < 1000, 0 <= y < 1000):
How would i go upon to generate the points within a circle such that:
(x−500)^2 + (y−500)^2 < 250000 ?
import random
import math
# radius of the circle
circle_r = 10
# center of the circle (x, y)
circle_x = 5
circle_y = 7
# random angle
alpha = 2 * math.pi * random.random()
# random radius
r = circle_r * math.sqrt(random.random())
# calculating coordinates
x = r * math.cos(alpha) + circle_x
y = r * math.sin(alpha) + circle_y
print("Random point", (x, y))
In your example circle_x is 500 as circle_y is. circle_r is 500.
Another version of calculating radius to get uniformly distributed points, based on this answer
u = random.random() + random.random()
r = circle_r * (2 - u if u > 1 else u)
FIRST ANSWER:
An easy solution would be to do a check to see if the result satisfies your equation before proceeding.
Generate x, y (there are ways to randomize into a select range)
Check if ((x−500)^2 + (y−500)^2 < 250000) is true
if not, regenerate.
The only downside would be inefficiency.
SECOND ANSWER:
OR, you could do something similar to riemann sums like for approximating integrals. Approximate your circle by dividing it up into many rectangles. (the more rectangles, the more accurate), and use your rectangle algorithm for each rectangle within your circle.
What you need is to sample from (polar form):
r, theta = [math.sqrt(random.randint(0,500))*math.sqrt(500), 2*math.pi*random.random()]
You can then transform r and theta back to cartesian coordinates x and y via
x = 500 + r * math.cos(theta)
y = 500 + r * math.sin(theta)
Related (although not Python), but gives the idea.
You can use below the code and if want to learn more
https://programming.guide/random-point-within-circle.html
import random
import math
circle_x = 500
circle_y = 500
a = random.randint(0,500) * 2 * math.pi
r = 1 * math.sqrt(random.randint(0,500))
x = r * math.cos(a) + circle_x
y = r * math.sin(a) + circle_y
here's an example hope could help someone :).
randProba = lambda a: a/sum(a)
npoints = 5000 # points to chose from
r = 1 # radius of the circle
plt.figure(figsize=(5,5))
t = np.linspace(0, 2*np.pi, npoints, endpoint=False)
x = r * np.cos(t)
y = r * np.sin(t)
plt.scatter(x, y, c='0.8')
n = 2 # number of points to chose
t = np.linspace(0, 2*np.pi, npoints, endpoint=False)[np.random.choice(range(npoints), n, replace=False, p=randProba(np.random.random(npoints)))]
x = r * np.cos(t)
y = r * np.sin(t)
plt.scatter(x, y)
You can use rejection sampling, generate a random point within the (2r)×(2r) square that covers the circle, repeat until get one point within the circle.
I would use polar coordinates:
r_squared, theta = [random.randint(0,250000), 2*math.pi*random.random()]
Then r is always less than or equal to the radius, and theta always between 0 and 2*pi radians.
Since r is not at the origin, you will always convert it to a vector centered at 500, 500, if I understand correctly
x = 500 + math.sqrt(r_squared)*math.cos(theta)
y = 500 + math.sqrt(r_squared)*math.sin(theta)
Choose r_squared randomly because of this

Calculate the Fourier series with the trigonometry approach

I try to implement the Fourier series function according to the following formulas:
...where...
...and...
Here is my approach to the problem:
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin(np.pi * 1000 * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = 0
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos(n * np.pi * x)) + (b(i, L) * np.sin(n * np.pi * x)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 10])
py.ylim([-2, 2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
...and here is what I get after plotting the result:
I've read the How to calculate a Fourier series in Numpy? and I've implemented this approach already. It works great, but it use the expotential method, where I want to focus on trigonometry functions and the rectangular method in case of calculating the integraions for a_{n} and b_{n} coefficients.
Thank you in advance.
UPDATE (SOLVED)
Finally, here is a working example of the code. However, I'll spend more time on it, so if there is anything that can be improved, it will be done.
from __future__ import division
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin((np.pi) * x) + np.sin((2 * np.pi) * x) + np.sin((5 * np.pi) * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = np.zeros(np.size(x))
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos((i * np.pi * x) / L)) + (b(i, L) * np.sin((i * np.pi * x) / L)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), '.', color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 5])
py.ylim([-2.2, 2.2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
Consider developing your code in a different way, block by block. You should be surprised if a code like this would work at the first try. Debugging is one option, as #tom10 said. The other option is rapid prototyping the code step by step in the interpreter, even better with ipython.
Above, you are expecting that b_1000 is non-zero, since the input f(x) is a sinusoid with a 1000 in it. You're also expecting that all other coefficients are zero right?
Then you should focus on the function b(n, L, accuracy = 1000) only. Looking at it, 3 things are going wrong. Here are some hints.
the multiplication of dx is within the loop. Sure about that?
in the loop, i is supposed to be an integer right? Is it really an integer? by prototyping or debugging you would discover this
be careful whenever you write (1/L) or a similar expression. If you're using python2.7, you're doing likely wrong. If not, at least use a from __future__ import division at the top of your source. Read this PEP if you don't know what I am talking about.
If you address these 3 points, b() will work. Then think of a in a similar fashion.

Categories

Resources