I need help with getting the temp using Antoine Eq by making use of classes.
My root action is failing and I don't know why.
My code:
from __future__ import division, print_function
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import root
class Chemical(object):
def __init__(self, name_1, balance_1, name_2, balance_2):
self.name = name_1 + ' ' + '+' + ' ' + name_2
self.data_1 = []
self.data_2 = []
self.data_1 = balance_1
self.data_2 = balance_2
def __str__(self):
if (self.name):
return "Mixture: %s" % (self.name)
else:
return None
def bubble_solve(self,a,P = 1.01,T = 300):
A1,B1,C1 = self.data_1
A2,B2,C2 = self.data_2
PA = 10**(A1 - B1/(C1+T))
PB = 10**(A2 - B2/(C2+T))
sol = root(lambda T: P - (a*PA + (1-a)*PB),T)
return sol.x[0]
def dew_solve(self, b, P = 1.01, T = 300):
A1,B1,C1 = self.data_1
A2,B2,C2 = self.data_2
PA = 10**(A1 - B1/(C1+T))
PB = 10**(A2 - B2/(C2+T))
sol = root(lambda T: 1 - (b*P/PA + (1-b)*P/PB), T)
return sol.x[0]
mixture = Chemical('benzene', [ 3.98523 , 1184.24 , -55.578], 'toulene',
[4.05043 , 1327.62 , -55.528])
print(mixture)
print()
print(mixture.bubble_solve(0.5)) #at a = 0.5
print(mixture.bubble_solve(0.5,2)) #at a = 0.5 and P = 2
print(mixture.dew_solve(0.5)) #at b = 0.5
print(mixture.dew_solve(0.5,2)) #at b = 0.5 and P = 2
This is what my code is printing:
Mixture: benzene + toulene
300.0
300.0
300.0
300.0
However, the answers need to be:
365.087, 390.14188, 371.7743, 396.688.
Why does the root action fail?
Welcome to StackOverflow, #Nathaniel!
The issue is in your PA, PB, and sol lines in bubble_solve and dew_solve. You seem to be mixing constants with independent variables! For instance, with your bubble solve, you have the default value of 300 set for T. Thus in the PA line, the value 300 is being used for T! One way to fix this would be the following:
...
def bubble_solve(self,a,P = 1.01,T = 300):
A1,B1,C1 = self.data_1
A2,B2,C2 = self.data_2
PA = lambda x: 10**(A1 - B1/(C1+x))
PB = lambda x: 10**(A2 - B2/(C2+x))
sol = root(lambda x: P - (a*PA(x) + (1-a)*PB(x)),T)
return sol.x[0]
def dew_solve(self, b, P = 1.01, T = 300):
A1,B1,C1 = self.data_1
A2,B2,C2 = self.data_2
PA = lambda x: 10**(A1 - B1/(C1+x))
PB = lambda x: 10**(A2 - B2/(C2+x))
sol = root(lambda x: 1 - (b*P/PA(x) + (1-b)*P/PB(x)), T)
return sol.x[0]
...
This will return the values you have expected. Note that I use x as the independent variable for PA and PB, but that it's arbitrary. However, it's probably best to not reuse T for your anonymous functions if you are passing values to it.
Related
Say that I want to solve a parametric constrained optimization, method. Is there any method I can use to avoid looping over the parameters?
I would use a code (adapted to my problem) such as (credit):
import numpy as np
from scipy.optimize import minimize
import random
import pandas as pd
pd.options.display.float_format = '{:.5f}'.format
def uncovered(x):
## note x[0]--> x ; x[1]--> y
first_prod = x[0]**2-2*x[0]-4*x[1]+1
second_prod_first = x[0]**3*(8*a**2*(b-1)*b-4*a*(b-2)-3)
second_prod_second = x[0]**2*(-8*a**2*(b-1)*b-8*a+5)
second_prod_third = x[0]*(4*x[1]*(4*a*(b-1)+1)+4*a*b - 1) -4*x[1] - 1
denom = 8*(x[0]-1)*(x[0]*(2*a*(b-1)+1)-1)**2
if (x[0]-1==0):
final_func=0
else:
final_func = first_prod*(second_prod_first+second_prod_second+second_prod_third)/denom
return -final_func
def constraint_strict(x):
return x[1]-0.5*a*x[0]*(x[0]-1)*(1-b)-0.0001
def constraint_nonstrict(x):
return 0.25*(1-2*x[0]+x[0]**2) - x[1]
def constraint_1(x):
return 1-x[0]
def constraint_2(x):
return x[0]
def run_uncovered_market_maximization(alpha,xi):
a=alpha
b=xi
# initial guesses
n = 2
x0 = np.zeros(n)
x0[0] = 1
x0[1] = 1.0
success = False
# show initial objective
print("We are maximizing uncovered welfare:")
print('Initial Objective: ' + str(uncovered(x0)))
while success == False:
# optimize
bnds = ((0,1),(-np.inf,np.inf))
con1 = {'type': 'ineq', 'fun': constraint_strict}
con2 = {'type': 'ineq', 'fun': constraint_nonstrict}
cons = ([con1,con2])
solution = minimize(uncovered,x0,method='SLSQP', bounds=bnds,constraints=cons,options={'disp': False})
x = solution.x
success = solution.success
if success == False:
x0[0] = x0[0]-random.uniform(0, 1)
x0[1] = 5.0-random.uniform(0, 1)
indcons=(x[1]+a*(1-x[0])*x[0]*(1-b)/2)/((((1-x[0])**2)/4)+a*(1-x[0])*(1-b)*x[0]/2)
# show final objective
print("Success:"+str(solution.success))
print('Final Objective: ' + str(uncovered(x)))
print('First Constraint: ' + str(constraint_strict(x)))
print('Second Constraint: ' + str(constraint_nonstrict(x)))
# print solution
print('Solution')
print('d = ' + str(x[0]))
print('p = ' + str(x[1]))
print('market demand:'+ str(1-indcons))
return (solution.success, str(x[0]),str(x[1]),str(uncovered(x)),indcons)
I am unsure on how to handle a and b given that the maximization should not be over these and they only represent parameters. I want to have a pair (x,y) for any acceptable value of (a,b). How to set the boundaries? and how to insert a and b if not by looping over them?
For now I do:
optlist =[]
for b in np.linspace(start=0, stop=1, num=2000):
for a in np.linspace(start=0, stop=1, num=2000):
print('-------------Optimization for a:{} and b:{}-------------------'.format(a,b))
print('-------------Uncovered market')
(usuccess, du, pu,ufinal,uindcons) = run_uncovered_market_maximization(a,b)
I am trying to run the minimization process found in this publication. The equation is seen on page 6 of the document 16 of the pdf.
I have a dataframe that looks like the below
df = pd.DataFrame({'h_t':[7.06398,6.29948,5.04570,6.20774,4.80106],
'p_atm':[101057.772801,101324.416001,101857.702401,101724.380801,101991.024001],
'q_p':[5.768132,3.825600,2.772215,5.830429,2.619304],
'q_s':[2.684433,3.403679,2.384275,1.008078,2.387106],
'tdg_f':[117.678100,110.131579,108.376963,103.669725,113.594771],
'tdg_tw':[121.052635,119.710907,114.921463,112.156868,115.444900],
'temp_water':[11.92,19.43,16.87,7.45,11.83]})
I have a constraint that says the below function must be positive where b1 and b3 are the coefficients I am optimizing.
def q_ge(q_p,q_s,b1,b3):
return min(q_p,(b1*q_s+b3))
I wrote my constraint below, but I am not sure if it is right.
def constraint_q_ge(x):
b1,b2,b3=x
power_flow = df.apply(lambda x:q_ge(x['q_p'],x['q_s'],b1,b3), axis = 1)
const = power_flow<0
return -const.sum()
Is this correct? I run the function on all rows and check if any are less than 0 and sum this. The negative of that sum should be greater than or equal to 0. If there is even a single value less than 0 this constraint is not met.
EDIT:
Below is the full problem.
from scipy.constants import g as gravity
from sklearn.metrics import mean_squared_error
from math import sqrt
from scipy.optimize import minimize
import warnings
try:
from numpy import any as _any
except ImportError:
def _any(arg):
if arg is True:
return True
if arg is False:
return False
return any(arg)
def water_density(T=None, T0=None, units=None, a=None,
just_return_a=False, warn=True):
if units is None:
K = 1
m = 1
kg = 1
else:
K = units.Kelvin
m = units.meter
kg = units.kilogram
if T is None:
T = 298.15*K
m3 = m**3
if a is None:
a = (-3.983035*K, # C
301.797*K, # C
522528.9*K*K, # C**2
69.34881*K, # C
999.974950*kg/m3)
if just_return_a:
return a
if T0 is None:
T0 = 273.15*K
t = T - T0
if warn and (_any(t < 0*K) or _any(t > 40*K)):
warnings.warn("Temperature is outside range (0-40 degC)")
return a[4]*(1-((t + a[0])**2*(t + a[1]))/(a[2]*(t + a[3])))
def celsius_to_kelvin(t_celsius):
return t_celsius+273.15
def tailwater(h_t, temp_water, p_atm):
t_water_kelvin = celsius_to_kelvin(temp_water)
rho = water_density(t_water_kelvin)
g = gravity
return (1+(rho*g*h_t)/(2*p_atm))
def tailwater_tdg(q_s,q_p,x, h_t,temp_water,p_atm,tdg_f):
b1,b2,b3=x
A = ((q_s+b1*q_s+b3)/(q_s+q_p))
B = tailwater(h_t, temp_water, p_atm)
C = ((q_p-b1*q_s-b3)/(q_s+q_p))
return 100*A*B*b2+tdg_f*C
def q_ge(q_p,q_s,b1,b3):
return min(q_p,(b1*q_s+b3))
def rmse(y, y_hat):
return sqrt(mean_squared_error(y,y_hat))
def objective(x):
y_hat = df.apply(lambda r:tailwater_tdg(q_s=r['q_s'],q_p=r['q_p'],x=x, h_t=r['h_t'],temp_water=r['temp_water'],p_atm=r['p_atm'],tdg_f=r['tdg_f']), axis = 1)
y = df['tdg_tw']
return rmse(y, y_hat)
#constraints and bounds for optimization model. See reference for more information
def constraint_q_ge(x):
b1,b2,b3=x
power_flow = df.apply(lambda x:q_ge(x['q_p'],x['q_s'],b1,b3), axis = 1)
const = power_flow<0
return -const.sum()
constraints = [{'type':'ineq', 'fun':constraint_q_ge}]
bounds = [(-500,10000),(.00001,10000),(-500,10000)]
x0=[1,1,1]
sol = minimize(objective, x0, method = 'SLSQP',constraints = constraints, bounds = bounds,options={'disp':True, 'maxiter':100})
I am currently trying to plot a function which describes linear perturbation growth in cosmology for different world models. I would like to be able to have all the curves on the same set of axes, but am struggling with setting it up.
My aim is to plot this function D, with respect to z, but have multiple plots with varying density parameters ($\Omega$).
I have managed two solutions but both aren't working perfectly, the first is very inefficient (adding new functions for each set of parameters):
z = np.arange(0.0,10,0.1)
#density parameters
MOm = 1.0
MOv = 0.0
COm = 0.3
COv = 0.7
H0 = 70
def Mf(z):
A = (5/2)*MOm*(H0**2)
H = H0 * np.sqrt( MOm*((1+z)**3) + MOv )
return A * ((1+z)/(H**3))
def MF(z):
res = np.zeros_like(z)
for i,val in enumerate(z):
y,err = integrate.quad(Mf,val,np.inf)
res[i] = y
return res
def MD(z):
return (H0 * np.sqrt( MOm*((1+z)**3) + MOv )) * MF(z)
def Cf(z):
A = (5/2)*COm*(H0**2)
H = H0 * np.sqrt( COm*((1+z)**3) + COv )
return A * ((1+z)/(H**3))
def CF(z):
res = np.zeros_like(z)
for i,val in enumerate(z):
y,err = integrate.quad(Cf,val,np.inf)
res[i] = y
return res
def CD(z):
return (H0 * np.sqrt( COm*((1+z)**3) + COv )) * CF(z)
plt.plot(z,MD(z),label="Matter Dominated")
plt.plot(z,CD(z),label="Current Epoch")
So I tried to make it simpler with a for loop, but have been unable to work out how to add labels to each plot inside the loop:
Om = (1.0,0.3)
Ov = (0.0,0.7)
for param1,param2 in zip(Om,Ov):
def f(z):
A = (5/2)*param1*(H0**2)
H = H0 * np.sqrt( param1*((1+z)**3) + param2 )
return A * ((1+z)/(H**3))
def F(z):
res = np.zeros_like(z)
for i,val in enumerate(z):
y,err = integrate.quad(f,val,np.inf)
res[i] = y
return res
def D(z):
return (H0 * np.sqrt( param1*((1+z)**3) + param2 )) * F(z)
plt.plot(z,D(z))
Could someone please help explain an efficient method of doing so? Or how to add labels to plots on the fly with a for loop. Any help would be greatly appreciated.
So I tried to make it simpler with a for loop, but have been unable to work out how to add labels to each plot inside the loop
from scipy import integrate
from matplotlib import pyplot as plt
MOm = 1.0
MOv = 0.0
COm = 0.3
COv = 0.7
z = np.arange(0.0,10,0.1)
H0 = 70
Om = (1.0,0.3)
Ov = (0.0,0.7)
fig = plt.figure(1)
for param1,param2 in zip(Om,Ov):
def f(z):
A = (5/2)*param1*(H0**2)
H = H0 * np.sqrt( param1*((1+z)**3) + param2 )
return A * ((1+z)/(H**3))
def F(z):
res = np.zeros_like(z)
for i,val in enumerate(z):
y,err = integrate.quad(f,val,np.inf)
res[i] = y
return res
def D(z):
return (H0 * np.sqrt( param1*((1+z)**3) + param2 )) * F(z)
## Now define labels as you need and labels as follows:
plt.plot(z,D(z),label = 'Om: {}, Ov: {}'.format(param1,param2))
plt.legend()
You can create a label in the loop based on the two parameters using a predefined string which you format with the respective values.
label="Om {}, Ov {}".format(param1, param2)
In total, that would give:
import numpy as np
import scipy.integrate as integrate
import matplotlib.pyplot as plt
z = np.arange(0.0,10,0.1)
MOm = 1.0
MOv = 0.0
COm = 0.3
COv = 0.7
H0 = 70
Om = (1.0,0.3)
Ov = (0.0,0.7)
plt.figure(figsize=(3.8,2.4))
for param1,param2 in zip(Om,Ov):
def f(z):
A = (5/2)*param1*(H0**2)
H = H0 * np.sqrt( param1*((1+z)**3) + param2 )
return A * ((1+z)/(H**3))
def F(z):
res = np.zeros_like(z)
for i,val in enumerate(z):
y,err = integrate.quad(f,val,np.inf)
res[i] = y
return res
def D(z):
return (H0 * np.sqrt( param1*((1+z)**3) + param2 )) * F(z)
plt.plot(z,D(z), label="Om {}, Ov {}".format(param1, param2))
plt.legend()
plt.show()
I have been using matplotlib from python to show the animation of 1D wave equation.But I got a problem of making the animation.I want the image of the wave to change with time.It means that I may need a loop to form many different pictures of the wave equation.But it seems that the time cannot be put into the wave functions ,so the images do not change at all.Please help me with the mistake that I made.
Here are the codes that I wrote:(Part of the codes comes from the book "Python Scripting for Computational Science")
from numpy import zeros,linspace,sin,pi
import matplotlib.pyplot as mpl
def I(x):
return sin(2*x*pi/L)
def f(x,t):
return sin(x*t)
def solver0(I,f,c,L,n,dt,tstop):
# f is a function of x and t, I is a function of x
x = linspace(0,L,n+1)
dx = L/float(n)
if dt <= 0:
dt = dx/float(c)
C2 = (c*dt/dx)**2
dt2 = dt*dt
up = zeros(n+1)
u = up.copy()
um = up.copy()
t = 0.0
for i in range(0,n):
u[i] = I(x[i])
for i in range(1,n-1):
um[i] = u[i]+0.5*C2*(u[i-1] - 2*u[i] + u[i+1]) + dt2*f(x[i],t)
um[0] = 0
um[n] = 0
while t <= tstop:
t_old = t
t += dt
#update all inner points:
for i in range(1,n-1):
up[i] = -um[i] + 2*u[i] + C2*(u[i-1] - 2*u[i] + u[i+1]) + dt2*f(x[i],t_old)
#insert boundary conditions:
up[0] = 0
up[n] = 0
#update data structures for next step
um = u.copy()
u = up.copy()
return u
c = 3.0 #given by myself
L = 10
n = 100
dt = 0
tstart = 0
tstop = 6
x = linspace(0,L,n+1)
t_values = linspace(tstart,tstop,31)
mpl.ion()
y = solver0(I, f, c, L, n, dt, tstop)
lines = mpl.plot(x,y)
mpl.axis([x[0], x[-1], -1.0, 1.0])
mpl.xlabel('x')
mpl.ylabel('y')
counter = 0
for t in t_values:
y = solver0(I,f,c,L,n,dt,tstop)
lines[0].set_ydata(y)
mpl.draw()
mpl.legend(['t=%4.1f' % t])
mpl.savefig('sea_%04d.png' %counter)
counter += 1
Maybe that's what you need?
y = solver0(I,f,c,L,n,dt,t)
This question is a follow-up to my previous question here: Assistance, tips and guidelines for converting Matlab code to Python
I have converted the Matlab code manually. I am using a MAC OS and running Python from the terminal. But how do I run the code below, for some value of N, where N is an even number? I should get a graph (specified by the plot code).
When I run it as is, I get nothing.
My code is below:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def Array(N):
K00 = np.logspace(0,3,101,10)
len1 = len(K00)
y0 = [0]*(3*N/2+3)
S = [np.zeros((len1,1)) for kkkk in range(N/2+1)]
KS = [np.zeros((len1,1)) for kkkk in range(N/2)]
PS = [np.zeros((len1,1)) for kkkk in range(N/2)]
Splot = [np.zeros((len1,1)) for kkkk in range(N/2+1)]
KSplot = [np.zeros((len1,1)) for kkkk in range(N/2)]
PSplot = [np.zeros((len1,1)) for kkkk in range(N/2)]
Kplot = np.zeros((len1,1))
Pplot = np.zeros((len1,1))
for series in range(0,len1):
K0 = K00[series]
Q = 10
r1 = 0.0001
r2 = 0.001
d = 0.001
a = 0.001
k = 0.999
P0 = 1
S10 = 1e5
tf = 1e10
time = np.linspace(0,tf,len1)
y0[0] = S10
y0[3*N/2+1] = K0
y0[3*N/2+2] = P0
for i in range(1,3*N/2+1):
y0[i] = 0
[t,y] = odeint(EqnsArray,y0,time, mxstep = 5000)
for alpha in range(0,(N/2+1)):
S[alpha] = y[:,alpha]
for beta in range((N/2)+1,N+1):
KS[beta-N/2-1] = y[:,beta]
for gamma in range(N+1,3*N/2+1):
PS[gamma-N-1] = y[:,gamma]
for alpha in range(0,(N/2+1)):
Splot[alpha][series] = y[len1-1,alpha]
for beta in range((N/2)+1,N+1):
KSplot[beta-N/2-1][series] = y[len1-1,beta]
for gamma in range(N+1,3*N/2+1):
PSplot[gamma-N-1][series] = y[len1-1,gamma]
for alpha in range(0,(N/2+1)):
u1 = u1 + Splot[alpha]
for beta in range((N/2)+1,N+1):
u2 = u2 + KSplot[beta-N/2-1]
for gamma in range(N+1,3*N/2+1):
u3 = u3 + PSplot[gamma-N-1]
K = soln[:,3*N/2+1]
P = soln[:,3*N/2+2]
Kplot[series] = soln[len1-1,3*N/2+1]
Pplot[series] = soln[len1-1,3*N/2+2]
utot = u1+u2+u3
#Plot
plt.plot(np.log10(K00),utot)
plt.show()
def EqnsArray(y,t):
for alpha in range(0,(N/2+1)):
S[alpha] = y[alpha]
for beta in range((N/2)+1,N+1):
KS[beta-N/2-1] = y[beta]
for gamma in range(N+1,3*N/2+1):
PS[gamma-N-1] = y[gamma]
K = y[3*N/2+1]
P = y[3*N/2+2]
# The model equations
ydot = np.zeros((3*N/2+3,1))
B = range((N/2)+1,N+1)
G = range(N+1,3*N/2+1)
runsumPS = 0
runsum1 = 0
runsumKS = 0
runsum2 = 0
for m in range(0,N/2):
runsumPS = runsumPS + PS[m]
runsum1 = runsum1 + S[m+1]
runsumKS = runsumKS + KS[m]
runsum2 = runsum2 + S[m]
ydot[B[m]] = a*K*S[m]-(d+k+r1)*KS[m]
for i in range(0,N/2-1):
ydot[G[i]] = a*P*S[i+1]-(d+k+r1)*PS[i]
for p in range(1,N/2):
ydot[p] = -S[p]*(r1+a*K+a*P)+k*KS[p-1]+d*(PS[p-1]+KS[p])
ydot[0] = Q-(r1+a*K)*S[0]+d*KS[0]+k*runsumPS
ydot[N/2] = k*KS[N/2-1]-(r2+a*P)*S[N/2]+d*PS[N/2-1]
ydot[G[N/2-1]] = a*P*S[N/2]-(d+k+r2)*PS[N/2-1]
ydot[3*N/2+1] = (d+k+r1)*runsumKS-a*K*runsum2
ydot[3*N/2+2] = (d+k+r1)*(runsumPS-PS[N/2-1])- \
a*P*runsum1+(d+k+r2)*PS[N/2-1]
ydot_new = []
for j in range(0,3*N/2+3):
ydot_new.extend(ydot[j])
return ydot_new
You have to call your function, like:
Array(12)
You have to add this at the end of your code.