How to use multiprocessing pool for a multivariable function python - python

I'm trying to use the library multiprocessing, to run the next code:
import matplotlib.pyplot as plt
import numpy as np
from multiprocessing import Pool
def newtalt(fun,x0,err,mit):
xnew = x0.copy()
F, dF = fun(x0)
r = F.copy()
M = dF.copy()
sigma = np.linalg.norm(r)
for k in range(mit):
if sigma < err:
break
d = np.linalg.solve(M, -r)
xnew = xnew + d
r, M = fun(xnew)
sigma = np.linalg.norm(r)
return xnew, k
def fun(x):
f_r = x[0] ** 3 - 3 * x[0] * (x[1] ** 2) - 1
f_i = 3 * (x[0]**2) * x[1] - (x[1] ** 3)
f = np.array([f_r,f_i])
df_rx = 3 * (x[0] ** 2) - 3 * (x[1] ** 2)
df_ry = -6 * x[0] * x[1]
df_ix = 6 * x[0] * x[1]
df_iy = 3 * (x[0] ** 2) - 3 * (x[1] ** 2)
df = np.array([[df_rx,df_ry],[df_ix,df_iy]])
return f, df
if __name__=='__main__':
pool = Pool(processes=10)
err = 1e-5
mit = 300
N = 50
x = np.linspace(-2.5, 2.5, N)
y = np.linspace(-2.5, 2.5, N)
A = np.zeros((N, N))
B = np.zeros([N, N, 4], dtype=int)
for i in range(N):
for j in range(N):
z = np.array([x[i], y[j]])
xsol, it = pool.map(newtonalt,(fun,z,err,mit))
pool.close()
A[i,j] = it
plt.imshow(A,cmap='Set1')
plt.show()
Cleary it doesn't work, 'cause I don't really know how to use multiprocessing properly, and it's even more difficult when the function you use have multiple arguments. I read that isn't good use .pool() inside a for loop, but anyway, as I was saying, I'm really lost with this library.

Related

How to put a derivation as a boundary in "scipy.optimize.curve_fit"

I'm trying to get the coefficients of a function p(T,x). I provided the data for p, T and x from excel sheets via panda. The following Code works quite nice for me:
import pandas as pd
import os
from scipy.optimize import curve_fit
import numpy as np
df = pd.read_excel(os.path.join(os.path.dirname(__file__), "./Data.xlsx"))
T = np.array(df['T'], dtype=float)
x = np.array(df['x'], dtype=float)
p = np.array(df['p'], dtype=float)
p_s = 67.17
def func(X, a, b, c, d, e, f):
T, x = X
return x * p_s + x * (1 - x) * (a + b * T + c * T ** 2 + d * x + e * x * T + f * x * T ** 2) * p_s
popt, pcov = curve_fit(func, (T, x), p)
print("a = %s , b = %s, c = %s, d = %s, e = %s, f = %s" % (popt[0], popt[1], popt[2], popt[3], popt[4], popt[5]))
My acutal problem is, that the function is swinging a little bit in the end.
Because of this behaviour i get two x values for one p value, which i dont want.
So to avoid this little swing i want to accomplish a boundary condition for the fitting that say something like dp/dx (for constant T) > 0. With dp/dx I mean the derivation of the function after x.
Is this possible with the normal bound paramter of curve_fit? How can I do this?
EDIT:
As suggested I've messed a little bit around with least_square function but I guess that I came to a point where I don't realy understand what I'm doing or have to do.
T = np.array(df['T'], dtype=float)
x = np.array(df['x'], dtype=float)
p = np.array(df['p'], dtype=float)
p_s = 67
def f(X, z):
T, x = X
return x * p_s + x * (1 - x) * (z[0] + z[1] * T + z[2] * T ** 2 + z[3] * x + z[4] * x * T + z[5] * x * T ** 2) * p_s
def g(X,p,z):
return p - f(X ,z)
z0 = np.array([0,0,0,0,0,0], dtype=float)
res, flag = least_squares(g,z0, args=(T,x,p))
print(res)
With this code I get the following error:
TypeError: g() takes 3 positional arguments but 4 were given

Replacing multiprocessing pool.map with mpi4py

I'm a beginner in using MPI, and I'm still going through the documentation. However, there's very little to work on when it comes to mpi4py. I have written a code that currently uses the multiprocessing module to run on many cores, but I need replace this with mpi4py so that I can use more than one node to run my code. My code is below, when using the multiprocessing module, and also without.
With multiprocessing,
import numpy as np
import multiprocessing
start_time = time.time()
E = 0.1
M = 5
n = 1000
G = 1
c = 1
stretch = [10, 1]
#Point-Distribution Generator Function
def CDF_inv(x, e, m):
A = 1/(1 + np.log(m/e))
if x == 1:
return m
elif 0 <= x <= A:
return e * x / A
elif A < x < 1:
return e * np.exp((x / A) - 1)
#Elliptical point distribution Generator Function
def get_coor_ellip(dist=CDF_inv, params=[E, M], stretch=stretch):
R = dist(random.random(), *params)
theta = random.random() * 2 * np.pi
return (R * np.cos(theta) * stretch[0], R * np.sin(theta) * stretch[1])
def get_dist_sq(x_array, y_array):
return x_array**2 + y_array**2
#Function to obtain alpha
def get_alpha(args):
zeta_list_part, M_list_part, X, Y = args
alpha_x = 0
alpha_y = 0
for key in range(len(M_list_part)):
z_m_z_x = X - zeta_list_part[key][0]
z_m_z_y = Y - zeta_list_part[key][1]
dist_z_m_z = get_dist_sq(z_m_z_x, z_m_z_y)
alpha_x += M_list_part[key] * z_m_z_x / dist_z_m_z
alpha_y += M_list_part[key] * z_m_z_y / dist_z_m_z
return (alpha_x, alpha_y)
#The part of the process containing the loop that needs to be parallelised, where I use pool.map()
if __name__ == '__main__':
# n processes, scale accordingly
num_processes = 10
pool = multiprocessing.Pool(processes=num_processes)
random_sample = [CDF_inv(x, E, M)
for x in [random.random() for e in range(n)]]
zeta_list = [get_coor_ellip() for e in range(n)]
x1, y1 = zip(*zeta_list)
zeta_list = np.column_stack((np.array(x1), np.array(y1)))
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
print len(x)*len(y)*n,'calculations to be carried out.'
M_list = np.array([.001 for i in range(n)])
# split zeta_list, M_list, X, and Y
zeta_list_split = np.array_split(zeta_list, num_processes, axis=0)
M_list_split = np.array_split(M_list, num_processes)
X_list = [X for e in range(num_processes)]
Y_list = [Y for e in range(num_processes)]
alpha_list = pool.map(
get_alpha, zip(zeta_list_split, M_list_split, X_list, Y_list))
alpha_x = 0
alpha_y = 0
for e in alpha_list:
alpha_x += e[0] * 4 * G / (c**2)
alpha_y += e[1] * 4 * G / (c**2)
print("%f seconds" % (time.time() - start_time))
Without multiprocessing,
import numpy as np
E = 0.1
M = 5
G = 1
c = 1
M_list = [.1 for i in range(n)]
#Point-Distribution Generator Function
def CDF_inv(x, e, m):
A = 1/(1 + np.log(m/e))
if x == 1:
return m
elif 0 <= x <= A:
return e * x / A
elif A < x < 1:
return e * np.exp((x / A) - 1)
n = 1000
random_sample = [CDF_inv(x, E, M)
for x in [random.random() for e in range(n)]]
stretch = [5, 2]
#Elliptical point distribution Generator Function
def get_coor_ellip(dist=CDF_inv, params=[E, M], stretch=stretch):
R = dist(random.random(), *params)
theta = random.random() * 2 * np.pi
return (R * np.cos(theta) * stretch[0], R * np.sin(theta) * stretch[1])
#zeta_list is the list of coordinates of a distribution of points
zeta_list = [get_coor_ellip() for e in range(n)]
x1, y1 = zip(*zeta_list)
zeta_list = np.column_stack((np.array(x1), np.array(y1)))
#Creation of a X-Y Grid
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
def get_dist_sq(x_array, y_array):
return x_array**2 + y_array**2
#Calculation of alpha, containing the loop that needs to be parallelised.
alpha_x = 0
alpha_y = 0
for key in range(len(M_list)):
z_m_z_x = X - zeta_list[key][0]
z_m_z_y = Y - zeta_list[key][1]
dist_z_m_z = get_dist_sq(z_m_z_x, z_m_z_y)
alpha_x += M_list[key] * z_m_z_x / dist_z_m_z
alpha_y += M_list[key] * z_m_z_y / dist_z_m_z
alpha_x *= 4 * G / (c**2)
alpha_y *= 4 * G / (c**2)
Basically what my code does is, it first generates a list of points that follow a certain distribution. Then I apply an equation to obtain the quantity 'alpha' using different relations between the distances of the points. The part that requires parallelisation is the single for loop involved in the calculation of alpha. What I want to do is to use mpi4py instead of multiprocessing to do this, and I am not sure how to get this going.
Transforming the multiprocessing.map version to MPI can be done using scatter / gather. In your case it is useful, that you already prepare the input list into one chunk for each rank. The main difference is, that all code gets executed by all ranks in the first place, so you must make everything that should be done only by the maste rank 0 conidtional.
if __name__ == '__main__':
comm = MPI.COMM_WORLD
if comm.rank == 0:
random_sample = [CDF_inv(x, E, M)
for x in [random.random() for e in range(n)]]
zeta_list = [get_coor_ellip() for e in range(n)]
x1, y1 = zip(*zeta_list)
zeta_list = np.column_stack((np.array(x1), np.array(y1)))
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
print len(x)*len(y)*n,'calculations to be carried out.'
M_list = np.array([.001 for i in range(n)])
# split zeta_list, M_list, X, and Y
zeta_list_split = np.array_split(zeta_list, comm.size, axis=0)
M_list_split = np.array_split(M_list, comm.size)
X_list = [X for e in range(comm.size)]
Y_list = [Y for e in range(comm.size)]
work_list = list(zip(zeta_list_split, M_list_split, X_list, Y_list))
else:
work_list = None
my_work = comm.scatter(work_list)
my_alpha = get_alpha(my_work)
alpha_list = comm.gather(my_alpha)
if comm.rank == 0:
alpha_x = 0
alpha_y = 0
for e in alpha_list:
alpha_x += e[0] * 4 * G / (c**2)
alpha_y += e[1] * 4 * G / (c**2)
This works fine as long as each processor gets a similar amount of work. If communication becomes an issue, you might want to split up the data generation among processors instead of doing it all on the master rank 0.
Note: Some things about the code are bogus, e.g. alpha_[xy] ends up as np.ndarray. The serial version runs into an error.
For people who are still interested in similar subjects, I highly recommend having a look at the MPIPoolExecutor() class here and the documentation is here.

Numpy arange error with Lagrange Multiplier in Python

I try to use Lagrange multiplier to optimize a function, and I am trying to loop through the function to get a list of number, however I got the error
ValueError: setting an array element with a sequence.
Here is my code, where do I go wrong? If the n is not an array I can get the result correctly though
import numpy as np
from scipy.optimize import fsolve
n = np.arange(10000,100000,10000)
def func(X):
x = X[0]
y = X[1]
L = X[2]
return (x + y + L * (x**2 + y**2 - n))
def dfunc(X):
dLambda = np.zeros(len(X))
h = 1e-3
for i in range(len(X)):
dX = np.zeros(len(X))
dX[i] = h
dLambda[i] = (func(X+dX)-func(X-dX))/(2*h);
return dLambda
X1 = fsolve(dfunc, [1, 1, 0])
print (X1)
Helps would be appreciated, thank you very much
First, check func = fsolve()
Second, print(func([1,1,0]))` - result in not number ([2 2 2 2 2 2 2 2 2]), beause "n" is list. if you want to iterate n try:
import numpy as np
from scipy.optimize import fsolve
n = np.arange(10000,100000,10000)
def func(X,n):
x = X[0]
y = X[1]
L = X[2]
return (x + y + L * (x**2 + y**2 - n))
def dfunc(X,n):
dLambda = np.zeros(len(X))
h = 1e-3
r = 0
for i in range(len(X)):
dX = np.zeros(len(X))
dX[i] = h
dLambda[i] = (func(X+dX,n)-func(X-dX,n))/(2*h)
return dLambda
for iter_n in n:
print("for n = {0} dfunc = {1}".format(iter_n,dfunc([0.8,0.4,0.3],iter_n)))

Dropping lower order terms of a polynomial with Sympy

Say we have this function,
f = poly(2*x**2 + 3*x - 1,x)
How would one go about dropping terms of degree n or lower.
For instance if n = 1 the result would be 2*x**2.
from sympy import poly
from sympy.abc import x
p = poly(x ** 5 + 2 * x ** 4 - x ** 3 - 2 * x ** 2 + x)
print(p)
n = 2
new_p = poly(sum(c * x ** i[0] for i, c in p.terms() if i[0] > n))
print(new_p)
Output:
Poly(x**5 + 2*x**4 - x**3 - 2*x**2 + x, x, domain='ZZ')
Poly(x**5 + 2*x**4 - x**3, x, domain='ZZ')

Plotting a NACA 4-series airfoil

I'm trying to plot an airfoil from the formula as described on this wikipedia page.
This Jupyter notebook can be viewed on this github page.
%matplotlib inline
import math
import matplotlib.pyplot as pyplot
def frange( start, stop, step ):
yield start
while start <= stop:
start += step
yield start
#https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil
def camber_line( x, m, p, c ):
if 0 <= x <= c * p:
yc = m * (x / math.pow(p,2)) * (2 * p - (x / c))
#elif p * c <= x <= c:
else:
yc = m * ((c - x) / math.pow(1-p,2)) * (1 + (x / c) - 2 * p )
return yc
def dyc_over_dx( x, m, p, c ):
if 0 <= x <= c * p:
dyc_dx = ((2 * m) / math.pow(p,2)) * (p - x / c)
#elif p * c <= x <= c:
else:
dyc_dx = ((2 * m ) / math.pow(1-p,2)) * (p - x / c )
return dyc_dx
def thickness( x, t, c ):
term1 = 0.2969 * (math.sqrt(x/c))
term2 = -0.1260 * (x/c)
term3 = -0.3516 * math.pow(x/c,2)
term4 = 0.2843 * math.pow(x/c,3)
term5 = -0.1015 * math.pow(x/c,4)
return 5 * t * c * (term1 + term2 + term3 + term4 + term5)
def naca4( m, p, t, c=1 ):
for x in frange( 0, 1.0, 0.01 ):
dyc_dx = dyc_over_dx( x, m, p, c )
th = math.atan( dyc_dx )
yt = thickness( x, t, c )
yc = camber_line( x, m, p, c )
xu = x - yt * math.sin(th)
xl = x + yt * math.sin(th)
yu = yc + yt * math.cos(th)
yl = yc - yt * math.cos(th)
yield (xu, yu), (xl, yl)
#naca2412
m = 0.02
p = 0.4
t = 12
naca4points = naca4( m, p, t )
for (xu,yu),(xl,yl) in naca4points:
pyplot.plot( xu, yu, 'r,')
pyplot.plot( xl, yl, 'r,')
pyplot.ylabel('y')
pyplot.xlabel('x')
pyplot.axis('equal')
figure = pyplot.gcf()
figure.set_size_inches(16,16,forward=True)
The result looks like .
I expected it to look more like .
Questions: Why is the line not completely smooth? There seems to be a discontinuity where the beginning and end meet. Why does it not look like the diagram on wikipedia? How do I remove the extra loop at the trailing edge? How do I fix the chord so that it runs from 0.0 to 1.0?
First, t should be 0.12 not 12. Second, to make a smoother plot, increase the sample points.
It is also a good idea to use vectorize method in numpy:
%matplotlib inline
import math
import matplotlib.pyplot as plt
import numpy as np
#https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil
def camber_line( x, m, p, c ):
return np.where((x>=0)&(x<=(c*p)),
m * (x / np.power(p,2)) * (2.0 * p - (x / c)),
m * ((c - x) / np.power(1-p,2)) * (1.0 + (x / c) - 2.0 * p ))
def dyc_over_dx( x, m, p, c ):
return np.where((x>=0)&(x<=(c*p)),
((2.0 * m) / np.power(p,2)) * (p - x / c),
((2.0 * m ) / np.power(1-p,2)) * (p - x / c ))
def thickness( x, t, c ):
term1 = 0.2969 * (np.sqrt(x/c))
term2 = -0.1260 * (x/c)
term3 = -0.3516 * np.power(x/c,2)
term4 = 0.2843 * np.power(x/c,3)
term5 = -0.1015 * np.power(x/c,4)
return 5 * t * c * (term1 + term2 + term3 + term4 + term5)
def naca4(x, m, p, t, c=1):
dyc_dx = dyc_over_dx(x, m, p, c)
th = np.arctan(dyc_dx)
yt = thickness(x, t, c)
yc = camber_line(x, m, p, c)
return ((x - yt*np.sin(th), yc + yt*np.cos(th)),
(x + yt*np.sin(th), yc - yt*np.cos(th)))
#naca2412
m = 0.02
p = 0.4
t = 0.12
c = 1.0
x = np.linspace(0,1,200)
for item in naca4(x, m, p, t, c):
plt.plot(item[0], item[1], 'b')
plt.plot(x, camber_line(x, m, p, c), 'r')
plt.axis('equal')
plt.xlim((-0.05, 1.05))
# figure.set_size_inches(16,16,forward=True)
Thanks for the code.
I have modified the code for symmetrical airfoils:
def naca4s(x, t, c=1):
yt = thickness(x, t, c)
return ((x, yt),
(x, -yt))

Categories

Resources