Related
I am new to this, and tried to implement this algorithm by using uniform B-Spline. And I don't know where I did it wrong, the result just doesn't come out the way it supposed to be.
I don't know if the basis is wrong or the procedure of the PIA is wrong. Is there someone that could help me out? Thank you so much!!
I use Python to implement all this.
In my understanding, the PIA is taking the given point set, P, as control points at first(iteration 0), and use these control points to calculate a b-spline, Q. Then find the difference, d, between the P and Q. Let Q+d in each iteration, until d is small enough as the threshold you set at the beginning.
I use deboor-Cox algorithm for generating the basis matrix.
def b_spline_basis(i, p, u, nodeVector):
# p means the degree of the spline
if p == 0:
if (nodeVector[i] <= u) & (u <= nodeVector[i + 1]): # if u is between two knots, the basis would be 1
result = 1.0
else:
result = 0.0
else:
# calculate non-zero intervals
length1 = nodeVector[i + p] - nodeVector[i]
length2 = nodeVector[i + p + 1] - nodeVector[i + 1]
# calculate coefficients for basis functions
if length1 == 0: # specifically 0/0
alpha = 0
else:
alpha = (u - nodeVector[i]) / length1
if length2 == 0:
beta = 0
else:
beta = (nodeVector[i + p + 1] - u) / length2
# calculate basis functions recursively
result = alpha * b_spline_basis(i, p - 1, u, nodeVector) + beta * b_spline_basis(i + 1, p - 1, u, nodeVector)
return result
And I tried the lemniscate to test whether my implementation of PIA is okay or not.
import numpy as np
import math
from bspline import b_spline
import matplotlib.pyplot as plt
import matplotlib
from bspline_basis import b_spline_basis
matplotlib.use('TkAgg')
# lemniscate with 200 points
alpha = 1
theta = np.linspace(0, 2 * np.pi, num=200)
x_real = alpha * np.sqrt(2) * np.cos(theta) / (np.sin(theta) ** 2 + 1)
y_real = alpha * np.sqrt(2) * np.cos(theta) * np.sin(theta) / (np.sin(theta) ** 2 + 1)
# draw the real points on lemniscate
plt.scatter(x_real, y_real)
# degree of bspline is 3, number of control points is 8
degree = 3
n = 8
points = []
delta = np.linspace(0, 2 * np.pi, num=8)
# x and y are the x-axis and y-axis for the control points
x = alpha * np.sqrt(2) * np.cos(delta) / (np.sin(delta) ** 2 + 1)
y = alpha * np.sqrt(2) * np.cos(delta) * np.sin(delta) / (np.sin(delta) ** 2 + 1)
plt.scatter(x, y, color='maroon')
# calculate bspline basis matrix
def bspline_basis(n, degree, knotVector):
basis = np.zeros([n, n])
for i in range(n):
j = 0
for u in delta:
basis[i][j] = b_spline_basis(i, degree, u, knotVector)
# print('knot=', knot)
# print('basis_i=', basis, 'j=',j)
j = j + 1
return basis
a = min(delta)
b = max(delta)
knotVector = [a, a, a, a, *delta[2:-2], b, b, b, b]
# basis matrix is stored in bs
bs = bspline_basis(n, degree, knotVector)
# I think if the basis is right, this plot would be a b-spline curve, but it doesn't turn out that way. I'm also confused by this.
plt.plot(np.dot(bs, np.transpose(x)), np.dot(bs, np.transpose(y)), color='red')
# the difference between real control points with calculated value
dx = x - np.transpose(np.dot(bs, np.transpose(x)))
dy = y - np.transpose(np.dot(bs, np.transpose(y)))
# norm is going to store the norm of (dx, dy)
norm = np.zeros(n)
for i in range(len(dx)):
norm[i] = math.sqrt(dx[i] ** 2 + dy[i] ** 2)
# make the biggest norm to be the error
err = max(norm)
iteration = 0
print('iteration #', iteration, ', err = ', err)
# set the threshold for the algorithm to stop
tol = 0.2
# in while loop, calculate the difference in each loop, until error is smaller than the threshold
while err > tol:
iteration = iteration + 1
x = x + dx
y = y + dy
dx = x - np.transpose(np.dot(bs, np.transpose(x)))
dy = y - np.transpose(np.dot(bs, np.transpose(y)))
for i in range(len(dx)):
norm[i] = math.sqrt(dx[i] ** 2 + dy[i] ** 2)
err = max(norm)
print('iteration #', iteration, ', err = ', err)
x_inter = np.transpose(np.dot(bs, np.transpose(x)))
y_inter = np.transpose(np.dot(bs, np.transpose(y)))
plt.show()
But the result is not even close. The err printed in each iteration gets bigger and bigger.
iteration # 0 , err = 0.8978393078534154
iteration # 1 , err = 0.5572305648715149
iteration # 2 , err = 0.8814649114823587
iteration # 3 , err = 1.406648477874589
iteration # 4 , err = 2.2515402019886657
iteration # 5 , err = 3.610001808299592
iteration # 6 , err = 5.794725750733798
iteration # 7 , err = 9.309544995196921
iteration # 8 , err = 14.966156756400013
iteration # 9 , err = 24.072299683891867
iteration # 10 , err = 38.73507669530552
iteration # 11 , err = 62.34988787737978
iteration # 12 , err = 100.3885976037046
iteration # 13 , err = 161.67015869470632
iteration # 14 , err = 260.40916333350236
iteration # 15 , err = 419.5188341631952
iteration # 16 , err = 675.9369969104991
iteration # 17 , err = 1089.2146572938898
iteration # 18 , err = 1755.3667774904786
iteration # 19 , err = 2829.2109590140344
iteration # 20 , err = 4560.398039137244
iteration # 21 , err = 7351.530766709586
iteration # 22 , err = 11851.91790312345
iteration # 23 , err = 19108.824114848438
iteration # 24 , err = 30811.492573031916
iteration # 25 , err = 49684.87189301904
iteration # 26 , err = 80124.93280280002
iteration # 27 , err = 129223.88403951934
iteration # 28 , err = 208424.68577890267
iteration # 29 , err = 336191.3189164541
iteration # 30 , err = 542318.7082430203
iteration # 31 , err = 874889.5879288138
iteration # 32 , err = 1411504.6936387809
iteration # 33 , err = 2277412.443263706
iteration # 34 , err = 3674778.915040246
...
The printed lines are too long, I won't show them all. But you get the point.
Beside, the plot is also wierd. And I just don't know where when wrong, and I upset my for days.
Is there someone can help with this? Thank you so so much!! I'm really confused right now, hoping there is someone can help me out. TAT
There are a few things we need to take care of.
First, I will put b_spline_basis into a separate file. It was almost correct but there are two changes. The intervals where the degree zero basis functions evaluate to 1 had to be adapted so that the basis functions sum up to one on the entire interval [a, b] (your version evaluated to more in the knots). This problem happens quite often, cf. e.g. here. Also, the 0/0 case needed 1 instead of 0 for alpha and beta:
def b_spline_basis(i, p, u, knotVector):
# p means the degree of the spline
if p == 0:
# The support is closed from left but open from the right ...
if (i != len(knotVector) - 2):
if ((knotVector[i] <= u) & (u < knotVector[i + 1])):
result = 1.0
else:
result = 0.0
# ... unless it is the last one, which is closed from both sides.
else:
if ((knotVector[i] <= u) & (u <= knotVector[i + 1])):
result = 1.0
else:
result = 0.0
else:
# calculate non-zero intervals
length1 = knotVector[i + p] - knotVector[i]
length2 = knotVector[i + p + 1] - knotVector[i + 1]
# calculate coefficients for basis functions
if length1 == 0: # specifically 0/0
alpha = 1 # You had 0 here.
else:
alpha = (u - knotVector[i]) / length1
if length2 == 0:
beta = 1 # You had 0 here as well.
else:
beta = (knotVector[i + p + 1] - u) / length2
# calculate basis functions recursively
result = alpha * b_spline_basis(i, p - 1, u, knotVector) + beta * b_spline_basis(i + 1, p - 1, u, knotVector)
return result
Second, I put also bspline_basis [sic] into a separate file. It is almost identical to your version but the matrix is not necessarily square in general. I would also strongly advise to rename the function; the resulting matrix is a transpose of what is usually called collocation matrix.
import numpy as np
from b_spline_basis import b_spline_basis
# calculate bspline basis matrix
def bspline_basis(n, degree, knotVector, delta):
basis = np.zeros([n, delta.size])
for i in range(n):
j = 0
for u in delta:
basis[i][j] = b_spline_basis(i, degree, u, knotVector)
# print('knot=', knot)
# print('basis_i=', basis, 'j=',j)
j = j + 1
return basis
Finally, I throw in a function for plotting a B-spline (as a curve) given its control points etc.
import numpy as np
from b_spline_basis import b_spline_basis
def plot_bspline(plt, num_samples, degree, knotVector, x_cps, y_cps, color):
beg = knotVector[0]
end = knotVector[-1]
num_cps = len(x_cps)
x_curve = np.zeros(num_samples)
y_curve = np.zeros(num_samples)
for i in range(num_samples):
for j in range(num_cps):
t_loc = i / (num_samples-1)
t = beg * (1 - t_loc) + end * t_loc
x_curve[i] += x_cps[j] * b_spline_basis(j, degree, t, knotVector)
y_curve[i] += y_cps[j] * b_spline_basis(j, degree, t, knotVector)
plt.plot(x_curve, y_curve, color=color)
Now we get back to your code, the few corrections are commented. In general, there seemed to be three sources of confusion:
The results of bspline_basis had to be transposed, because they are a transposed collocation matrix.
Evaluating using bspline_basis does not give you a B-spline as a curve but only its values in delta.
It is important to distinguish between x_target, y_target (values of the lemniscate that you want to approximate) and x_cps, y_cps (B-spline control points in the current iteration). You called both of them x, y.
import numpy as np
import math
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
from b_spline_basis import b_spline_basis
from bspline_basis import bspline_basis
from plot_bspline import plot_bspline
# lemniscate with 200 points
alpha = 1
theta = np.linspace(0, 2 * np.pi, num=200)
x_real = alpha * np.sqrt(2) * np.cos(theta) / (np.sin(theta) ** 2 + 1)
y_real = alpha * np.sqrt(2) * np.cos(theta) * np.sin(theta) / (np.sin(theta) ** 2 + 1)
# draw the real points on lemniscate
plt.scatter(x_real, y_real)
# degree of bspline is 3, number of control points is 8
degree = 3
n = 8
points = []
delta = np.linspace(0, 2 * np.pi, num=8)
# x_target and y_target are the values we want to approximate.
# They will be used as starting values for the control points as well.
x_target = alpha * np.sqrt(2) * np.cos(delta) / (np.sin(delta) ** 2 + 1)
y_target = alpha * np.sqrt(2) * np.cos(delta) * np.sin(delta) / (np.sin(delta) ** 2 + 1)
plt.scatter(x_target, y_target, color='maroon')
a = min(delta)
b = max(delta)
knotVector = [a, a, a, a, *delta[2:-2], b, b, b, b]
# basis matrix is stored in bs
bs = bspline_basis(n, degree, knotVector, delta)
# I think if the basis is right, this plot would be a b-spline curve, but it doesn't turn out that way. I'm also confused by this.
# The transpositions were wrong.
# Also, using bs does not give you a B-spline as a curve but only its values evaluated at delta, i.e., at 8 points.
plt.plot(np.dot(np.transpose(bs), x_target), np.dot(np.transpose(bs), y_target), color='red')
# If you also plot the B-spline as curve that uses x_target and y_target as control points, you will see that the red curve connects 8 of its values.
plot_bspline(plt, 100, 3, knotVector, x_target, y_target, 'green')
# Now to PIA.
# The control points in the first iteration will be the initial values.
x_cps = x_target
y_cps = y_target
# Then we have a difference between the target values and the corresponding values of our B-spline.
dx = x_target - np.transpose(np.dot(np.transpose(bs), x_cps))
dy = y_target - np.transpose(np.dot(np.transpose(bs), y_cps))
# norm is going to store the norm of (dx, dy)
norm = np.zeros(n)
for i in range(len(dx)):
norm[i] = math.sqrt(dx[i] ** 2 + dy[i] ** 2)
# make the biggest norm to be the error
err = max(norm)
iteration = 0
print('iteration #', iteration, ', err = ', err)
# set the threshold for the algorithm to stop
tol = 1e-5
# in while loop, calculate the difference in each loop, until error is smaller than the threshold
while err > tol and iteration < 100:
iteration = iteration + 1
# We change the control points ...
x_cps = x_cps + dx
y_cps = y_cps + dy
# ... and compute the difference from the target (which is constant)!
dx = x_target - np.transpose(np.dot(np.transpose(bs), x_cps))
dy = y_target - np.transpose(np.dot(np.transpose(bs), y_cps))
for i in range(len(dx)):
norm[i] = math.sqrt(dx[i] ** 2 + dy[i] ** 2)
err = max(norm)
print('iteration #', iteration, ', err = ', err)
x_inter = np.transpose(np.dot(np.transpose(bs), x_cps))
y_inter = np.transpose(np.dot(np.transpose(bs), y_cps))
# If I plot the way you did, I will again not get a B-spline as a curve but the values of the B-spline evaluated at delta. Notice that it passes the maroon points.
plt.plot(x_inter, y_inter, color='yellow')
# Let's now plot the entire B-spline as a curve. Notice that it passes through the maroon points.
plot_bspline(plt, 100, 3, knotVector, x_cps, y_cps, 'magenta')
plt.show()
If we now have a look at the plot, there is quite a lot happening:
The blue points are the samples of the lemniscate, i.e., the input.
The maroon points are the eight points on the lemniscate that we will be approximating (there seem to be only seven, since the first and last ones coincide).
The green curve is the initial guess, i.e., a B-spline that uses the maroon points as its control points.
The red polygon uses bspline_basis to connect the values of the green B-spline in the parameter values in delta. This is a corrected version of your red curve.
The magenta curve is the final guess, i.e., a B-spline that approximates the maroon points up to tol.
The yellow curve uses bspline_basis to connect the values of the magenta B-spline along delta. This is a corrected version of your x_inter, y_inter.
I wish you good luck with further B-spline experiments. If you are also into neural networks, you might enjoy a recent paper by my friends that investigated the connection between LSPIA and gradient descent:
Dany Rios and Bert Jüttler: LSPIA,(stochastic) gradient descent, and parameter correction. Journal of Computational and Applied Mathematics 406 (2022): 113921 (preprint)
I wish to set-up an initially circular (e=0) system of planetary rings which I can later perturb over time and see how the eccentricity changes. However, my calculation of the eccentricity vector returns -1 as the value of my initial ring, rather than zero.
The eccentricity vector equation takes this form
import numpy as np
import matplotlib.pyplot as plt
G = 6.674e-20 # km^3 kg^-1 s^-2
day = 60.0 * 60.0 * 24.0
dt = day / 10.0
Mass = 5.683e26
N = 30000
delta = np.random.random(1) * 2.0 * np.pi / N
angles = np.linspace(0.0, 2.0 * np.pi, N) + delta
radius = np.random.uniform(low = 1e6, high = 2e6, size = (N)) # ring radius
xrings, yrings = radius * np.cos(angles), radius * np.sin(angles) # positions
vxrings, vyrings = np.sqrt((G * Mass) / radius) * -np.sin(angles), np.sqrt((G * Mass) / radius) * np.cos(angles) # velocities
dist = np.hypot(xrings, yrings) # distance between particles
# update positions
xrings += (vxrings * dt)
yrings += (vyrings * dt)
#update velocities
vxrings -= (G * Mass * xrings / (dist ** 3.0 + 1.0e6) * dt)
vyrings -= (G * Mass * yrings / (dist ** 3.0 + 1.0e6) * dt)
v = np.hypot(vxrings, vyrings)
mu = G*Mass
e = (((abs(v)**2) / mu) - (1/abs(dist)))*radius - (((radius*v) / mu)*v)
plt.plot(e, radius)
plt.show()
I have tried interchanging dist and radius in various ways within the equation as I know the radius needs to be with respect to the central mass, but to no avail.
I think the problem is arising due to the fact that it is a vector equation and when you have implemented it, you've used the magnitudes of radius and velocity when you should have used their vectors.
Implementing either equation from the wiki (with the vectors for r and v) gives the expected result of e being 0 when dt is 0:
import numpy as np
import matplotlib.pyplot as plt
G = 6.674e-20 # km^3 kg^-1 s^-2
day = 60.0 * 60.0 * 24.0
dt = day / 10.0
Mass = 5.683e26
mu = G*Mass
dt = 0
N = 30000
delta = np.random.random(1) * 2.0 * np.pi / N
angles = np.linspace(0.0, 2.0 * np.pi, N) + delta
radius = np.random.uniform(low = 1e6, high = 2e6, size = (N)) # ring radius
xrings, yrings = radius * np.cos(angles), radius * np.sin(angles) # positions
vxrings, vyrings = np.sqrt((G * Mass) / radius) * -np.sin(angles), np.sqrt((G * Mass) / radius) * np.cos(angles) # velocities
dist = np.hypot(xrings, yrings) # distance between particles
# update positions
xrings += (vxrings * dt)
yrings += (vyrings * dt)
# #update velocities
vxrings -= (G * Mass * xrings / (dist ** 3.0 + 1.0e6) * dt)
vyrings -= (G * Mass * yrings / (dist ** 3.0 + 1.0e6) * dt)
# Convert to array of vectors assuming there is no motion out of the plane
r_vector = np.array([[i, j, 0 ] for i, j in zip(xrings, yrings)])
v_vector = np.array([[i, j, 0] for i, j in zip(vxrings, vyrings)])
# Implement the equation as given in the wiki page
# Cross product method
h = [np.cross(i, j) for i, j in zip(r_vector, v_vector)] # r cross v
v_h = [np.cross(i, j)/mu for i, j in zip(v_vector, h)] # v cross h over mu
r_normalised = [i/np.linalg.norm(i) for i in r_vector]
e_vector_cross = [i-j for i,j in zip(v_h, r_normalised)]
absolute_e_cross = [np.linalg.norm(i) for i in e_vector_cross]
plt.figure()
plt.title('Cross product method')
plt.xlabel('Eccentricity')
plt.ylabel('Radius')
plt.plot(absolute_e_cross, radius)
# Dot product method
first_factor = [np.linalg.norm(i)**2/mu -1/np.linalg.norm(j) for i, j in zip(v_vector, r_vector)]
first = [i*j for i, j in zip(first_factor, r_vector)]
second_factor = [np.dot(i, j)/mu for i, j in zip(r_vector, v_vector)]
second = [i*j for i, j in zip(second_factor, v_vector)]
e_vector_dot = [i-j for i, j in zip(first, second)]
absolute_e_dot = [np.linalg.norm(i) for i in e_vector_dot]
plt.figure()
plt.title('Dot product method')
plt.xlabel('Eccentricity')
plt.ylabel('Radius')
plt.plot(absolute_e_dot, radius)
Output:
In our physics class we have to model a damping torsional pendulum.
Ilustration of torsional pendulum:
We came up with this equation of motion:
Where θ is the angle, A is torsion parameter, B is Newton's parameter, C is Stokes' parameter, and D is friction parameter. We also use the sign function sgn that determines the direction of the acting force upon the pendulum, depending on the current angle from the reference point.
The problem is, that I'm unable to solve it using Runge-Kutta method in Python.
I got a working solution in MATLAB by using Euler's method, which has some flaws, but it is something.
MATLAB Code:
function [theta, dtheta, epsilon] = drt(t, theta0, dtheta0, A, B, C, D)
epsilon = zeros(1, length(t));
theta = epsilon;
dtheta = epsilon;
theta(1) = theta0;
dtheta(1) = dtheta0;
epsilon(1) = A * alpha0 - B * dtheta^2 - C * dtheta - D;
dt = t(2) - t(1);
for i = 1 : (length(t) - 1)
epsilon(i + 1)= - A * theta(i) - B * dtheta(i)^2 * sign(dtheta(i)) - C * dtheta(i) - D * sign(dtheta(i));
dtheta(i + 1)= dtheta(i) + dt * epsilon(i);
theta(i + 1) = theta(i) + dt * dtheta(i);
end
end
We call this MATLAB function like this for example:
t = linspace(0, 10, 100);
theta0 = 90;
dtheta0 = 0;
A = 1;
B = 0.1;
C = 0.1;
D = 0.1;
[theta, dtheta, epsilon] = drt(t, theta0, dtheta0, A, B, C, D);
We can then plot the theta and other values in a graph, which shows us, how the torsional pendulum is being damped by the external forces acting on it.
Python Code:
import numpy as np
import matplotlib.pyplot as plt
# Damping torsional pendulum
def drp(drp_alpha, drp_d_alpha, drp_params):
a = drp_params["tors"]
b = drp_params["newt"]
c = drp_params["stok"]
d = drp_params["fric"]
result = a * drp_alpha - b * np.power(drp_d_alpha, 2) * np.sign(drp_d_alpha) - c * drp_d_alpha - d * np.sign(drp_d_alpha)
return result
# Runge-Kutta 4th Order
# f - function DamRotPen
# x0 - initial condition
# t0 - initial time
# tmax - maximum time
# dt - sample time
def RG4(rg4_f, rg4_x0, rg4_t0, rg4_tmax, rg4_dt):
# Time vector
rg4_t = np.arange(rg4_t0, rg4_tmax, rg4_dt)
# Time vector size
rg4_t_sz = rg4_t.size
# Initialize the array
rg4_alpha = np.zeros(rg4_t_sz)
# Initial value of the system
rg4_alpha[0] = rg4_x0
for k in range(rg4_t_sz - 1):
k1 = dt * f(rg4_t[k], rg4_alpha[k])
k2 = dt * rg4_f(rg4_t[k] + dt / 2, rg4_alpha[k] + k1 / 2)
k3 = dt * rg4_f(rg4_t[k] + dt / 2, rg4_alpha[k] + k2 / 2)
k4 = dt * rg4_f(rg4_t[k] + dt, rg4_alpha[k] + k3)
rg4_d_alpha = (k1 + 2 * k2 + 2 * k3 + k4) / 6
rg4_alpha[k + 1] = rg4_alpha[k] + rg4_d_alpha
return rg4_alpha, rg4_t
# Parameters of the forces acting on the system
# tors - torsion parameter
# newt - Newton's parameter
# stok - Stokes' parameter
# fric - friction parameter
params = {"tors": 1, "newt": 0.1, "stok": 0.1, "fric": 0.1}
# Start parameters
alpha = 90
d_alpha = 0
# Initial time
t0 = 0
# Maximum time
tmax = 120
# Sample time
dt = 0.01
# Define DamRotPen function as 'f' using lambda
f = lambda t, alpha : drp(alpha, d_alpha, params)
# Try to solve this shit
alpha, t = RG4(f, alpha, t0, tmax, dt)
# Plot this shit
plt.plot(t, alpha, "r", "r", label="Position I guess")
plt.xlabel("Time t / s")
plt.grid()
plt.show()
After plotting the values, we can see on the graph, that the θ sky rockets after certain amount of time. I don't know what I'm doing wrong, I tried practically everything, so that's why I'm asking you for help. (Though I think part of the problem might be my misunderstanding on how to implement the Runge-Kutta method, maybe I got the math wrong, etc...)
I would like to know how to represent the Dirac delta function as source term in Fipy. I want to solve the following equation
I have tried the following code
from fipy import *
nx = 50
ny = 1
dx = dy = 0.025 # grid spacing
L = dx * nx
mesh = Grid2D(dx=dx, dy=dy, nx=nx, ny=ny)
phi = CellVariable(name="solution variable", mesh=mesh, value=0.)
Gamma=1
delta=1 # I want knowing how to make this right.
eqG = TransientTerm() == DiffusionTerm(coeff=Gamma)+delta
valueTopLeft = 0
valueBottomRight = 1
X, Y = mesh.faceCenters
facesTopLeft = ((mesh.facesLeft & (Y > L / 2)) | (mesh.facesTop & (X < L / 2)))
facesBottomRight = ((mesh.facesRight & (Y < L / 2)) |
(mesh.facesBottom & (X > L / 2)))
phi.constrain(valueTopLeft, facesTopLeft)
phi.constrain(valueBottomRight, facesBottomRight)
timeStepDuration = 10 * 0.9 * dx ** 2 / (2 * 0.8)
steps = 100
results=[]
for step in range(steps):
eqG.solve(var=phi, dt=timeStepDuration)
results.append(phi.value)
The code is working but I want the exact Dirac delta function. I looked up the numerix module but couldnt find such function. Sx1 and Sy1 are constants. Am using python 2.7
It's probably a good idea to smooth the Dirac delta function as is done with diffusion interface methods (see equations 11, 12 and 13 here). So, this is one choice
def delta_func(x, epsilon):
return ((x < epsilon) & (x > -epsilon)) * \
(1 + numerix.cos(numerix.pi * x / epsilon)) / 2 / epsilon
2 * epsilon is the width of the Dirac delta function and is chosen to be a few grid spacings wide. You could also just use 1 / dx and choose the closest grid point to the Dirac delta function's location. However, I think that becomes more grid dependent. Here is a working code in 1D.
from fipy import *
nx = 50
dx = dy = 0.025 # grid spacing
L = dx * nx
mesh = Grid1D(dx=dx, nx=nx)
phi = CellVariable(name="solution variable", mesh=mesh, value=0.)
Gamma=1
def delta_func(x, epsilon):
return ((x < epsilon) & (x > -epsilon)) * \
(1 + numerix.cos(numerix.pi * x / epsilon)) / 2 / epsilon
x0 = L / 2.
eqG = TransientTerm() == DiffusionTerm(coeff=Gamma)+ delta_func(mesh.x - x0, 2 * dx)
valueTopLeft = 0
valueBottomRight = 1
timeStepDuration = 10 * 0.9 * dx ** 2 / (2 * 0.8)
steps = 100
viewer = Viewer(phi)
for step in range(steps):
res = eqG.solve(var=phi, dt=timeStepDuration)
print(step)
viewer.plot()
input('stopped')
Here, epsilon = 2 * dx, an arbitrary choice, and the delta function is centered around L / 2. 2D just required multiplying the functions.
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.