I am trying to simulate a physics situation that involves calculating very small numbers. When the numbers get too small the values become garbage and/or are rounded to zero which doesn't help me. I am also using the scipy constants module for certain constants. I am trying to calculate the position using Euler's Method and calculating the velocity using momentum. The physics isn't the important part in this problem.
I have tried using the decimal module but think I am running into problems when using decimal and scipy constants together. Also, when using Decimal, do i need to convert every variable into Decimal before calculating?
In the loop below, it can only compute about 3 values before the error occurs.
# Create the arrays for velocity and position
vx = sp.zeros(n+1)
vy = sp.zeros(n+1)
x = sp.zeros(n+1)
y = sp.zeros(n+1)
time = sp.zeros(n+1)
# Initialize our values
vx[0] = vx0
vy[0] = vy0
x[0] = x0
y[0] = y0
time[0] = 0
i = 0
while y[i] > 0:
step = math.sqrt(x[i]**2 + y[i]**2) / cs.c
vx[i + 1] = vx[i] + (((cs.hbar * cs.c) / (2*cs.electron_mass)) * (x[i] / (x[i]**2 + y[i]**2)))
vy[i + 1] = vy[i] + (((cs.hbar * cs.c) / (2*cs.electron_mass)) * (y[i] / (x[i]**2 + y[i]**2)))
x[i + 1] = x[i] + (vx[i] * step)
y[i + 1] = y[i] + (vy[i] * step)
i += 1
RuntimeWarning: invalid value encountered in double_scalars
First and foremost, I'd start with using natural units , with hbar=c=m=1. Then re-evaluate if underflow persists or not.
Related
Is there a way to convert number ranges?
I need to convert a linear range (0-1) to a logarithmic one (100*10^-12 - 1) so I can put a put a moveable horizontal line on a plotly plot (https://plotly.com/python/horizontal-vertical-shapes/#horizontal-and-vertical-lines-in-dash).
As far as I’m aware I can’t make my slider logarithmic to begin with (https://dash.plotly.com/dash-core-components/slider#non-linear-slider-and-updatemode).
I’ve tried normalizing. I’m not sure if that’s the right word, but basically putting my value into:
f(x) = log10(x * (max-min) + min)
Where:
x is the linear value being converted
max is the max of the log scale (1)
min is the min of the log scale (100*10^-12)
But f(.2) = .447 when I’m expecting 10*10^-9.
Is there a way accomplish this (or a better way to put a moveable horizontal line on the plot)?
BTW, 100*10^-12== 10^-10.
Seems you want to take logarithm of values at 10^-10..1 range to map them into 0..1 range and vice versa?
Y = A * log10(B * X)
substituting end values:
0 = A * log10(B * 10^-10) = A * (log10(B) - 10)
log10(B) = 10
B = 10^10
1 = A * log10(10^10 * 1) = A * 10
A = 0.1
So formula is
Y = 0.1 * log10(10^10 * X) =
1 + 0.1 * log10(X)
Reverse formula
10*Y = log10(10^10 * X)
10^(10*Y) = 10^10 * X
X = 10^(10*Y) * 10^-10 =
10^(10*Y-10)
using your example Y=0.2, we get X = 10^-8 as expected
from math import log10
for i in range(-10, 1):
X = 10**i
Y = 1 + 0.1 * log10(X)
print(Y)
print()
for i in range(0, 11):
Y = i / 10
X = 10**(10*Y-10)
print(X)
I am trying to solve a function in an annular domain that has a change of phase with respect to the angular direction of the annulus.
My attempt to solve it is the following:
import numpy as np
from scipy import integrate
def f(x0, y0):
r = np.sqrt(x0**2 + y0**2)
if r >= rIn and r <= rOut:
theta = np.arctan(y0 / x0)
R = np.sqrt((x - x0)**2 + (y - y0)**2 + z**2)
integrand = (np.exp(-1j * (k*R + theta))) / R
return integrand
else:
return 0
# Test
rIn = 0.5
rOut = 1.5
x = 1
y = 1
z = 1
k = 3.66
I = integrate.dblquad(f, -rOut, rOut, lambda x0: -rOut, lambda x0: rOut)
My problem is that I don't know how to get rid of the division by zero occuring when I evaluate theta.
Any help will be more than appreciated!
Use numpy.arctan2 instead, it will have problems only if both x and y are zero, in which case the angle is undetermined.
Also I see you that your integrand is complex, in this case you will probably have to handle real and imaginary part separately, as done here.
Using MATLAB, I am trying to solve the equation using Newtons Method but keep printing the result "None". I am not sure where the mistake is as I do not want to tamper with the formula.
def newt(p):
maxIt = 1000
tol = 10^(-5)
for i in range(maxIt):
fp = 2*p**3 + p**2 - p + 1 #double * for exponent
fprimep = 6*p**2 + 2*p - 1 #f'p
p_new = p - fp/fprimep
if abs (p - p_new) < tol:
return p
p = p_new
#initial values
p0 = -1.2
p = newt(p0)
print(p)
The error in your code is due to a partial conversion from Matlab. You define tol = 10^(-5), but this is not exponentiation in Python, it is bitwise xor. Correcting that, you get the proper result:
def newt(p):
maxIt = 1000
tol = 1e-5 # Error was here
for i in range(maxIt):
fp = 2*p**3 + p**2 - p + 1 #double * for exponent
fprimep = 6*p**2 + 2*p - 1 #f'p
p_new = p - fp/fprimep
if abs (p - p_new) < tol:
return p
p = p_new
#initial values
p0 = -1.2
p = newt(p0)
print(p)
# -1.23375
As for the return value, your function returns None when the method does not converge. I'm not sure this was a intentional decision, but it is a good convention anyway, since it allows the user to know the method did not converge. A more robust method would be to throw an exception, maybe adding the guess at the time the iteration stopped and the convergence ratio.
I have two solutions of the current equation:
The first one is using Finite difference scheme ( code below ):
# Some variable declarations
nx = 300
ny = 300
nt = 100
xmin = 0.
xmax = 2.
ymin = 0.
ymax = 1.
dx = (xmax - xmin) / (nx - 1)
dy = (ymax - ymin) / (ny - 1)
# Initialization
p = np.zeros((nx, ny))
pd = np.zeros((nx, ny))
b = np.zeros((nx, ny))
# Source
b[int(nx / 4), int(ny / 4)] = 100
b[int(3 * nx / 4), int(3 * ny / 4)] = -100
for it in range(nt):
pd = p.copy()
p[1:-1,1:-1] = (((pd[1:-1, 2:] + pd[1:-1, :-2]) * dy**2 +
(pd[2:, 1:-1] + pd[:-2, 1:-1]) * dx**2 -
b[1:-1, 1:-1] * dx**2 * dy**2) /
(2 * (dx**2 + dy**2)))
p[0, :] = 0
p[nx-1, :] = 0
p[:, 0] = 0
p[:, ny-1] = 0
Using FFT I have the following code:
def poisson(b,nptx,npty,dx,dy,nboundaryx,nboundaryy):
p = np.zeros((nptx,npty))
ppad = np.zeros((nptx+nboundaryx,npty+nboundaryy))
phatpad = np.zeros((nptx+nboundaryx,npty+nboundaryy))
bpad = np.zeros((nptx+nboundaryx,npty+nboundaryy))
bpad[:nptx,:npty] = b
kxpad = 2*np.pi*np.fft.fftfreq(nptx+nboundaryx,d=dx)
kypad = 2*np.pi*np.fft.fftfreq(npty+nboundaryy,d=dy)
epsilon = 1.e-9
ppad = np.real(np.fft.ifft2(-np.fft.fft2(bpad)/np.maximum(kxpad[None, :]**2 + kypad[:, None]**2,epsilon)))
p = ppad[:nptx,:npty]
p[0,:] = 0
p[nptx-1,:] = 0
p[:,0] = 0
p[:,npty-1] = 0
return p
nptx = 300
npty = 300
b = np.zeros((nptx, npty))
b[int(nptx / 4), int(npty / 4)] = 100
b[int(3 * nptx / 4), int(3 * npty / 4)] = -100
xmin = 0.
xmax = 2.
ymin = 0.
ymax = 1.
nboundaryx = 0
nboundaryy = 0
dx = (xmax - xmin) / (nptx+nboundaryx - 1)
dy = (ymax - ymin) / (npty+nboundaryy - 1)
print(dx)
p = poisson(b,nptx,npty,dx,dy,nboundaryx,nboundaryy)
The results are:
First image using Finite Difference
Second image using FFT
I know using FD scheme is correct but not sure if I did in FFT correctly. I see a round shape on FFT, is this correct?
There are two main differences
For the finite differences you are calculating the discrete differences, for the FFT solution you are simply computing the poison operator on the continuous space and applying that to your equation. To compute the finite differences exactly the same way you would need to use the in the discrete domain instead of calculating the fft what you can do is to remember that fft(roll(x, 1)) = exp(-2j * np.pi * np.fftfreq(N))* fft(x) where roll denotes the circular shift by oen sample.
Other point is that you are using boundary conditions (zero potential on the walls) the quick and dirty solution is to use method of image charges to ensure the potential vanishes on the walls and compute the poison equation on the augmented space. If you care about memory usage or solution purity you could use the sine transform that has slightly more complicated translation formulas, but can be computed without augmenting the space since the potential is forced to be zero on the boundaries by its definition (because sin(pi * n) = 0 for any integer n)
The solution in the frequency domain is a direct solution, you calculate each coefficient with a closed formula and then perform the inverse Fourier transform, no iteration is required. The accuracy tends to be good as well, as far as you compute the differences with enough accuracy.
If you are really worried about this, you should focus in differences like (1 - exp(2j*pi/N)) because the second term is close to 1, the number of significative bits will be reduced. But you can improve the accuracy of such expressions by factoring it as exp(1j*pi/N) * (exp(-1j*pi/N) - exp(1j*pi/N)) = exp(1j*pi/N) * (-2j * sin(pi/N)) where you have a product and you don't loose any significant bit. All of this is more important if you are compluting it in single or half precision (you probably will not notice any rounding error using numpy.float64 or numpy.complex128).
If you calculate in the frequency domain and you are not happy with the accuracy you can always "refine" it with some iterations of your finite differences equation.
Let (0,0) and (Xo,Yo) be two points on a Cartesian plane. We want to determine the parabolic curve, Y = AX^2 + BX + C, which passes from these two points and has a given arc length equal to S. Obviously, S > sqrt(Xo^2 + Yo^2). As the curve must pass from (0,0), it should be C=0. Hence, the curve equation reduces to: Y = AX^2 + BX. How can I determine {A,B} knowing {Xo,Yo,S}? There are two solutions, I want the one with A>0.
I have an analytical solution (complex) that gives S for a given set of {A,B,Xo,Yo}, though here the problem is inverted... I can proceed by solving numerically a complex system of equations... but perhaps there is a numerical routine out there that does exactly this?
Any useful Python library? Other ideas?
Thanks a lot :-)
Note that the arc length (line integral) of the quadratic a*x0^2 + b*x0 is given by the integral of sqrt(1 + (2ax + b)^2) from x = 0 to x = x0. On solving the integral, the value of the integral is obtained as 0.5 * (I(u) - I(l)) / a, where u = 2ax0 + b; l = b; and I(t) = 0.5 * (t * sqrt(1 + t^2) + log(t + sqrt(1 + t^2)), the integral of sqrt(1 + t^2).
Since y0 = a * x0^2 + b * x0, b = y0/x0 - a*x0. Substituting the value of b in u and l, u = y0/x0 + a*x0, l = y0/x0 - a*x0. Substituting u and l in the solution of the line integral (arc length), we get the arc length as a function of a:
s(a) = 0.5 * (I(y0/x0 + a*x0) - I(y0/x0 - a*x0)) / a
Now that we have the arc length as a function of a, we simply need to find the value of a for which s(a) = S. This is where my favorite root-finding algorithm, the Newton-Raphson method, comes into play yet again.
The working algorithm for the Newton-Raphson method of finding roots is as follows:
For a function f(x) whose root is to be obtained, if x(i) is the ith guess for the root,
x(i+1) = x(i) - f(x(i)) / f'(x(i))
Where f'(x) is the derivative of f(x). This process is continued till the difference between two consecutive guesses is very small.
In our case, f(a) = s(a) - S and f'(a) = s'(a). By simple application of the chain rule and the quotient rule,
s'(a) = 0.5 * (a*x0 * (I'(u) + I'(l)) + I(l) - I(u)) / (a^2)
Where I'(t) = sqrt(1 + t^2).
The only problem that remains is calculating a good initial guess. Due to the nature of the graph of s(a), the function is an excellent candidate for the Newton-Raphson method, and an initial guess of y0 / x0 converges to the solution in about 5-6 iterations for a tolerance/epsilon of 1e-10.
Once the value of a is found, b is simply y0/x0 - a*x0.
Putting this into code:
def find_coeff(x0, y0, s0):
def dI(t):
return sqrt(1 + t*t)
def I(t):
rt = sqrt(1 + t*t)
return 0.5 * (t * rt + log(t + rt))
def s(a):
u = y0/x0 + a*x0
l = y0/x0 - a*x0
return 0.5 * (I(u) - I(l)) / a
def ds(a):
u = y0/x0 + a*x0
l = y0/x0 - a*x0
return 0.5 * (a*x0 * (dI(u) + dI(l)) + I(l) - I(u)) / (a*a)
N = 1000
EPSILON = 1e-10
guess = y0 / x0
for i in range(N):
dguess = (s(guess) - s0) / ds(guess)
guess -= dguess
if abs(dguess) <= EPSILON:
print("Break:", abs((s(guess) - s0)))
break
print(i+1, ":", guess)
a = guess
b = y0/x0 - a*x0
print(a, b, s(a))
Run the example on CodeSkulptor.
Note that due to the rational approximation of the arc lengths given as input to the function in the examples, the coefficients obtained may ever so slightly differ from the expected values.