Python Truth value of a series is ambiguous error in Function - python-3.x

I 'm trying to build a function that uses several scalar values as inputs and one series or array also as an input.
The function applies calculations to each value in the series. It works fine so far. But now I'm adding a phase where it has to check the value of the series and if it's less than X it performs one calculation other it performs a different calculation.
However I keep getting a 'truth value series is ambiguous error and I can't seem to solve it.
What is a work around?
My code is below
import numpy as np
import pandas as pd
import math
tramp = 2
Qo = 750
Qi = 1500
b = 1.2
Dei = 0.8
Df = 0.08
Qf = 1
tmax = 30
tper = 'm'
t = pd.Series(range(1,11))
def QHyp_Mod(Qi, b, Dei, Df, Qf, tmax, tper, t):
tper = 12
Qi = Qi * (365/12)
Qf = Qf * (365/12)
ai = (1 / b) * ((1 / (1 - Dei)) ** b - 1)
aim = ai / tper
ai_exp = -np.log(1 - Df)
aim_exp = ai_exp / tper
t_exp_sw = 118
Qi_exp = Qi / ((1 + aim * t_exp_sw * b) ** (1 / b))
Qcum = (Qi / (aim * (1 - b))) * (1 - (1 / ((1 + aim * t * b) ** ((1 - b) / b))))
t_exp = t - t_exp_sw
Qcum_Exp = (Qi_exp / aim_exp) * (1 - np.exp(-aim_exp * t_exp))
if t < t_exp_sw:
return Qcum
else:
return Qcum_exp
z = QHyp_Mod(Qi=Qi, b=b, Dei=Dei, Df=Df, Qf=Qf, tmax=tmax, tper=tper, t=t)

Replace the if - else statement:
if t < t_exp_sw:
return Qcum
else:
return Qcum_exp
with this:
Q.where(t < t_exp_sw, Q_exp)
return Q
The where method tests the conditional for each member of Q, if true keeps the original value, and if false replaces it with the corresponding element of Q_exp

Related

Simpson's rule 3/8 for n intervals in Python

im trying to write a program that gives the integral approximation of e(x^2) between 0 and 1 based on this integral formula:
Formula
i've done this code so far but it keeps giving the wrong answer (Other methods gives 1.46 as an answer, this one gives 1.006).
I think that maybe there is a problem with the two for cycles that does the Riemman sum, or that there is a problem in the way i've wrote the formula. I also tried to re-write the formula in other ways but i had no success
Any kind of help is appreciated.
import math
import numpy as np
def f(x):
y = np.exp(x**2)
return y
a = float(input("¿Cual es el limite inferior? \n"))
b = float(input("¿Cual es el limite superior? \n"))
n = int(input("¿Cual es el numero de intervalos? "))
x = np.zeros([n+1])
y = np.zeros([n])
z = np.zeros([n])
h = (b-a)/n
print (h)
x[0] = a
x[n] = b
suma1 = 0
suma2 = 0
for i in np.arange(1,n):
x[i] = x[i-1] + h
suma1 = suma1 + f(x[i])
alfa = (x[i]-x[i-1])/3
for i in np.arange(0,n):
y[i] = (x[i-1]+ alfa)
suma2 = suma2 + f(y[i])
z[i] = y[i] + alfa
int3 = ((b-a)/(8*n)) * (f(x[0])+f(x[n]) + (3*(suma2+f(z[i]))) + (2*(suma1)))
print (int3)
I'm not a math major but I remember helping a friend with this rule for something about waterplane area for ships.
Here's an implementation based on Wikipedia's description of the Simpson's 3/8 rule:
# The input parameters
a, b, n = 0, 1, 10
# Divide the interval into 3*n sub-intervals
# and hence 3*n+1 endpoints
x = np.linspace(a,b,3*n+1)
y = f(x)
# The weight for each points
w = [1,3,3,1]
result = 0
for i in range(0, 3*n, 3):
# Calculate the area, 4 points at a time
result += (x[i+3] - x[i]) / 8 * (y[i:i+4] * w).sum()
# result = 1.4626525814387632
You can do it using numpy.vectorize (Based on this wikipedia post):
a, b, n = 0, 1, 10**6
h = (b-a) / n
x = np.linspace(0,n,n+1)*h + a
fv = np.vectorize(f)
(
3*h/8 * (
f(x[0]) +
3 * fv(x[np.mod(np.arange(len(x)), 3) != 0]).sum() + #skip every 3rd index
2 * fv(x[::3]).sum() + #get every 3rd index
f(x[-1])
)
)
#Output: 1.462654874404461
If you use numpy's built-in functions (which I think is always possible), performance will improve considerably:
a, b, n = 0, 1, 10**6
x = np.exp(np.square(np.linspace(0,n,n+1)*h + a))
(
3*h/8 * (
x[0] +
3 * x[np.mod(np.arange(len(x)), 3) != 0].sum()+
2 * x[::3].sum() +
x[-1]
)
)
#Output: 1.462654874404461

How do I use multithreading on this function for a np.meshgrid of values?

The following code generates numpy 2D lists of r and E values for the specified intervals.
r = np.linspace(3, 14, 10)
E = np.linspace(0.05, 0.75, 10)
r, E = np.meshgrid(r, E)
I am then using the following nested loop to generate output from the function ionisationGamma for each r and E interval value.
for ridx in trange(len(r)):
z = []
for cidx in range(len(r[ridx])):
z.append(ionisationGamma(r[ridx][cidx], E[ridx][cidx]))
Z.append(z)
Z = np.array(Z)
This loop gives me a 2D numpy array Z, which is my output and I am using it for a 3D graph. The problem with it is: it is taking ~6 hours to generate the output for all these intervals as there are so many values due to np.meshgrid. I have just discovered multi-threading in Python and wanted to know how I can implement this by using it. Any help is appreciated.
See below code for ionisationGamma
def ionisationGamma(r, E):
I = complex(0.1, 1.0)
a_soft = 1.0
omega = 0.057
beta = 0.0
dt = 0.1
steps = 10000
Nintervals = 60
N = 3000
xmin = float(-300)
xmax = -xmin
x = [0.0]*N
dx = (xmax - xmin) / (N - 1)
L = dx * N
dk = 2 * M_PI / L
propagator = None
in_, out_, psi0 = None, None, None
in_ = [complex(0.,0.)] * N
psi0 = [complex(0.,0.)] * N
out_ = [[complex(0.,0.)]*N for i in range(steps+1)]
overlap = exp(-r) * (1 + r + (1 / 3) * pow(r, 2))
normC = 1 / (sqrt(2 * (1 + overlap)))
gammai = 0.5
qi = 0.0 + (r / 2)
pi = 0.0
gammai1 = 0.5
gammai2 = 0.5
qi1 = 0.0 - (r / 2)
qi2 = 0.0 + (r / 2)
pi1 = 0.0
pi2 = 0.0
# split initial wavepacket
for i in range(N):
x[i] = xmin + i * dx
out_[0][i] = (normC) * ((pow(gammai1 / M_PI, 1. / 4.) * exp(complex(-(gammai1 / 2.) * pow(x[i] - qi1, 2.), pi1 * (x[i] - qi1)))) + (pow(gammai2 / M_PI, 1. / 4.) * exp(complex(-(gammai2 / 2.) * pow(x[i] - qi2, 2.), pi2 * (x[i] - qi2)))))
in_[i] = (normC) * ((pow(gammai1 / M_PI, 1. / 4.) * exp(complex(-(gammai1 / 2.) * pow(x[i] - qi1, 2.), pi1 * (x[i] - qi1)))) + (pow(gammai2 / M_PI, 1. / 4.) * exp(complex(-(gammai2 / 2.) * pow(x[i] - qi2, 2.), pi2 * (x[i] - qi2)))))
psi0[i] = in_[i]
for l in range(1, steps+1):
for i in range(N):
propagator = exp(complex(0, -potential(x[i], omega, beta, a_soft, r, E, dt, l) * dt / 2.))
in_[i] = propagator * in_[i];
in_ = np.fft.fft(in_, N)
for i in range(N):
k = dk * float(i if i < N / 2 else i - N)
propagator = exp(complex(0, -dt * pow(k, 2) / (2.)))
in_[i] = propagator * in_[i]
in_ = np.fft.ifft(in_, N)
for i in range(N):
propagator = exp(complex(0, -potential(x[i], omega, beta, a_soft, r, E, dt, l) * dt / 2.))
in_[i] = propagator * in_[i]
out_[l][i] = in_[i]
initialGammaCentre = 0.0
finalGammaCentre = 0.0
for i in range(500, 2500 +1):
initialGammaCentre += pow(abs(out_[0][i]), 2) * dx
finalGammaCentre += pow(abs(out_[steps][i]), 2) * dx
ionisationGamma = finalGammaCentre / initialGammaCentre
return ionisationGamma
def potential(x, omega, beta, a_soft, r, E, dt, l):
V = (-1. / sqrt((x - (r / 2)) * (x - (r / 2)) + a_soft * a_soft)) + ((-1. / sqrt((x + (r / 2)) * (x + (r / 2)) + a_soft * a_soft))) + E * x
return V
Since the question is about how to use multiprocessing, the following code will work:
import multiprocessing as mp
if __name__ == '__main__':
with mp.Pool(processes=16) as pool:
Z = pool.starmap(ionisationGamma, arguments)
Z = np.array(Z)
Where the arguments are:
arguments = list()
for ridx in range(len(r)):
for cidx in range(len(r[ridx])):
arguments.append((r[ridx][cidx], E[ridx][cidx]))
I am using starmap instead of map, since you have multiple arguments that you want to unpack. This will divide the arguments iterable over multiple cores, using the ionisationGamma function and the final result will be ordered.
However, I do feel the need to say that the main solution is not really the multiprocessing but the original function code. In ionisationGamma you are using several times the slow python for loops. And it would benefit your code a lot if you could vectorize those operations.
A second observation is that you are using many of those loops separately and it would be nice if you could separate that one big function into multiple smaller functions. Then you can time every function individually and speed up those that are too slow.

How do I translate a math formula to python code?

I need to translate this formula into python:
x = b + (sqrt((b^2)-1)) / 2a
Can somebody please help me?
import math
x = b + (math.sqrt((b ** 2) - 1 )) / (2 * a)
Mathematically correct this solution is
import math
import cmath
def quadratic(a, b, c):
if b == 0:
f = (cmath.sqrt(b ** 2 - 1 )) / (2 * a)
else:
f = (math.sqrt(b ** 2 - 1 )) / (2 * a)
return b + f, b -f
print(quadratic(1, 2, 3))
This will give you the two roots.
You can access the roots by indexing:
my_roots = quadratic(1, 2, 3)
x_1 = my_roots[0]
x_2 = my_roots[1]
By using a Python function , you can re-use this code.
This should work as well. No need to import any library.
Assuming a != 0.
When b >= 1:
x = b + (((b ** 2) - 1 ) ** 0.5) / (2 * a)
NOTE: You should ideally have two roots for sqrt(b^2 - 1). Your function should return both values. Now, I am guessing that, under certain prior knowledge you have decided to only keep the positive root.
When b == 0, you get two roots. Note that sqrt(-1) = j or -j (complex roots).
x = (+1j) / (2 * a) # positive root
# OR
x = (-1j) / (2 * a) # negative root
More generally, when b**2 < 1, you get two complex roots.
x = b + (k*1j) ((1 - (b ** 2)) ** 0.5) / (2 * a)
# positive root: k = +1
# OR
# negative root: k = -1

Monte Carlo simulation of a system of polymer chain

I want to perform Monte Carlo simulation to the particles which are interacting via Lennard-Jones potential + FENE potential. I'm getting negative values in the FENE potential which have the log value in it. The error is "RuntimeWarning: invalid value encountered in log return (-0.5 * K * R**2 * np.log(1-((np.sqrt(rij2) - r0) / R)**2))" The FENE potential is given by:
import numpy as np
def gen_chain(N, R0):
x = np.linspace(1, (N-1)*0.8*R0, num=N)
y = np.zeros(N)
z = np.zeros(N)
return np.column_stack((x, y, z))
def lj(rij2):
sig_by_r6 = np.power(sigma/rij2, 3)
sig_by_r12 = np.power(sig_by_r6, 2)
lje = 4.0 * epsilon * (sig_by_r12 - sig_by_r6)
return lje
def fene(rij2):
return (-0.5 * K * R**2 * np.log(1-((np.sqrt(rij2) - r0) / R)**2))
def total_energy(coord):
# Non-bonded
e_nb = 0
for i in range(N):
for j in range(i-1):
ri = coord[i]
rj = coord[j]
rij = ri - rj
rij2 = np.dot(rij, rij)
if (np.sqrt(rij2) < rcutoff):
e_nb += lj(rij2)
# Bonded
e_bond = 0
for i in range(1, N):
ri = coord[i]
rj = coord[i-1]
rij = ri - rj
rij2 = np.dot(rij, rij)
e_bond += fene(rij2)
return e_nb + e_bond
def move(coord):
trial = np.ndarray.copy(coord)
for i in range(N):
delta = (2.0 * np.random.rand(3) - 1) * max_delta
trial[i] += delta
return trial
def accept(delta_e):
beta = 1.0/T
if delta_e <= 0.0:
return True
random_number = np.random.rand(1)
p_acc = np.exp(-beta*delta_e)
if random_number < p_acc:
return True
return False
if __name__ == "__main__":
# FENE parameters
K = 40
R = 0.3
r0 = 0.7
# LJ parameters
sigma = r0/0.33
epsilon = 1.0
# MC parameters
N = 50 # number of particles
rcutoff = 2.5*sigma
max_delta = 0.01
n_steps = 10000000
T = 0.5
coord = gen_chain(N, R)
energy_current = total_energy(coord)
traj = open('traj.xyz', 'w')
for step in range(n_steps):
if step % 1000 == 0:
traj.write(str(N) + '\n\n')
for i in range(N):
traj.write("C %10.5f %10.5f %10.5f\n" % (coord[i][0], coord[i][1], coord[i][2]))
print(step, energy_current)
coord_trial = move(coord)
energy_trial = total_energy(coord_trial)
delta_e = energy_trial - energy_current
if accept(delta_e):
coord = coord_trial
energy_current = energy_trial
traj.close()
The problem is that calculating rij2 = np.dot(rij, rij) in total energy with the constant values you use is always a very small number. Looking at the expression inside the log used to calculate FENE, np.log(1-((np.sqrt(rij2) - r0) / R)**2), I first noticed that you're taking the square root of rij2 which is not consistent with the formula you provided.
Secondly, notice that ((rij2 - r0) / R)**2 is the same as ((r0 - rij2) / R)**2, since the sign gets lost when squaring. Because rij2 is very small (already in the first iteration -- I checked by printing the values), this will be more or less equal to ((r0 - 0.05)/R)**2 which will be a number bigger than 1. Once you subtract this value from 1 in the log expression, 1-((np.sqrt(rij2) - r0) / R)**2 will be equal to np.nan (standing for "Not A Number"). This will propagate through all the function calls (for example, calling energy_trial = total_energy(coord_trial) will effectively set energy_trial to np.nan), until an error will be raised by some function.
Maybe you could do something with np.isnan() call, documented here. Moreover, you should check how you iterate through the coord (there's some inconsistencies throughout the code) -- I suggest you check the code review community as well.

How to fix Range function in Python?

I am trying to convert MATLAB code into Python and am facing errors related to the range function of Python.
The entire code can be found here and I am working on Range Imaging code.
MATLAB code
Ts=(2*(Xc-X0))/c;
Tf=(2*(Xc+X0))/c+Tp;
n=2*ceil((.5*(Tf-Ts))/dt);
t=Ts+(0:n-1)*dt;
dw=pi2/(n*dt);
w=wc+dw*(-n/2:n/2-1);
x=Xc+.5*c*dt*(-n/2:n/2-1);
kx=(2*w)/c;
value of dt is 2.500000000000000e-09, n is 4268, Ts is 1.300000000000000e-05
Python
Ts = (2 * (Xc - X0)) / c
Tf = (2 * (Xc - X0)) / c + Tp
n = 2 * math.ceil((.5 * (Tf - Ts)) / dt)
t = list(Ts + (np.array(range(0, n-1)) * dt)) # tried using the solution in the comments
dw = pi2 / (n * dt)
w = list(wc + dw * (np.array(range(-n/2,n/2-1)))) # getting error here after trying same kind of solution
x = Xc + .5 * c * dt * range(-n/2,n/2-1)
kx=(2 * w) / c
The Python code throws the following error:
TypeError: 'float' object cannot be interpreted as an integer
Since you are coming from Matlab, you most likely want to use numpy for vector/matrix calculations. Lists in python cannot be multiplied like arrays in Matlab, but numpy arrays can. range will result in a range object, which you can convert to a numpy array, or you can directly use numpy.arange:
import numpy as np
import math
Ts = (2 * (Xc - X0)) / c
Tf = (2 * (Xc - X0)) / c + Tp
n = 2 * math.ceil((.5 * (Tf - Ts)) / dt)
t = Ts + np.arange(0, n*dt, dt) # np.arange(start, stop, step)
dw = pi2 / (n * dt)
w = wc + dw * np.arange(-n/2, n/2) # not n/2-1 since stop is not included
x = Xc + 0.5 * c * dt * np.arange(-n/2, n/2)
kx = (2 * w) / c
A difference between Matlab and Numpy in this case is that Matlab will include the last value (i.e. interval [start, stop]) where numpy does not (i.e. interval [start,stop)). Meaning that you will have to use n*dt for the stop input argument.
The range function in Python returns a range object which is itself just a list. Lists cannot be multiplied with a decimal number, which is what you're trying to do: range(0,(n-1)) * dt.
But you could convert the range list to a numpy array:
t = list(Ts + (numpy.array(range(0, n-1)) * dt))

Resources