2023년 4월 16일 일요일

PC-SAFT equation of state

VLE_mixture

혼합물의 기체-액체 상평형 (vapor-liquid phase equilibrium)


The notebook executes VLE_mixture.py that uses DATA.txt as input

In [1]:
%ls DATA.txt  *.py
 C 드라이브의 볼륨: Windows10
 볼륨 일련 번호: 70CE-288B

 C:\DATA\Ch\Chang\Theory\SAFT  PC-SAFT\Mixture\2020 PC-SAFT 디렉터리


 C:\DATA\Ch\Chang\Theory\SAFT  PC-SAFT\Mixture\2020 PC-SAFT 디렉터리

2021-02-15  오후 01:47             1,068 DATA.txt
2018-03-07  오전 04:43            18,456 pcsaft_mix.py
2018-04-19  오후 04:31            42,322 VLE_mixture.py
               3개 파일              61,846 바이트
               0개 디렉터리  80,717,217,792 바이트 남음
In [2]:
!type DATA.txt
[ ethanol and water binary mixture ]
# EOS :   0 for PR   1 for PC-SAFT
1
# ncomp
2
-----------------------------------------------
ethanol (polar)
# Mw (g/mol)  Tc (K)  Pc (bar)   omega    Tnb (K)
46.069  513.9  61.4  0.644  351.4
#  m    sig (A)   epsk (K)  kAB     eAB (K)  dipole(D)  xp
2.2049  3.2774  187.24  0.03363  2652.7  1.7  0.29466
-----------------------------------------------
water (polar)
# Mw (g/mol)  Tc (K)  Pc (bar)   omega    Tnb (K)
18.015  647.3  221.2  0.344  373.15
#  m    sig (A)   epsk (K)  kAB     eAB (K)  dipole(D)  xp
1.0405  2.9657  175.15  0.08924  2706.7  1.85  0.66245
-----------------------------------------------
# k12   k13 ..  k1N,   k23 ...
0.  
#   i   j    kAiBj     eAiBj (K)  -- cross association override
------------------------------------ end of cross association
# type of calculation: 0(Z), 1(phi), 2(BublP) 3(DewP) 4(BublT) 5(DewT) 6(Flash)
4
#   T (K)     P (bar)     z1    z2  ... zN 
351.4  1.013    -0.5  0.5 
# scaling fraction for initial liquid volume
0.99
--EOF------------------------------------------
In [3]:
%matplotlib inline
In [4]:
%run VLE_mixture.py
=========================================
  Vapor-Liquid Equilibrium of Mixture    
=========================================
[ ethanol and water binary mixture ]

using PC-SAFT eos ... 

Component 1  =  ethanol (polar) 
 Mw (g/mol)  Tc (K)   Pc (bar)   omega     Tb (K) 
   46.069    513.90     61.400   0.644     351.40
     m       sig (A)  epsk (K)     kAB      eAB (K)  dipole (D)  xp 
   2.2049    3.2774    187.24    0.033630  2652.70    1.700    0.29466 

Component 2  =  water (polar) 
 Mw (g/mol)  Tc (K)   Pc (bar)   omega     Tb (K) 
   18.015    647.30    221.200   0.344     373.15
     m       sig (A)  epsk (K)     kAB      eAB (K)  dipole (D)  xp 
   1.0405    2.9657    175.15    0.089240  2706.70    1.850    0.66245 

Kij's are 
   0.0000    0.0000 
   0.0000    0.0000 

cross association override in PC-SAFT 
    i     j    kAiBj     eAiBj (K)
The number of overrides is = 0 

checking associating components in PC-SAFT ...
  component 1  is associating  
  component 2  is associating  
The number of associating components = 2 

Bubble T calculation at P =    1.013 (bar) 

scaling fraction for initial liquid volume =    0.99 

-- Experimental data -- 
     T (K)    P (bar)    x1        y1 
press Enter to proceed 

<<  Txy - Bubble point temperature calculation for given P, x  >> 
 
 P  =     1.013 (bar) 

  T (K)      x1        y1       iteration
-----------------------------------------
   374.55    0.0000    0.0000       4 
   372.88    0.0050    0.0586       4 
   371.37    0.0100    0.1089       4 
   370.05    0.0150    0.1520       4 
   368.84    0.0200    0.1896       5 
   367.76    0.0250    0.2224       5 
   366.80    0.0300    0.2511       4 
   365.91    0.0350    0.2766       4 
   365.11    0.0400    0.2992       4 
   364.38    0.0450    0.3195       4 
   363.71    0.0500    0.3377       4 
   363.10    0.0550    0.3540       4 
   362.55    0.0600    0.3688       4 
   362.02    0.0650    0.3824       4 
   361.56    0.0700    0.3945       4 
   361.11    0.0750    0.4058       4 
   360.71    0.0800    0.4161       4 
   360.36    0.0850    0.4253       4 
   360.00    0.0900    0.4341       4 
   359.70    0.0950    0.4420       4 
   359.40    0.1000    0.4494       4 
   359.11    0.1050    0.4564       4 
   358.87    0.1100    0.4627       4 
   358.62    0.1150    0.4688       4 
   358.39    0.1200    0.4744       4 
   358.20    0.1250    0.4795       4 
   357.99    0.1300    0.4845       4 
   357.81    0.1350    0.4891       4 
   357.65    0.1400    0.4933       4 
   357.48    0.1450    0.4974       4 
   357.32    0.1500    0.5014       4 
   357.17    0.1550    0.5051       4 
   357.05    0.1600    0.5085       4 
   356.92    0.1650    0.5118       4 
   356.78    0.1700    0.5151       4 
   356.67    0.1750    0.5181       4 
   356.56    0.1800    0.5209       4 
   356.46    0.1850    0.5236       3 
   356.36    0.1900    0.5263       4 
   356.25    0.1950    0.5290       4 
   356.16    0.2000    0.5314       3 
   356.08    0.2050    0.5337       3 
   355.99    0.2100    0.5361       3 
   355.91    0.2150    0.5384       3 
   355.83    0.2200    0.5405       3 
   355.76    0.2250    0.5426       3 
   355.68    0.2300    0.5447       3 
   355.61    0.2350    0.5467       3 
   355.55    0.2400    0.5486       3 
   355.48    0.2450    0.5506       3 
   355.41    0.2500    0.5525       3 
   355.35    0.2550    0.5543       3 
   355.29    0.2600    0.5561       3 
   355.23    0.2650    0.5579       3 
   355.17    0.2700    0.5597       3 
   355.11    0.2750    0.5615       3 
   355.05    0.2800    0.5632       3 
   355.00    0.2850    0.5649       3 
   354.94    0.2900    0.5667       3 
   354.89    0.2950    0.5684       3 
   354.84    0.3000    0.5700       3 
   354.78    0.3050    0.5717       3 
   354.73    0.3100    0.5734       3 
   354.68    0.3150    0.5751       3 
   354.63    0.3200    0.5767       3 
   354.58    0.3250    0.5784       3 
   354.53    0.3300    0.5801       3 
   354.49    0.3350    0.5818       3 
   354.44    0.3400    0.5834       3 
   354.39    0.3450    0.5851       3 
   354.35    0.3500    0.5868       3 
   354.30    0.3550    0.5885       3 
   354.25    0.3600    0.5901       3 
   354.21    0.3650    0.5918       3 
   354.16    0.3700    0.5936       3 
   354.12    0.3750    0.5953       3 
   354.08    0.3800    0.5970       3 
   354.04    0.3850    0.5986       3 
   354.00    0.3900    0.6004       3 
   353.96    0.3950    0.6021       3 
   353.91    0.4000    0.6039       3 
   353.87    0.4050    0.6057       3 
   353.83    0.4100    0.6075       3 
   353.79    0.4150    0.6093       3 
   353.74    0.4200    0.6111       3 
   353.70    0.4250    0.6130       3 
   353.66    0.4300    0.6148       3 
   353.62    0.4350    0.6167       3 
   353.58    0.4400    0.6186       3 
   353.54    0.4450    0.6205       3 
   353.50    0.4500    0.6224       3 
   353.47    0.4550    0.6243       3 
   353.43    0.4600    0.6263       3 
   353.39    0.4650    0.6282       3 
   353.35    0.4700    0.6302       3 
   353.31    0.4750    0.6322       3 
   353.27    0.4800    0.6343       3 
   353.24    0.4850    0.6363       3 
   353.20    0.4900    0.6384       3 
   353.16    0.4950    0.6405       3 
   353.12    0.5000    0.6426       3 
   353.09    0.5050    0.6447       3 
   353.05    0.5100    0.6469       3 
   353.02    0.5150    0.6490       3 
   352.98    0.5200    0.6512       3 
   352.95    0.5250    0.6534       3 
   352.91    0.5300    0.6557       3 
   352.88    0.5350    0.6579       3 
   352.84    0.5400    0.6602       3 
   352.81    0.5450    0.6625       3 
   352.77    0.5500    0.6649       3 
   352.74    0.5550    0.6672       3 
   352.71    0.5600    0.6696       3 
   352.68    0.5650    0.6720       3 
   352.64    0.5700    0.6744       3 
   352.61    0.5750    0.6769       3 
   352.58    0.5800    0.6794       3 
   352.55    0.5850    0.6819       3 
   352.52    0.5900    0.6844       3 
   352.49    0.5950    0.6869       3 
   352.46    0.6000    0.6895       3 
   352.43    0.6050    0.6921       3 
   352.40    0.6100    0.6947       3 
   352.37    0.6150    0.6974       3 
   352.34    0.6200    0.7001       3 
   352.32    0.6250    0.7027       3 
   352.29    0.6300    0.7055       3 
   352.26    0.6350    0.7082       3 
   352.23    0.6400    0.7110       3 
   352.21    0.6450    0.7138       3 
   352.18    0.6500    0.7167       3 
   352.16    0.6550    0.7196       3 
   352.13    0.6600    0.7225       3 
   352.11    0.6650    0.7254       3 
   352.08    0.6700    0.7284       3 
   352.05    0.6750    0.7314       3 
   352.03    0.6800    0.7344       3 
   352.01    0.6850    0.7374       3 
   351.99    0.6900    0.7405       3 
   351.96    0.6950    0.7436       3 
   351.94    0.7000    0.7467       2 
   351.92    0.7050    0.7499       2 
   351.90    0.7100    0.7531       2 
   351.88    0.7150    0.7563       2 
   351.86    0.7200    0.7596       2 
   351.84    0.7250    0.7628       2 
   351.82    0.7300    0.7662       2 
   351.81    0.7350    0.7695       2 
   351.79    0.7400    0.7729       2 
   351.77    0.7450    0.7763       2 
   351.76    0.7500    0.7797       2 
   351.74    0.7550    0.7832       2 
   351.72    0.7600    0.7868       2 
   351.71    0.7650    0.7903       2 
   351.69    0.7700    0.7939       2 
   351.68    0.7750    0.7975       2 
   351.66    0.7800    0.8011       2 
   351.65    0.7850    0.8048       2 
   351.64    0.7900    0.8085       2 
   351.63    0.7950    0.8123       2 
   351.61    0.8000    0.8161       2 
   351.61    0.8050    0.8199       2 
   351.60    0.8100    0.8237       2 
   351.58    0.8150    0.8277       2 
   351.57    0.8200    0.8316       2 
   351.57    0.8250    0.8355       2 
   351.56    0.8300    0.8395       2 
   351.55    0.8350    0.8436       2 
   351.54    0.8400    0.8477       2 
   351.54    0.8450    0.8518       2 
   351.53    0.8500    0.8559       2 
   351.53    0.8550    0.8601       2 
   351.52    0.8600    0.8644       2 
   351.52    0.8650    0.8686       2 
   351.52    0.8700    0.8729       2 
   351.51    0.8750    0.8773       2 
   351.51    0.8800    0.8817       2 
   351.51    0.8850    0.8861       2 
   351.51    0.8900    0.8906       2 
   351.50    0.8950    0.8951       2 
   351.51    0.9000    0.8996       2 
   351.51    0.9050    0.9042       2 
   351.51    0.9100    0.9089       2 
   351.51    0.9150    0.9136       2 
   351.52    0.9200    0.9183       2 
   351.52    0.9250    0.9230       2 
   351.52    0.9300    0.9278       2 
   351.52    0.9350    0.9327       2 
   351.53    0.9400    0.9376       2 
   351.53    0.9450    0.9425       1 
   351.54    0.9500    0.9475       2 
   351.55    0.9550    0.9526       2 
   351.55    0.9600    0.9577       2 
   351.56    0.9650    0.9628       2 
   351.57    0.9700    0.9680       2 
   351.58    0.9750    0.9732       2 
   351.60    0.9800    0.9784       2 
   351.61    0.9850    0.9838       2 
   351.61    0.9900    0.9891       2 
   351.63    0.9950    0.9945       2 
   351.64    1.0000    1.0000       2 
Press Enter to quit 

VLE_mixture.py

In [5]:
!type VLE_mixture.py
# VLE_mixture.py                        | 2016-9 ~ 2018-3  J. Chang
# Vapor-Liquid Equilibrium of Mixture                              
 
from math import *
import matplotlib.pyplot as plt

from pcsaft_mix import *

  

def Zpr( V, T, z ) :                    # returns compressibility factor of Peng-Robinson equation of state 

  global R, Tc, Pc, omega, ncP, Keps
  
  # V   molar volume  (m3/mol)     
  # unit of pressure  (bar)
        
  a = [0]*ncP         
  b = [0]*ncP
  
  for i in range(1,ncP) :
    kapa = 0.37464+1.54226*omega[i]-0.26992*omega[i]**2
    alpha = (1+kapa*( 1- sqrt(T/Tc[i]) ))**2
    a[i] = 0.45724 * R**2 * Tc[i]**2 / Pc[i] * alpha
    b[i] = 0.07780 * R * Tc[i] / Pc[i]
    
  amix = 0
  bmix = 0
  for i in range(1,ncP) :
    bmix += z[i] * b[i]
    for j in range(1,ncP) :
      if i == j : aij = a[i]
      else      : aij = sqrt( a[i]*a[j]) * (1 - Keps[i][j])
      amix += z[i]*z[j]*aij
  
  Z = V/( V-bmix ) - amix/(R*T) * V / ( V*(V+bmix) + bmix*(V-bmix) )
  return  Z


   
def phi_pr( V, T, z ) :                 # returns fugacity coefficients
 
  global R, Tc, Pc, omega, ncP, Keps
           
  a = [0]*ncP         
  b = [0]*ncP
   
  for i in range(1,ncP) :
    kapa = 0.37464+1.54226*omega[i]-0.26992*omega[i]**2
    alpha = (1+kapa*( 1- sqrt(T/Tc[i]) ))**2
    a[i] = 0.45724 * R**2 * Tc[i]**2 / Pc[i] * alpha
    b[i] = 0.07780 * R * Tc[i] / Pc[i]
    
  amix = 0
  bmix = 0
  za = [0]*ncP    
  
  for i in range(1,ncP) :
    bmix += z[i] * b[i]
    for j in range(1,ncP) :
      if i == j : aij = a[i]
      else      : aij = sqrt( a[i]*a[j]) * (1 - Keps[i][j])
      amix  += z[i]*z[j]*aij
      za[i] += z[j]*aij
      
  Z = V/( V-bmix ) - amix/(R*T) * V / ( V*(V+bmix) + bmix*(V-bmix) )
  
  root2 = sqrt(2)
  phi = [0]*ncP
  
  for i in range(1,ncP) :
    lnphi = b[i]/bmix *(Z - 1) - log( Z - bmix*Z/V ) - amix / (2*root2*bmix*R*T)        \
    * ( 2*za[i]/amix - b[i]/bmix ) * log( (1+(1+root2)*bmix/V) / (1+(1-root2)*bmix/V) ) 
    phi[i] = exp( lnphi ) 
    
  return  phi
  


def PR_ab( T, z ) :                     # returns coefficients a and b of PR equation of state
  
  # used to find analytic solution of PR eos
  global R, Tc, Pc, omega, ncP, Keps
  a = [0]*ncP         
  b = [0]*ncP
  for i in range(1,ncP) :
    kapa = 0.37464+1.54226*omega[i]-0.26992*omega[i]**2
    alpha = (1+kapa*( 1- sqrt(T/Tc[i]) ))**2
    a[i] = 0.45724 * R**2 * Tc[i]**2 / Pc[i] * alpha
    b[i] = 0.07780 * R * Tc[i] / Pc[i]
  amix = 0
  bmix = 0
  for i in range(1,ncP) :
    bmix += z[i] * b[i]
    for j in range(1,ncP) :
      if i == j : aij = a[i]
      else      : aij = sqrt( a[i]*a[j]) * (1 - Keps[i][j])
      amix += z[i]*z[j]*aij  
  return  amix, bmix



def Fugacity( V, T, z, P ) :            # calls fugacity routine of equation of state, and return fugacity coefficients
  
  global eos, ncP
  global m, sig, epsk, kAB, eAB, nc, Keps, ncas, dipol, xp

  # fugacity coefficient 
  if   eos == 'PR' : 
    phi = phi_pr( V, T, z ) 
  elif eos == 'PC-SAFT' : 
    phi = pcsaft( 'Phi', V, T, z, m, sig, epsk, kAB, eAB, nc, Keps, ncas, dipol, xp ) 

  # fugacity     
  fug = [0]*ncP 
  for i in range(1,ncP) :  fug[i] = phi[i] * z[i] * P
  
  return  phi, fug
  


def Peos( V, T, z ) :                   # calls equation of state, and returns  Peos(V) 
  
  global eos, R 
  global m, sig, epsk, kAB, eAB, nc, Keps, ncas, dipol, xp
  if   eos == 'PR'      :  Z = Zpr( V, T, z )
  elif eos == 'PC-SAFT' :  Z = pcsaft( 'Z', V, T, z, m, sig, epsk, kAB, eAB, nc, Keps, ncas, dipol, xp )
  Peos = (R*T/V) * Z 
  return  Peos, Z 
  

  
def Peos_P( V, P, T, z ) :              # calls equation of state, and returns  Peos(V) - P
  
  global eos, R 
  global m, sig, epsk, kAB, eAB, nc, Keps, ncas, dipol, xp
  if   eos == 'PR'      :  Z = Zpr( V, T, z )
  elif eos == 'PC-SAFT' :  Z = pcsaft( 'Z', V, T, z, m, sig, epsk, kAB, eAB, nc, Keps, ncas, dipol, xp )
  Peos = (R*T/V) * Z 
  return  Peos - P
      


def newton__Peos_P( V0, P, T, z ) :     # returns molar volume that satisfies  Peos(V) - P = 0  by Newton method
  
  # V0  is initial guess of molar volume
  # returns None when convergence error occurs
  
  MAXIT = 100                               # maximum 100 iterations
  relax = 1. # 0.3

  #VV = [ V0 ]                              # Keep the record
  V = V0
  dV = V * 1e-5
  
  for i in range(MAXIT) :                    
    fx = Peos_P( V, P, T, z )
    if abs( fx/P ) < 1e-5 :  break                    # converged
    dfdV = ( Peos_P( V+dV, P, T, z ) - fx ) / dV 
    if abs( dfdV * V/P ) < 1e-5  :                    # convergence error
      #print( VV )                                    # check how bad convergence records are 
      return None 
    delV = - fx / dfdV
    #if abs(delV/V) > 0.1  : relax *= 0.5             # when large change occurs (>10%), reduce relax
    V += delV * relax
    #VV.append( V )
    
  return  V
  
  


def Find_vol_PR( P, T, z, phase ) :         # returns molar volumes of PR equation of state using analytical formula 
  
  global R
  pi = 3.1415926536
    
  a, b = PR_ab( T, z )
       
  # using math formula for cubic equation  x^3 + a2 * x^2 + a1 * x + a0 = 0

  a2 = b-R*T/P 
  a1 = -3*b*b + (a-2*b*R*T)/P  
  a0 = b*b*b + (b*b*R*T-a*b)/P 
  
  q = ( 3*a1 - a2*a2 ) / 9 
  r = ( 9*a1*a2 - 27*a0 - 2*a2*a2*a2 ) / 54 
  D = q*q*q + r*r 

  if D < 0 :  # three real roots, q < 0  
  
    # The following formula are for D < 0, there exist three real roots
    th = acos( r / sqrt(-q*q*q) ) 
    V1 = 2*sqrt(-q) * cos( th/3 ) - a2 / 3 
    V2 = 2*sqrt(-q) * cos( th/3 + 2/3*pi ) - a2 / 3 
    V3 = 2*sqrt(-q) * cos( th/3 + 4/3*pi ) - a2 / 3 

    # vapor volume is the largest, and liquid volume is the smallest one
    Vvap = V1 
    if V2 > Vvap : Vvap = V2 
    if V3 > Vvap : Vvap = V3 
    Vliq = V1 
    if V2 < Vliq : Vliq = V2 
    if V3 < Vliq : Vliq = V3 

  else :   # D > 0, single real root
    
    s = r + sqrt(D) 
    if s > 0 : s = s**(1/3)       # s arg > 0
    else     : s = -(-s)**(1/3)   # s arg < 0 
    t = r - sqrt(D) 
    if t > 0 : t = t**(1/3)       # t arg > 0
    else     : t = -(-t)**(1/3)   #  t arg < 0

    Vvap = -a2 / 3 + s + t         # one real root, two complex roots not physical
    Vliq = Vvap

    
  if phase == 'vapor' :
    Vol = Vvap
  else :
    Vol = Vliq
  
  return  Vol

  

def Find_vol( P, T, z, phase ) :            # returns molar volumes of general equations of state
   
  global eos, R, Tc, Pc, ncP, scalVl0, SavedResult
  
  if phase == 'vapor' and SavedResult[ 'existVvap' ] == False :    # for a single calculation or the first calculation in scanning analysis
  
    # analytic solution of PR eos is used as initial guess 
    Vol = Find_vol_PR( P, T, z, phase )    
    if eos == 'PR' : return Vol       # solution already at hand   # return and exit
    
    # find numerical solution for general equation of state
    Vol = newton__Peos_P( Vol, P, T, z )
    
    # SavedResult[ 'existVvap' ] = True
    # SavedResult[ 'Vvap' ] = Vol
      
  elif phase == 'vapor' and SavedResult[ 'existVvap' ] == True :   
  
    # when previously saved results are available, use it as initial guess
    Vol = SavedResult[ 'Vvap' ]
    Vol = newton__Peos_P( Vol, P, T, z )
    
    # SavedResult[ 'Vvap' ] = Vol


    
  elif phase == 'liquid' and SavedResult[ 'existVliq' ] == False :    # for a single calculation or the first calculation in scanning analysis
  
    # analytic solution of PR eos is used as initial guess 
    Vol = Find_vol_PR( P, T, z, phase )    
    if eos == 'PR' : return Vol       # solution already at hand   # return and exit
    
    # find numerical solution for general equation of state
    Vol = newton__Peos_P( Vol*scalVl0, P, T, z )  # use different guess value of liquid volume than PR eos using scaling fraction
    
    # SavedResult[ 'existVliq' ] = True
    # SavedResult[ 'Vliq' ] = Vol
      
  elif phase == 'liquid' and SavedResult[ 'existVliq' ] == True :   
  
    # when previously saved results are available, use it as initial guess
    Vol = SavedResult[ 'Vliq' ]
    Vol = newton__Peos_P( Vol, P, T, z )
    
    # SavedResult[ 'Vliq' ] = Vol

    
  return  Vol

  


def BubbleP_ini( T, x ) :               # returns initial guesses of P and y
   
  global Tc, Pc, Tb, ncP
  
  Ps = [0]*ncP
  
  # Guess bubble pressure from vapor pressure and x's
  # Estimated vapor pressure from Tc, Pc and Tb   by using  ln(Ps) = A - B / T
  # print()
  # print("Estimated (hypothetical) vapor pressures from Tc, Pc, Tb " )
  
  P  = 0.
  for i in range(1,ncP) :
    B = ( log(Pc[i]) - log(1.013) ) / ( 1/Tb[i] - 1/Tc[i] )
    A = log(1.013) + B / Tb[i]
    Ps[i] = exp( A - B / T )
    # print(" Component %d,  Psat = %9.3f (bar) " %( i, Ps[i] ) )
    P += x[i] * Ps[i]                                # assuming ideal solution

  y  = [0]*ncP
  for i in range(1,ncP) : y[i] = x[i] * Ps[i] / P   # assuming ideal solution

  # initial guesses
#  print(" P  = %9.3f (bar)      <-- guess value from estimated vapor pressures" % P )
#  print(" y  = ", end=" " )
#  for i in range(1,ncP) :
#    print("%8.5f" % y[i], end=" ") 
#  print("  <-- initial guess \n")
 
  return  P, y  




def BubbleP( T, x, P, y ) :             #  P, y are initial guesses 
  
  global ncP
  Maxit = 3000
  
  for it in range(Maxit) :
    
    # Get fugacity of liquid and volume
    Vliq = Find_vol( P, T, x, 'liquid' )    
    phiL, fL = Fugacity( Vliq, T, x, P )
    
    # Get fugacity of vapor and volume     
    Vvap = Find_vol( P, T, y, 'vapor' )     
    phiV, fV = Fugacity( Vvap, T, y, P )

    # Adjust y composition 
    ynew = [0]*ncP
    sumy = 0
    for i in range(1,ncP) :   
      ynew[i] = x[i] * ( phiL[i] / phiV[i] )
      # ynew[i] = y[i] * ( fL[i] / fV[i] )   # Division by zero for pure substance
      sumy += ynew[i]
    
    # Adjust P
    Pnew = P * sumy
  
    # Convergence check for fugacity equality and constancy of pressure
    Converged = True
    for i in range(1,ncP) :
      if abs( fL[i] - fV[i] ) > 1e-5 : Converged = False
    if abs( Pnew - P ) < 1e-5  and  Converged :  break   
  
    # Update y and P
    P = Pnew
    y = ynew
    
    # Error check for single phase
    if abs(Vvap-Vliq)/Vvap < 1e-5 :
      it = None
      return  P, y, Vliq, Vvap, it

    # Print running results    
#    print(" iter = %2d   P = %8.3f " %( it, P ), end=" " )
#    print(" y = ", end=" " )
#    for i in range(1,ncP) : print("%8.5f" %y[i], end=" ") 
#    print()
  
  return  P, y, Vliq, Vvap, it




def DewP_ini( T, y ) :                  # returns initial guesses of P and x
    
  global Tc, Pc, Tb, ncP
  
  # Guess bubble pressure from vapor pressure and x's
  # Estimated vapor pressure from Tc, Pc and Tb   by using  ln(Ps) = A - B / T
#  print("Estimated (hypothetical) vapor pressures from Tc, Pc, Tb "  )

  Ps = [0]*ncP
  sum = 0.
  for i in range(1,ncP) :
    B = ( log(Pc[i]) - log(1.013) ) / ( 1/Tb[i] - 1/Tc[i] )
    A = log(1.013) + B / Tb[i]
    Ps[i] = exp( A - B / T )
#    print(" Component %d,  Psat = %9.3f (bar) "       %( i, Ps[i] ) )
    sum += y[i] / Ps[i]                               # assuming ideal solution
  
  P = 1 / sum
  
  x  = [0]*ncP
  for i in range(1,ncP) :  x[i] = y[i] * P / Ps[i]    # assuming ideal solution

  return  P, x  




def DewP( T, y, P, x ) :                #  P, x are initial guesses 
  
  global ncP
  
  Maxit = 3000
  
  for it in range(Maxit) :
    
    # Get fugacity of vapor      
    Vvap = Find_vol( P, T, y, 'vapor' )
    phiV, fV = Fugacity( Vvap, T, y, P )
     
    # Get fugacity of liquid 
    Vliq = Find_vol( P, T, x, 'liquid' )
    phiL, fL = Fugacity( Vliq, T, x, P )
     
    # Adjust x composition 
    xnew = [0]*ncP
    sumx = 0
    for i in range(1,ncP) :   
      xnew[i] = y[i] * ( phiV[i] / phiL[i] )
      sumx += xnew[i]
    
    # Adjust P
    Pnew = P / sumx  
  
    # Convergence check for fugacity equality and constancy of pressure
    Converged = True
    for i in range(1,ncP) :
      if abs( fL[i] - fV[i] ) > 1e-5 : Converged = False
    if abs( Pnew - P ) < 1e-5  and  Converged :  break   
  
    # Update x and P
    P = Pnew
    x = xnew

    # Error check for single phase
    if abs(Vvap-Vliq)/Vvap < 1e-5 :
      it = None
      return  P, x, Vliq, Vvap, it
      
  
  return  P, x, Vliq, Vvap, it




def BubbleT_ini( P, x ) :               # returns initial guesses of T and y
    
  global Tc, Pc, Tb, ncP, Bp
  
  Ap = [0]*ncP
  Bp = [0]*ncP
  Ps = [0]*ncP
  
  # Guess bubble temperature from vapor pressure and x's
  # Estimated boiling temperature from Tc, Pc and Tb   by using  ln(Ps) = A - B / T
#  print("\n")
#  print("Estimated boiling temperatures from Tc, Pc, Tb "  )
  
  T = 0.
  for i in range(1,ncP) :
    B = ( log(Pc[i]) - log(1.013) ) / ( 1/Tb[i] - 1/Tc[i] )
    A = log(1.013) + B / Tb[i]
    Tbp = B / ( A - log(P) )
    Ap[i] = A
    Bp[i] = B
#    print(" Component %d,  Tb = %9.2f (K) " %( i, Tbp ))
    T += x[i] * Tbp                                # mole fraction average
                              
  # initial guesses
#  print(" T  = %9.2f (K)      <-- guess value as mole fraction average" % T )
  
  # Estimated vapor pressure from Tc, Pc and Tb   by using  ln(Ps) = A - B / T
#  print("Estimated (hypothetical) vapor pressures at this T " )
  
  y  = [0]*ncP
  for i in range(1,ncP) :
    Ps[i] = exp( Ap[i] - Bp[i] / T )
#    print(" Component %d,  Psat = %9.3f (bar) " %( i, Ps[i] ))
    y[i] = x[i] * Ps[i] / P                          # assuming ideal solution

#  print(" y  = ", end=" " )
#  for i in range(1,ncP) : print("%8.5f" % y[i], end=" ") 
#  print("  <-- initial guess \n")
    
  return  T, y  




def BubbleT( P, x, T, y ) :             #  T, y are initial guesses 
  
  global ncP, Bp

  B = 0                                       # parameter to adjust temperature
  for i in range(1,ncP) :  B += x[i]*Bp[i]    # mole fraction average  
  
  Maxit = 3000
  
  for it in range(Maxit) :
    
    # Get fugacity of liquid 
    Vliq = Find_vol( P, T, x, 'liquid' )
    phiL, fL = Fugacity( Vliq, T, x, P )
    
    # Get fugacity of vapor      
    Vvap = Find_vol( P, T, y, 'vapor' )
    phiV, fV = Fugacity( Vvap, T, y, P )
      
    # Adjust y composition 
    ynew = [0]*ncP
    sumy = 0
    for i in range(1,ncP) :   
      ynew[i] = x[i] * ( phiL[i] / phiV[i] )
      sumy += ynew[i]
    
    # Adjust T
    # ln(Ps) = A - B/T  ->  d ln(Ps) = B dT/T^2
    # Pnew = P * sumy   ->  - ln sumy = B/T^2 dT   
                                
    delT = -log(sumy) * T**2 / B  
  
    # Convergence check for fugacity equality and constancy of pressure
    Converged = True
    for i in range(1,ncP) :
      if abs( fL[i] - fV[i] ) > 1e-5 : Converged = False
    if abs( delT ) < 1e-3  and  Converged :  break   
  
    # Update y and T
    T = T + delT
    y = ynew
    
    # Error check for single phase
    if abs(Vvap-Vliq)/Vvap < 1e-5 :
      it = None
      return  T, y, Vliq, Vvap, it

    # Print running results    
#    print(" iter = %2d   T = %8.3f " %( it, T ), end=" " )
#    print(" y = ", end=" " )
#    for i in range(1,ncP) :
#      print("%8.5f" % y[i], end=" ") 
#    print()
  
  return  T, y, Vliq, Vvap, it




def DewT_ini( P, y ) :                  # returns initial guesses of T and x
   
  global Tc, Pc, Tb, ncP, Bp
  
  Ap = [0]*ncP
  Bp = [0]*ncP
  Ps = [0]*ncP
  
  # Guess bubble temperature from vapor pressure and x's
  # Estimated boiling temperature from Tc, Pc and Tb   by using  ln(Ps) = A - B / T
#  print("\n")
#  print("Estimated boiling temperatures from Tc, Pc, Tb "  )
  
  T = 0.
  for i in range(1,ncP) :
    B = ( log(Pc[i]) - log(1.013) ) / ( 1/Tb[i] - 1/Tc[i] )
    A = log(1.013) + B / Tb[i]
    Tbp = B / ( A - log(P) )
    Ap[i] = A
    Bp[i] = B
#    print(" Component %d,  Tb = %9.2f (K) " %( i, Tbp ))
    T += y[i] * Tbp                                # mole fraction average
                              
  # initial guesses
#  print(" T  = %9.2f (K)      <-- guess value as mole fraction average" % T )
  
  # Estimated vapor pressure from Tc, Pc and Tb   by using  ln(Ps) = A - B / T
#  print("Estimated (hypothetical) vapor pressures at this T " )
  
  x  = [0]*ncP
  for i in range(1,ncP) :
    Ps[i] = exp( Ap[i] - Bp[i] / T )
#    print(" Component %d,  Psat = %9.3f (bar) " %( i, Ps[i] ))
    x[i] = y[i] * P / Ps[i]                          # assuming ideal solution

#  print(" x  = ", end=" " )
#  for i in range(1,ncP) : print("%8.5f" % x[i], end=" ") 
#  print("  <-- initial guess \n")
    
  return  T, x




def DewT( P, y, T, x ) :                # T, x are initial guesses 
  
  global ncP, Bp

  B = 0                                       # parameter to adjust temperature
  for i in range(1,ncP) :  B += y[i]*Bp[i]    # mole fraction average  
  
  Maxit = 3000
  
  for it in range(Maxit) :
    
    # Get fugacity of vapor      
    Vvap = Find_vol( P, T, y, 'vapor' )
    phiV, fV = Fugacity( Vvap, T, y, P )
      
    # Get fugacity of liquid 
    Vliq = Find_vol( P, T, x, 'liquid' )
    phiL, fL = Fugacity( Vliq, T, x, P )
    
    # Adjust x composition 
    xnew = [0]*ncP
    sumx = 0
    for i in range(1,ncP) :   
      xnew[i] = y[i] * ( phiV[i] / phiL[i] )
      sumx += xnew[i]
    
    # Adjust T
    # ln(Ps) = A - B/T  ->  d ln(Ps) = B dT/T^2
    # Pnew = P / sumx   ->  ln sumx = B/T^2 dT   
                                
    delT = log(sumx) * T**2 / B 
  
    # Convergence check for fugacity equality and constancy of pressure
    Converged = True
    for i in range(1,ncP) :
      if abs( fL[i] - fV[i] ) > 1e-5 : Converged = False
    if abs( delT ) < 1e-3  and  Converged :  break   
  
    # Update x and T
    T = T + delT
    x = xnew
    
    # Error check for single phase
    if abs(Vvap-Vliq)/Vvap < 1e-5 :
      it = None
      return  T, x, Vliq, Vvap, it

    # Print running results    
#    print(" iter = %2d   T = %8.3f " %( it, T ), end=" " )
#    print(" x = ", end=" " )
#    for i in range(1,ncP) :
#      print("%8.5f" % x[i], end=" ") 
#    print()
  
  return  T, x, Vliq, Vvap, it



   
def Flash( P, T, z, x, y, v ) :         # x, y, v are initial guesses

  global ncP
  
  Maxit = 3000
  K = [0]*ncP 
 
  for it in range(Maxit) :

    # Get fugacity of liquid 
    Vliq = Find_vol( P, T, x, 'liquid' )
    phiL, fL = Fugacity( Vliq, T, x, P )
    
    # Get fugacity of vapor      
    Vvap = Find_vol( P, T, y, 'vapor' )
    phiV, fV = Fugacity( Vvap, T, y, P )
   
    # K value
    for i in range(1,ncP) :  K[i] = phiL[i] / phiV[i] 
    
    # Find vapor fraction v
    vold = v 
    
    for iv in range(100) :
      F = 0
      dFdv = 0
      for i in range(1,ncP) :  
        F += z[i] * (K[i]-1) / ( 1 + v * (K[i]-1) )   
        dFdv += - z[i] * (K[i]-1)**2 / ( 1 + v * (K[i]-1) )**2
      delv = - F / dFdv
      if abs(delv) < 1e-5 :  break 
      v += delv
    
    # Convergence check for fugacity equality and constancy of v
    Converged = True
    for i in range(1,ncP) :
      if abs( fL[i] - fV[i] ) > 1e-5 : Converged = False
    if abs( v - vold ) < 1e-5  and  Converged :  break 

    # update x, y
    for i in range(1,ncP) :  
      x[i] = z[i] / ( 1 + v * (K[i]-1) )
      y[i] = K[i] * x[i]

      
    # Print running results    
#    print(" iter = %2d   v = %8.5f " %( it, v ), end=" " )
#    print(" x, y = ", end=" " )
#    for i in range(1,ncP) : print("%8.4f  %8.4f" %(x[i],y[i]), end=" ") 
#    print()
    
  return x, y, K, Vliq, Vvap, v, it  
  

  
  

def Read_data__initialize() :
  
  global eos, R, component, Mw, Tc, Pc, omega, Tb, nc, ncP, Keps, scalVl0, plot_title, SavedResult
  global Texp, Pexp, Xexp, Yexp, isTPxySet
  global m, sig, epsk, kAB, eAB, ncas, dipol, xp
  
  
  R = 8.314e-5                # (m3.bar/mol/K)
   
  # Read data
  f = open("Data.txt",'r')
  mixture = f.readline()
  f.readline()                # skip comment line
  eos = int( f.readline() )
  f.readline()  
  nc = int( f.readline() )    # number of components
  f.readline()                # --------------------

  print( mixture )
  if   eos == 0 :  eos = 'PR'
  elif eos == 1 :  eos = 'PC-SAFT'
  print("using %s eos ... \n" % eos )
  
  ncP = nc+1                  # used for set the size of list   
  component = [0]*ncP
  Mw = [0]*ncP
  Tc = [0]*ncP
  Pc = [0]*ncP
  omega = [0]*ncP
  Tb = [0]*ncP  
  m  = [0]*ncP
  sig  = [0]*ncP
  epsk = [0]*ncP
  kAB  = [ [0]*ncP for i in range(ncP) ]
  eAB  = [ [0]*ncP for i in range(ncP) ]
  dipol= [0]*ncP
  xp   = [0]*ncP
  
  for i in range(1,ncP) :
    component[i] = f.readline().replace("\n","")
    f.readline()
    Mw[i], Tc[i], Pc[i], omega[i], Tb[i] = map( float, f.readline().split() )
    f.readline()
    pcsaftDATA = f.readline().split()
    if len(pcsaftDATA)   == 5 :
      m[i], sig[i], epsk[i], kAB[i][i], eAB[i][i] = map( float, pcsaftDATA )
    elif len(pcsaftDATA) == 7 :                                       # polar component
      m[i], sig[i], epsk[i], kAB[i][i], eAB[i][i], dipol[i], xp[i] = map( float, pcsaftDATA )
        
    f.readline()               # -----------------------------------------------------
    # Echo Property
    print("Component %d  =  %s "  %( i, component[i] ) )
    print(" Mw (g/mol)  Tc (K)   Pc (bar)   omega     Tb (K) ")
    print(" %8.3f  %8.2f   %8.3f %7.3f   %8.2f"     %( Mw[i], Tc[i], Pc[i], omega[i], Tb[i] ) )
    print("     m       sig (A)  epsk (K)     kAB      eAB (K)  dipole (D)  xp ")
    print(" %8.4f  %8.4f  %8.2f %11.6f %8.2f %8.3f  %9.5f \n"          
          %( m[i], sig[i], epsk[i], kAB[i][i], eAB[i][i], dipol[i], xp[i] ) )
    # check diameter in (m)  
    if sig[i] > 1e-5 :  sig[i] *= 1e-10        # convert (A) to (m)
  
  
  # Binary parameters in the order of k12, k13, .. k1N, k23, k24, .. 
  f.readline()
  Kij = list( map( float, f.readline().split() ) )

  # Setup kij
  # make 2d Zero array for Keps[i][j]        
  Keps = [ [0]*ncP for i in range(ncP) ]
  
  for i in range(1,ncP) :
    for j in range(i+1,ncP) :
      Keps[i][j] = Kij.pop(0)        # pop from the first
      Keps[j][i] = Keps[i][j]
  
  print("Kij's are ")    
  for i in range(1,ncP) :
    for j in range(1,ncP) :    
      print("%9.4f" % Keps[i][j], end=" ")
    print('')
  print("")  


  # fill cross association by Walbach-Sandler combining rules
  for i in range(1,ncP) :
    for j in range(i+1,ncP) :
      eAB[i][j] = (eAB[i][i]+eAB[j][j])/2
      eAB[j][i] = eAB[i][j]
      kAB[i][j] = sqrt( kAB[i][i]*kAB[j][j] ) * ( sqrt(sig[i]*sig[j]) * 2/(sig[i]+sig[j]) )**3 
      kAB[j][i] = kAB[i][j]
      
  
  # Read cross association override Walbach-Sandler combining rules (if provided)
  
  print( "cross association override in PC-SAFT \n    i     j    kAiBj     eAiBj (K)" )
  
  f.readline()
  
  novride = 0
  
  while True :
    try :   
      args = f.readline().split()                      #  i   j    kAiBj     eAiBj
      i = int( args[0] )
      j = int( args[1] )
      kAB[i][j] = float( args[2] )
      eAB[i][j] = float( args[3] )
      
      kAB[j][i] = kAB[i][j]       
      eAB[j][i] = eAB[i][j]
      print("%5d %5d %11.6f %8.2f " %( i, j, kAB[i][j], eAB[i][j] ) )
      novride += 1
      
    except :
      break
  
  print("The number of overrides is = %d \n" % novride )
  
  
  # check the number of associating components including self- and induced- associations
  
  if eos == 'PC-SAFT' :
      
    print("checking associating components in PC-SAFT ..." )
    
    ncas = 0                                 # number of associating components
    
    for i in range(1,ncP) :
      for j in range(1,ncP) :
        if kAB[i][j] > 1e-10 :
          print("  component %d  is associating  " %i ) 
          ncas += 1
          break                               # stop checking and examine next component
     
    print("The number of associating components = %d \n" % ncas )
    
  
  
  # read T, P, compositions
  
  z = [0]*ncP

  f.readline()                   
  TypCal = int( f.readline() )    # 0 (Z), 1 (phi), 2 (BubbleP), 3 (DewP),4 (BubbleT), 5 (DewT), 6 (Flash)
                                  # 
  f.readline()  
  args = list( map( float, f.readline().split() ))
    
  T = args.pop(0)
  P = args.pop(0)
  for i in range(1,ncP) :
    z[i] = args.pop(0)
    
  # Echo type of calculation
  
  if TypCal == 2 :            # 2 (BubbleP)
    print("Bubble P calculation at T = %7.2f (K)  %7.2f (C) \n" % ( T, T-273.15 ) )
    plot_title = "Bubble P calculation at T  = %7.2f (K) \n" % T    \
               + component[1] + " and " + component[2] + " \n"      # for binary mixture
  elif TypCal == 3 :            # 3 (DewP)
    print("  Dew  P calculation at T = %7.2f (K)  %7.2f (C) \n" % ( T, T-273.15 ) )
    plot_title = "  Dew  P calculation at T = %7.2f (K) \n" % T    \
               + component[1] + " and " + component[2] + " \n"      # for binary mixture
  elif TypCal == 4 :            # 4 (BubbleT)
    print("Bubble T calculation at P = %8.3f (bar) \n" % P )
    plot_title = "Bubble T calculation at P = %8.3f (bar) \n" % P  \
               + component[1] + " and " + component[2] + " \n"      # for binary mixture
  elif TypCal == 5 :            # 5 (DewT)
    print("  Dew  T calculation at P = %8.3f (bar) \n" % P )
    plot_title = "  Dew  T calculation at P = %8.3f (bar) \n" % P  \
               + component[1] + " and " + component[2] + " \n"      # for binary mixture
  elif TypCal == 6 :            # 6 (Flash)
    print("  Flash  calculation at T = %7.2f (K) and P = %8.3f (bar) \n " %( T, P ) )

    
  # scaling fraction for initial liquid volume taken from PR eos
  
  f.readline()
  scalVl0 = float( f.readline() )          # 1.0  # 0.95  # 0.9  
  # scalVl0 = float( input("enter scaling fraction for initial liquid volume (e.g. 0.95) = ") )
  print("scaling fraction for initial liquid volume = %7.2f \n" % scalVl0 )  


  # read experimental data for binary mixture if provided
  
  Texp = []
  Pexp = []
  Xexp = []
  Yexp = []
  
  expdataheader = f.readline()
  if expdataheader.find("y") > -1 :    # if header contains "y" for gas composition 
    isTPxySet = True 
    isTPx_Set = False 
    nexp = 4
  else :
    isTPxySet = False
    isTPx_Set = True
    nexp = 3

  print("-- Experimental data -- \n     T (K)    P (bar)    x1        y1 ")   
            
  while True :
    try :   
      line = f.readline().replace("\t"," ")    # convert tab into space
      #print(line)
      args = line.split()                      # T, P, x, y
      for i in range(nexp) :
        args[i] = float( args[i] )             # T, P, x, y  into numeric data
        
      if expdataheader.find("(C)")   > -1 :  args[0] += 273.15         # C -> K
      if expdataheader.find("(F)")   > -1 :  args[0] = (args[0]-32)/1.8 + 273.15      # F -> K      
      if expdataheader.find("(mmHg)")> -1 :  args[1] *= 1.013 / 760    # mmHg -> bar
      if expdataheader.find("(psi)") > -1 :  args[1] *= 1.013 / 14.7   # psi  -> bar
      if expdataheader.find("(MPa)") > -1 :  args[1] *= 10.0           # MPa  -> bar
      if expdataheader.find("(kPa)") > -1 :  args[1] /= 100.0          # kPa  -> bar
      
      if isTPxySet : print("  %7.2f  %8.3f  %8.4f  %8.4f " %( args[0], args[1], args[2], args[3] ) )
      if isTPx_Set : print("  %7.2f  %8.3f  %8.4f        " %( args[0], args[1], args[2]          ) )
      
      Texp.append( args[0] ) 
      Pexp.append( args[1] )
      Xexp.append( args[2] )
      if isTPxySet : Yexp.append( args[3] )
      #input("Press Enter to do .. ")      
    except :
      break
  
    
  # saving result for successive calculations
  SavedResult = { } 
  SavedResult[ 'existVvap' ] = False
  SavedResult[ 'existVliq' ] = False
 
  return TypCal, T, P, z
  





#--[ Main program ]-----------------------------------------------------


global eos, R, component, Mw, Tc, Pc, omega, Tb, nc, ncP, Keps, plot_title, SavedResult
global Texp, Pexp, Xexp, Yexp, isTPxySet
global m, sig, epsk, kAB, eAB, ncas, dipol, xp

# unit conversion
m3TOcm3 = 1e6

print("=========================================")
print("  Vapor-Liquid Equilibrium of Mixture    ")
print("=========================================")

TypCal, T, P, z  = Read_data__initialize()

input("press Enter to proceed ")
print()


if  TypCal == -1 :            # test calculation for pressure

  V = float( input("enter V (cm3/mol)? ") )

  print("<< Calculation for given T, V, z's >> \n "  )
  print(" T  = %10.3f (K) "   % T )
  print(" V  = %10.4f (cm3/mol) " % V )
  print(" z's = ", end=" " )
  for i in range(1,ncP) :
    print("%8.5f" %z[i], end=" ")   
  print("\n")

  V = V * 1e-6
  P, Z = Peos( V, T, z )
  print(" P  = %10.4f (bar) " % P )
  print(" Z  = %10.4f       " % Z )
  
  
elif TypCal == 0 :            # 0 (Z)


  print("<< Calculation for given T, P, z's >> \n "  )
  # T, P, z's 
  print(" T  = %10.3f (K) "   % T )
  print(" P  = %10.4f (bar) " % P )
  print(" z's = ", end=" " )
  for i in range(1,ncP) :
    print("%8.5f" %z[i], end=" ")   
  print("\n")
  
  Vvap = Find_vol( P, T, z, 'vapor' )
  Vliq = Find_vol( P, T, z, 'liquid' )
  
  Zvap = P * Vvap / ( R * T )
  Zliq = P * Vliq / ( R * T )
  print(" compressibility factor       molar volume (cm^3/mol) ")
  print("    vapor     liquid             vapor       liquid   ")
  print("  %8.4f  %8.4f         %10.2f  %10.2f " % (Zvap, Zliq, Vvap*m3TOcm3, Vliq *m3TOcm3 ) )

 
elif TypCal == 1 :            # 1 (phi)


  print("<< Calculation for given T, P, z's >> \n "  )
  # T, P, z's 
  print(" T  = %10.2f (K) "   % T )
  print(" P  = %10.4f (bar) " % P )
  print(" z's = ", end=" " )
  for i in range(1,ncP) :
    print("%8.5f" %z[i], end=" ")   
  print("\n")
  
  Vvap = Find_vol( P, T, z, 'vapor' )
  Vliq = Find_vol( P, T, z, 'liquid' )
  
  phiV, fV = Fugacity( Vvap, T, z, P )
  phiL, fL = Fugacity( Vliq, T, z, P )
  
  print("           Vapor phase                Liquid phase ")
  print(" Comp  fugacity (bar)   phi       fugacity (bar)    phi " )  
  
  for i in range(1,ncP) :
    print("   %d   %10.4f %10.4f      %10.4f    %10.4f"    \
        %( i, fV[i], phiV[i], fL[i], phiL[i] ) )


elif TypCal == 2 :            # 2 (BubbleP)

  print("<<  Pxy - Bubble point pressure calculation for given T, x  >> \n "  )
  print(" T  = %9.2f (K) "   % T )
  
  x = z[:]  
  
  WholeRange = False           # single point calculation
  if nc == 2 and z[1]+z[2] < 1e-10 : WholeRange = True    # for example, zero compositions are given
  
  if not WholeRange :
    
    print(" x  = ", end=" " )
    for i in range(1,ncP) :
      print("%8.4f" %x[i], end=" ") 
    print()
  
    P, y = BubbleP_ini( T, x )
    P, y, Vliq, Vvap, it = BubbleP( T, x, P, y )
  
    print("\n Bubble point pressure  = %10.5f (bar) \n" % P )
    print(" comp     liquid     vapor       K " )
    for i in range(1,ncP) :
      print("  %d     %8.4f   %8.4f   %8.4f " %( i, x[i], y[i], y[i]/x[i] ) ) 
    print()    
    
    Zvap = P * Vvap / ( R * T )
    Zliq = P * Vliq / ( R * T )
    print("  Z     %8.4f   %8.4f \n" %( Zliq, Zvap ) )    
    print(" Number of iterations = %d " % it )


  else :    # WholeRange from x = 0 to 1    only for binary mixture 

    # list for plot
    PP = []
    XX = []
    YY = []    
    
    nstep = 200
    X = [ i/nstep for i in range(nstep+1) ]
    
    # initial guess at x1 = 0
    x[1] = X[0]
    x[2] = 1 - x[1]
    P, y = BubbleP_ini( T, x )

    print()
    print("  P (bar)    x1        y1       iteration")
    print("-----------------------------------------")
    
    for x[1] in X :
    
      x[2] = 1 - x[1]
      # P, y, Vliq, Vvap, it = BubbleP( T, x, P, y )

      try :  P, y, Vliq, Vvap, it = BubbleP( T, x, P, y )
      except : break
        
      # Check error for single phase close to critical point
      if it == None :  break
      if it > 999   :  break   # Convergence error occurs 
      
      print("%10.5f  %8.4f  %8.4f       %d " %( P, x[1], y[1], it ) )
    
      SavedResult[ 'existVvap' ] = True
      SavedResult[ 'existVliq' ] = True
      SavedResult[ 'Vvap' ] = Vvap
      SavedResult[ 'Vliq' ] = Vliq
      
      PP.append( P )
      XX.append( x[1] )
      YY.append( y[1] )  

    # plot Pxy diagram      
    plt.plot( XX, PP, linestyle='-'  )
    plt.plot( YY, PP, linestyle='--')
    plt.plot( Xexp, Pexp, 'bo')
    if isTPxySet : plt.scatter( Yexp, Pexp, s=40, facecolors='none', edgecolors ='r' )
    plt.xlabel(r'$x_1, y_1$')
    plt.xlim(0,1)
    plt.ylabel('P (bar)')    
    plt.title( plot_title, fontsize=11 )
    plt.show()


elif TypCal == 3 :            # 3 (DewP)

  print("<<  Pxy - Dew point pressure calculation for given T, y  >> \n "  )
  print(" T  = %9.2f (K) "   % T )
  
  y = z[:]    

  WholeRange = False           # single point calculation
  if nc == 2 and z[1]+z[2] < 1e-10 : WholeRange = True
  
  if not WholeRange :
    
    print(" y  = ", end=" " )
    for i in range(1,ncP) :
      print("%8.4f" % y[i], end=" ") 
    print("\n")
  
    P, x = DewP_ini( T, y )
    P, x, Vliq, Vvap, it = DewP( T, y, P, x )
  
    print("\n Dew point pressure  = %10.5f (bar) \n" % P )
    print(" comp     vapor     liquid       K " )
    for i in range(1,ncP) :
      print("  %d     %8.4f   %8.4f   %8.4f " %( i, y[i], x[i], y[i]/x[i] ) ) 
    print()    
    
    Zvap = P * Vvap / ( R * T )
    Zliq = P * Vliq / ( R * T )
    print("  Z     %8.4f   %8.4f \n" %( Zvap, Zliq ) )    
    print(" Number of iterations = %d " % it )


  else :    # WholeRange from x = 0 to 1    only for binary mixture 

    # list for plot
    PP = []
    XX = []
    YY = []    
    
    nstep = 100
    Y = [ i/nstep for i in range(nstep+1) ]
    
    # initial guess at y1 = 0
    y[1] = Y[0]
    y[2] = 1 - y[1]
    P, x = DewP_ini( T, y )

    print()
    print("  P (bar)    x1        y1       iteration")
    print("-----------------------------------------")
    
    for y[1] in Y :
    
      y[2] = 1 - y[1]
      
      P, x, Vliq, Vvap, it = DewP( T, y, P, x )

      # Check error for single phase close to critical point
      if it == None :  break
      if it > 999   :  break   # Convergence error occurs 
      
      print("%10.5f  %8.4f  %8.4f       %d " %( P, x[1], y[1], it ) )
      
      SavedResult[ 'existVvap' ] = True
      SavedResult[ 'existVliq' ] = True
      SavedResult[ 'Vvap' ] = Vvap
      SavedResult[ 'Vliq' ] = Vliq
      
      PP.append( P )
      XX.append( x[1] )
      YY.append( y[1] )  

    # plot Pxy diagram      
    plt.plot( XX, PP, linestyle='-'  )
    plt.plot( YY, PP, linestyle='--')
    plt.plot( Xexp, Pexp, 'bo')
    if isTPxySet : plt.scatter( Yexp, Pexp, s=40, facecolors='none', edgecolors ='r' )
    plt.xlabel(r'$x_1, y_1$')
    plt.xlim(0,1)
    plt.ylabel('P (bar)')
    plt.title( plot_title, fontsize=11 )
    plt.show()


elif TypCal == 4 :            # 4 (BubbleT)

  print("<<  Txy - Bubble point temperature calculation for given P, x  >> \n "  )
  print(" P  = %9.3f (bar) "   % P )
  
  x = z[:]   
  
  WholeRange = False           # single point calculation
  if nc == 2 and z[1]+z[2] < 1e-10 : WholeRange = True
  
  if not WholeRange :
    
    print(" x  = ", end=" " )
    for i in range(1,ncP) :
      print("%8.4f" % x[i], end=" ") 
    print()
  
    T, y = BubbleT_ini( P, x )
    T, y, Vliq, Vvap, it = BubbleT( P, x, T, y )
  
    print("\n Bubble point temperature  = %8.2f (K) \n" % T )
    print(" comp     liquid     vapor       K " )
    for i in range(1,ncP) :
      print("  %d     %8.4f   %8.4f   %8.4f " %( i, x[i], y[i], y[i]/x[i] ) ) 
    print()    
    
    Zvap = P * Vvap / ( R * T )
    Zliq = P * Vliq / ( R * T )
    print("  Z     %8.4f   %8.4f \n" %( Zliq, Zvap ) )    
    print(" Number of iterations = %d " % it )


  else :    # WholeRange from x = 0 to 1    only for binary mixture 

    # list for plot
    TT = []
    XX = []
    YY = []    
    
    nstep = 200
    X = [ i/nstep for i in range(nstep+1) ]
    
    # initial guess at x1 = 0
    x[1] = X[0]
    x[2] = 1 - x[1]
    T, y = BubbleT_ini( P, x )

    print()
    print("  T (K)      x1        y1       iteration")
    print("-----------------------------------------")
    
    for x[1] in X :
    
      x[2] = 1 - x[1]
      
      T, y, Vliq, Vvap, it = BubbleT( P, x, T, y )

      # Check error for single phase close to critical point
      if it == None :  break
      if it > 999   :  break   # Convergence error occurs 
      
      print(" %8.2f  %8.4f  %8.4f       %d " %( T, x[1], y[1], it ) )
      
      SavedResult[ 'existVvap' ] = True
      SavedResult[ 'existVliq' ] = True
      SavedResult[ 'Vvap' ] = Vvap
      SavedResult[ 'Vliq' ] = Vliq
      
      TT.append( T )
      XX.append( x[1] )
      YY.append( y[1] )  

    # plot Pxy diagram      
    plt.plot( XX, TT, linestyle='-'  )
    plt.plot( YY, TT, linestyle='--')
    plt.plot( Xexp, Texp, 'bo')
    if isTPxySet : plt.scatter( Yexp, Texp, s=40, facecolors='none', edgecolors ='r' )  
    plt.xlabel(r'$x_1, y_1$') 
    plt.xlim(0,1)
    plt.ylabel('T (K)')
    plt.title( plot_title, fontsize=11 )
    plt.show()


elif TypCal == 5 :            # 5 (DewT)

  print("<<  Txy - Dew point temperature calculation for given P, y  >> \n "  )
  print(" P  = %9.3f (bar) "   % P )
  
  y = z[:]    
  
  WholeRange = False           # single point calculation
  if nc == 2 and z[1]+z[2] < 1e-10 : WholeRange = True
  
  if not WholeRange :
    
    print(" y  = ", end=" " )
    for i in range(1,ncP) :
      print("%8.4f" % y[i], end=" ") 
    print()
  
    T, x = DewT_ini( P, y )
    T, x, Vliq, Vvap, it = DewT( P, y, T, x )
  
    print("\n Dew point temperature  = %8.2f (K) \n" % T )
    print(" comp     vapor     liquid       K " )
    for i in range(1,ncP) :
      print("  %d     %8.4f   %8.4f   %8.4f " %( i, y[i], x[i], y[i]/x[i] ) ) 
    print()    
    
    Zvap = P * Vvap / ( R * T )
    Zliq = P * Vliq / ( R * T )
    print("  Z     %8.4f   %8.4f \n" %( Zvap, Zliq ) )    
    print(" Number of iterations = %d " % it )


  else :    # WholeRange from x = 0 to 1    only for binary mixture 

    # list for plot
    TT = []
    XX = []
    YY = []    
    
    nstep = 100
    Y = [ i/nstep for i in range(nstep+1) ]
    
    # initial guess at y1 = 0
    y[1] = Y[0]
    y[2] = 1 - y[1]
    T, x = DewT_ini( P, y )

    print()
    print("  T (K)      x1        y1       iteration")
    print("-----------------------------------------")
    
    for y[1] in Y :
    
      y[2] = 1 - y[1]
      
      T, x, Vliq, Vvap, it = BubbleT( P, y, T, x )

      # Check error for single phase close to critical point
      if it == None :  break
      if it > 999   :  break   # Convergence error occurs 
      
      print(" %8.2f  %8.4f  %8.4f       %d " %( T, x[1], y[1], it ) )
      
      SavedResult[ 'existVvap' ] = True
      SavedResult[ 'existVliq' ] = True
      SavedResult[ 'Vvap' ] = Vvap
      SavedResult[ 'Vliq' ] = Vliq
      
      TT.append( T )
      XX.append( x[1] )
      YY.append( y[1] )  

    # plot Pxy diagram      
    plt.plot( XX, TT, linestyle='-'  )
    plt.plot( YY, TT, linestyle='--')
    plt.plot( Xexp, Texp, 'bo')
    if isTPxySet : plt.scatter( Yexp, Texp, s=40, facecolors='none', edgecolors ='r' )
    plt.xlabel(r'$x_1, y_1$')
    plt.xlim(0,1)
    plt.ylabel('T (K)')
    plt.title( plot_title, fontsize=11 )
    plt.show()


elif TypCal == 6 :            # 6 (Flash)

  print("<<  Flash calculation for given T, P, z  >> \n "  )
  print(" T  = %9.2f (K) "   % T )
  print(" P  = %9.3f (bar) " % P )
  print(" z  = ", end=" " )
  for i in range(1,ncP) :  print("%8.4f" % z[i], end=" ") 
  print("\n")

  # Get bubble pressure and dew pressure, and check whether  Pdew < P < Pbubble
  
  Pb, yb = BubbleP_ini( T, z )
  Pb, yb, Vliq, Vvap, it = BubbleP( T, z, Pb, yb )
  print(" bubble P  = %9.3f (bar)   " % Pb )
  
  Pd, xd =  DewP_ini( T, z )
  Pd, xd, Vliq, Vvap, it =    DewP( T, z, Pd, xd )
  print("   dew  P  = %9.3f (bar) \n" % Pd )
  
  if P < Pd or P > Pb :
    print(" Pressure is not in valid range! \n Taking mean value ... \n" )
    P = ( Pd + Pb ) / 2
    print(" P  = %9.3f (bar) " % P )   
  else :
    print(" Pressure is valid:  P(dew) < P < P(bubble)")

  # initial guess 
  v = ( Pb - P ) / ( Pb - Pd )
  x = [0]*ncP
  y = [0]*ncP
  for i in range(1,ncP) :  x[i] = (1-v) * z[i] + v *xd[i]
  for i in range(1,ncP) :  y[i] = (1-v) *yb[i] + v * z[i] 

  x, y, K, Vliq, Vvap, v, it  =  Flash( P, T, z, x, y, v )
  
  print()  
  print(" comp      feed        liquid     vapor       K " )
  for i in range(1,ncP) :
    print("  %d      %8.4f    %8.4f   %8.4f   %8.4f " %( i, z[i], x[i], y[i], K[i] ) ) 
  print()    

  Zvap = P * Vvap / ( R * T )
  Zliq = P * Vliq / ( R * T )
  print(" fraction  1.0000    %8.4f   %8.4f \n" %( 1-v, v ) )  
  print("  Z                  %8.4f   %8.4f \n" %( Zliq, Zvap ) )
  print(" Number of iterations = %d  \n" % it )


input("Press Enter to quit ")



pcsaft_mix.py

In [6]:
!type pcsaft_mix.py
# PC-SAFT mixture 

from math import *
import numpy as np
from scipy import linalg
 
#-----------------------------------------------------------------------

def pcsaft( ZorPhi, V, T, x, m, sig, epsk, kAB, eAB, nc, Keps, ncas, dipol, xp ) :

  ncP = nc + 1           
  
  Pi = 3.1415926536
  Nav = 6.02214179e23
  
  rhom = 1 / V                               # molar density  (mol/m3)
  rho = rhom * Nav                           # number density (molecules/m3)

  M = 0                                      # mean chain length
  d = [0]*ncP                                # effective segmemt diameter (m)
  t = [0]*4
  g = [0]*ncP
  roDg = [0]*ncP
  
  for i in range(1,ncP) :
    M += x[i] * m[i]       
    d[i] = sig[i] * ( 1 - 0.12*exp( -3*epsk[i]/T ) )
    t[0] += Pi/6*rho* x[i] * m[i] 
    t[1] += Pi/6*rho* x[i] * m[i] * d[i]
    t[2] += Pi/6*rho* x[i] * m[i] * d[i]**2
    t[3] += Pi/6*rho* x[i] * m[i] * d[i]**3

  # Hard-sphere term      m * (Zhs-1) ...  
  Zhc = M * ( t[3]/(1-t[3]) + 3*t[1]*t[2]/t[0]/(1-t[3])**2      \
      + t[2]**3 *( 3 - t[3] ) /t[0]/(1-t[3])**3 ) 
    
  # Hard chain term     
  for i in range(1,ncP) :
    g[i] = 1/(1-t[3]) + d[i]/2 * 3*t[2]/(1-t[3])**2  \
         + (d[i]/2)**2 * 2*t[2]**2/(1-t[3])**3
    roDg[i] = t[3]/(1-t[3])**2     \
      +  d[i]/2 * ( 3*t[2]/(1-t[3])**2 + 6*t[2]*t[3]/(1-t[3])**3 )     \
      + (d[i]/2)**2 * ( 4*t[2]**2/(1-t[3])**3 + 6*t[2]**2 *t[3]/(1-t[3])**4 )
    Zhc += - x[i] * (m[i]-1) * roDg[i] / g[i] 
                
  
# Dispersion term

  y = t[3]             # packing fraction

  a0 = [ 0.9105631445, 0.6361281449, 2.6861347891, -26.547362491, 97.759208784, -159.59154087, 91.297774084]
  a1 = [ -0.3084016918, 0.1860531159, -2.5030047259, 21.419793629, -65.255885330, 83.318680481, -33.746922930]
  a2 = [ -0.0906148351, 0.4527842806, 0.5962700728, -1.7241829131, -4.1302112531, 13.776631870, -8.6728470368]
  b0 = [ 0.7240946941, 2.2382791861, -4.0025849485, -21.003576815, 26.855641363, 206.55133841, -355.60235612]
  b1 = [ -0.5755498075, 0.6995095521, 3.8925673390, -17.215471648, 192.67226447, -161.82646165, -165.20769346]
  b2 = [ 0.0976883116, -0.2557574982, -9.1558561530, 20.642075974, -38.804430052, 93.626774077, -29.666905585]

  a = [ ]
  b = [ ]
  I1 = 0
  I2 = 0
  DyI1 = 0
  DyI2 = 0
  
  for i in range(7):   # [0,1,2 ... 6]
    a.append( a0[i] + (M-1)/M*a1[i] + (M-1)*(M-2)/M**2 *a2[i] )
    b.append( b0[i] + (M-1)/M*b1[i] + (M-1)*(M-2)/M**2 *b2[i] )
    
    I1 += a[i] * y**i             # used for Adisp 
    I2 += b[i] * y**i
    DyI1 += (i+1)* a[i] * y**i
    DyI2 += (i+1)* b[i] * y**i

  C1 = 1/( 1 + M*(8*y-2*y**2)/(1-y)**4 - (M-1)*(20*y-27*y**2+12*y**3-2*y**4)/(1-y)**2/(2-y)**2 )    
  # C2 = DC1 = (d/dy) C1 
  C2 = -C1*C1 * ( M*(-4*y**2+20*y+8)/(1-y)**5 - (M-1)*(2*y**3+12*y**2-48*y+40)/(1-y)**3/(2-y)**3 )

  m2ekTsig3 = 0
  m2ekT2sig3 = 0
  for i in range(1,ncP) :
    for j in range(1,ncP) :
      sig3 = ( ( sig[i] + sig[j] ) / 2 )**3
      ekT = sqrt( epsk[i] * epsk[j] ) * (1-Keps[i][j]) / T
      m2ekTsig3  += x[i]*x[j]*m[i]*m[j]* ekT * sig3
      m2ekT2sig3 += x[i]*x[j]*m[i]*m[j]* ekT**2 * sig3
      
  Zdisp = - 2*Pi*rho *DyI1 *m2ekTsig3 - Pi*rho *M *( C1*DyI2 + C2*y*I2 ) * m2ekT2sig3

  
  
  # Association term
  
  Zassoc = 0
     
  # ncas = 2  # for test
  
  # single associaing components
  if ncas == 1 : 
  
    # find the only associating component I
    for i in range(1,ncP) :
      if eAB[i][i] > 1e-10 : I = i
      
    F = exp( eAB[I][I] / T ) - 1
    K = sig[I]**3 * kAB[I][I]
    Del = g[I] * F * K
    
    if x[I] < 1e-10 :           # to avoid math error for pure substance
      X = 1
      Zassoc = 0
    else :
      X = ( -1 + sqrt( 1 + 4*rho*x[I]*Del) ) / ( 2*rho*x[I]*Del )
      roDDel = roDg[I] * F * K
      DX = -x[I] * X**3 / ( 1 + rho*x[I]*X**2 *Del ) * ( Del + roDDel )
      Zassoc = x[I] * 2* ( 1/X - 1/2 ) * rho * DX

    #print(X,rho*DX)  # for check
    
  
  # multiple associaing components
  elif ncas >= 2 :  
  
  
    F   = [ [0]*ncP for i in range(ncP) ]
    K   = [ [0]*ncP for i in range(ncP) ]
    DEL  = [ [0]*ncP for i in range(ncP) ]
    roDDEL = [ [0]*ncP for i in range(ncP) ]
    
    for i in range(1,ncP) :
      for j in range(1,ncP) :
      
        F[i][j] = exp( eAB[i][j] / T ) - 1                        
        K[i][j] = ( ( sig[i]+sig[j] )/2 )**3 * kAB[i][j]
        Gij = 1/(1-t[3]) + d[i]*d[j]/(d[i]+d[j]) * 3*t[2]/(1-t[3])**2  \
                + ( d[i]*d[j]/(d[i]+d[j]) )**2 * 2*t[2]**2/(1-t[3])**3
        DEL[i][j] = Gij * F[i][j] * K[i][j]
        
        roDGij = t[3]/(1-t[3])**2     \
      +  d[i]*d[j]/(d[i]+d[j]) * ( 3*t[2]/(1-t[3])**2 + 6*t[2]*t[3]/(1-t[3])**3 )   \
      + ( d[i]*d[j]/(d[i]+d[j]) )**2 * ( 4*t[2]**2/(1-t[3])**3 + 6*t[2]**2 *t[3]/(1-t[3])**4 )
        roDDEL[i][j] = roDGij * F[i][j] * K[i][j]
        
    
    # Find X's by iteration (successive substitution)
    X  = [1]*ncP                         # defaul value X = 1 for non-associating components
    # guess of X for associating component
    for i in range(1,ncP) :
      if eAB[i][i] > 1e-10 and x[i] > 1e-10 : 
        X[i] = ( -1 + sqrt( 1 + 4*rho*x[i]*DEL[i][i] ) ) / ( 2*rho*x[i]*DEL[i][i] )
    
    method = 0          # successive substitution
    #method = 1            # Newton method

    if method == 0 :      # successive substitution
    
      while 1 :
        #print(X[1],X[2])  # to check
        
        Xold = X[:]
        err = 0
        
        for i in range(1,ncP) :
          sum = 1
          for j in range(1,ncP) :
            sum += rho * x[j] * X[j] * DEL[i][j]
          X[i] = 1 / sum
          err += abs( X[i]-Xold[i] )
        
        if err/ncP < 1e-5 : break

        
    elif method == 1 :     # Newton method

      dX = [0]*ncP
      
      while 1 :
        #print(X) # to check  
      
        A = [ [0]*nc for i in range(nc) ]    # from 0, 1, 2 ...
        B = [0]*nc       
        
        for i in range(1,ncP) :
          i_ = i - 1
          A[i_][i_] = 1
          B[i_] = 1 - X[i]
          
          for j in range(1,ncP) :
            A[i_][i_] += rho * x[j] * X[j] * DEL[i][j]
            B[i_]   += - rho * x[j] * X[i] * X[j] * DEL[i][j]
            j_ = j - 1
            A[i_][j_] += rho * x[j] * X[i] * DEL[i][j]
        
        # Solve matrix equation    
        u = linalg.solve(A, B)
        dX[1:] = u[:]

        # update X and check error
        err = 0
        for i in range(1,ncP) : 
          X[i] += dX[i]           
          err  += abs( dX[i] )
        if err/ncP < 1e-5 : break

    # end if
    
 
    # Find DX      # = dX / d rho
    DX = [0]*ncP    
    
    # method = 0   # by iteration
    method = 1     # by matrix
    
    if method == 0 :    # method = 0  by iteration (successive substitution)
    
      while 1 :
        DXold = DX[:]
        err = 0
        
        for i in range(1,ncP) :
          sum = 0
          for j in range(1,ncP) :
            sum += x[j] * X[j] * ( DEL[i][j] + roDDEL[i][j] ) 
            if j == i : continue
            sum += rho * x[j] * DX[j] * DEL[i][j] 
                 
          DX[i] = - X[i]**2 / ( 1 + rho * x[i] * X[i]**2 *DEL[i][i] ) * sum
          err += abs( DX[i]-DXold[i] )
        
        if err/ncP < 1e-6 : break

        
    elif method == 1 :   # method == 1  by solving matrix
    
      A = [ [0]*nc for i in range(nc) ]    # from 0, 1, 2 ...
      B = [0]*nc       
      
      for i in range(1,ncP) :      
        i_ = i - 1
        A[i_][i_] = 1
        B[i_] = 0
        for j in range(1,ncP) :
          j_ = j - 1
          A[i_][j_] += X[i]**2 * rho * x[j] * DEL[i][j]
          B[i_]   += - X[i]**2 * x[j]*X[j]*( DEL[i][j] + roDDEL[i][j] )
          
      # Solve matrix equation    
      u = linalg.solve(A, B)
      DX[1:] = u[:]

    # end if method
    
    
    Zassoc = 0
    for i in range(1,ncP) :
      Zassoc += x[i]* 2* ( 1/X[i] - 1/2 ) * rho * DX[i]
      #print(X[i],rho*DX[i])  # for check
  
  #-- end of association --

  
  # polar term - Jog and Chapman
  
  Zpolar = 0
  nc_pol = 0              # number of polar components
  mu2k = [0]*ncP
  
  for i in range(1,ncP) :
    if dipol[i] > 1e-10 : 
      nc_pol += 1
      mu2k[i] = dipol[i]**2 / 1.380572e26       # dipol^2 (D^2) / k    (m3 K)
  
  if nc_pol > 0 :
  
    dd = [ [0]*ncP for i in range(ncP) ]
    for i in range(1,ncP) :
      for j in range(1,ncP) :
        dd[i][j] = ( d[i] + d[j] ) / 2
     
    rosta = 6/Pi * t[3]
    I2p = ( 1 -0.3618*rosta -0.3205*rosta**2 +0.1078*rosta**3 ) / ( 1 -0.5236*rosta )**2
    I3p = ( 1 +0.62378*rosta -0.11658*rosta**2 ) / ( 1 -0.59056*rosta +0.20059*rosta**2 )
    DI2p = ( 0.6854 -0.83043848*rosta +0.3234*rosta**2 -0.05644408*rosta**3 ) / ( 1 -0.5236*rosta )**3
    DI3p = ( 1.21434 -0.63434*rosta -0.0562765454*rosta**2 ) / ( 1 -0.59056*rosta +0.20059*rosta**2 )**2

    sumij = 0
    for i in range(1,ncP) :
      for j in range(1,ncP) :
        sumij += x[i]*x[j] *m[i]*m[j] *xp[i]*xp[j] *mu2k[i]*mu2k[j] / dd[i][j]**3
    A2p = - 2 * Pi / 9 * rho / T**2 * sumij * I2p
    roDA2p = A2p  - 2 * Pi / 9 * rho / T**2 * sumij * rosta * DI2p
    
    sumijl = 0
    for i in range(1,ncP) :
      for j in range(1,ncP) :
        for l in range(1,ncP) :
          sumijl += x[i]*x[j]*x[l] *m[i]*m[j]*m[l] *xp[i]*xp[j]*xp[l]         \
                   *mu2k[i]*mu2k[j]*mu2k[l] /( dd[i][j]*dd[j][l]*dd[i][l] )
    A3p = 5 * Pi**2 / 162 * rho**2 / T**3 * sumijl * I3p
    roDA3p = 2*A3p  + 5 * Pi**2 / 162 * rho**2 / T**3 * sumijl * rosta * DI3p
    
    if abs( A2p ) < 1e-10 :       # nonpolar pure component
      Zpolar = 0
    else :
      Zpolar = 2/( 1 - A3p/A2p ) * roDA2p - 1/( 1 - A3p/A2p )**2 * ( roDA2p - roDA3p ) 
    
  #-- end of polar term
  
  
  Z = 1 + Zhc + Zdisp + Zassoc + Zpolar
  if ZorPhi == 'Z' :  return Z            # done with Z



  
 
  # Fugacity coefficient  
  
  Ahs = 1/t[0] * ( 3*t[1]*t[2]/(1-t[3]) + t[2]**3 /t[3]/(1-t[3])**2   \
      + ( t[2]**3/t[3]**2 - t[0] )* log(1-t[3]) )
  
  Ahc = M * Ahs 
  for i in range(1,ncP) :  Ahc += -x[i] * (m[i]-1) * log( g[i] )

  tx = [ [0]*ncP  for i in range(4) ]
  for k in range(1,ncP) :
    tx[0][k] = Pi/6 *rho *m[k] 
    tx[1][k] = Pi/6 *rho *m[k] * d[k]
    tx[2][k] = Pi/6 *rho *m[k] * d[k]**2
    tx[3][k] = Pi/6 *rho *m[k] * d[k]**3
  
  Ahsx = [0]*ncP
  for k in range(1,ncP) :
    Ahsx[k] = -tx[0][k]/t[0]*Ahs + 1/t[0] * ( 3*( tx[1][k]*t[2] + t[1]*tx[2][k] )/(1-t[3])  \
        + 3*t[1]*t[2]*tx[3][k]/(1-t[3])**2 + 3*t[2]**2 *tx[2][k]/t[3]/(1-t[3])**2           \
        + t[2]**3 *tx[3][k] *(3*t[3]-1) /t[3]**2 /(1-t[3])**3                               \
        + ( ( 3*t[2]**2 *tx[2][k]*t[3] - 2*t[2]**3 *tx[3][k] )/t[3]**3 - tx[0][k] )         \
        * log(1-t[3]) + ( t[0] - t[2]**3 / t[3]**2 ) * tx[3][k]/(1-t[3])   )

  gx = [ [0]*ncP for i in range(ncP) ]
  for i in range(1,ncP) :
    for k in range(1,ncP) :
      gx[i][k] = tx[3][k]/(1-t[3])**2                                                    \
        +  d[i]/2 *( 3*tx[2][k]/(1-t[3])**2 + 6*t[2]*tx[3][k]/(1-t[3])**3 )              \
        + (d[i]/2)**2 * ( 4*t[2]*tx[2][k]/(1-t[3])**3 + 6*t[2]**2 *tx[3][k]/(1-t[3])**4 ) 

  Ahcx = [0]*ncP
  for k in range(1,ncP) :
    Ahcx[k] = m[k] * Ahs + M * Ahsx[k] - (m[k]-1) * log( g[k] )             # with correction
    for i in range(1,ncP) :  Ahcx[k] += -x[i] * (m[i]-1) / g[i] * gx[i][k] 

  # add to fugacity coefficient        
  lnphi = [0]*ncP   
  
  for k in range(1,ncP) : 
    lnphi[k] = Z-1 - log(Z) + Ahc + Ahcx[k]
    for j in range(1,ncP) :  lnphi[k] += -x[j] * Ahcx[j]

      

  # Dispersion term
    
  Adisp = -2*Pi *rho *I1 * m2ekTsig3 -Pi *rho *M *C1 *I2 *m2ekT2sig3
  
  m2ekTsig3x  = [0]*ncP
  m2ekT2sig3x = [0]*ncP
  for k in range(1,ncP) :
    for j in range(1,ncP) :
      sig3 = ( ( sig[k] + sig[j] ) / 2 )**3
      ekT = sqrt( epsk[k] * epsk[j] ) * (1-Keps[k][j]) / T
      m2ekTsig3x[k]  += 2*m[k]*x[j]*m[j]* ekT * sig3
      m2ekT2sig3x[k] += 2*m[k]*x[j]*m[j]* ekT**2 * sig3
      
  C1x = [0]*ncP
  for k in range(1,ncP) :
    C1x[k] = C2 * tx[3][k] - C1**2 *( m[k]*(8*y -2*y**2)/(1-y)**4      \
           - m[k]*(20*y -27*y**2 +12*y**3 -2*y**4)/(1-y)**2/(2-y)**2 )
  
  ax = [ [0]*ncP  for i in range(7) ]
  bx = [ [0]*ncP  for i in range(7) ]
  for i in range(7) :
    for k in range(1,ncP) :
      ax[i][k] = m[k]/M**2 *a1[i] + m[k]/M**2 *(3-4/M) *a2[i]
      bx[i][k] = m[k]/M**2 *b1[i] + m[k]/M**2 *(3-4/M) *b2[i]
  
  I1x = [0]*ncP
  I2x = [0]*ncP
  for k in range(1,ncP) :
    for i in range(7) :
      I1x[k] += a[i] * i * tx[3][k] * y**(i-1) + ax[i][k] * y**i 
      I2x[k] += b[i] * i * tx[3][k] * y**(i-1) + bx[i][k] * y**i

  Adispx = [0]*ncP
  for k in range(1,ncP) :
    Adispx[k] = - 2*Pi*rho * ( I1x[k] * m2ekTsig3 + I1 * m2ekTsig3x[k] )     \
          - Pi*rho *( ( m[k]*C1*I2 + M*C1x[k]*I2 + M*C1*I2x[k] ) *m2ekT2sig3   \
          + M *C1 *I2 *m2ekT2sig3x[k] )

  # add to fugacity coefficient        
  for k in range(1,ncP) : 
    lnphi[k] += Adisp + Adispx[k]
    for j in range(1,ncP) :  lnphi[k] += -x[j] * Adispx[j]

 
 
  # Association term   
  
  # single associaing components of I
  if ncas == 1 : 
  
    Aassoc = x[I] * 2* ( log(X) - X/2 + 1/2 )
    Delx = [0]*ncP  
    Xx   = [0]*ncP 

    for k in range(1,ncP) :  
      Delx[k] = gx[I][k] * F * K
      Xx[k] = -X**2/( 1 + rho*x[I]*X**2*Del)*( rho*x[I]*X*Delx[k] )
    Xx[I]  += -X**2/( 1 + rho*x[I]*X**2*Del)*( rho*X*Del )
    
    Aassocx = [0]*ncP
    for k in range(1,ncP) :
      Aassocx[k] =  x[I] * 2* ( 1/X - 1/2 ) * Xx[k]
    Aassocx[I] +=  2 * ( log(X) - X/2 + 1/2 )      
    
    # add to fugacity coefficient        
    for k in range(1,ncP) : 
      lnphi[k] += Aassoc + Aassocx[k]
      for j in range(1,ncP) :  lnphi[k] += -x[j] * Aassocx[j]
    
    
  # multiple associaing components
  elif ncas >= 2 :  
  
    
    DELx = [ [ [0]*ncP for j in range(ncP) ] for i in range(ncP) ]
    
    for i in range(1,ncP) :
      for j in range(1,ncP) :
        for k in range(1,ncP) :
          Gijxk = tx[3][k]/(1-t[3])**2                                                    \
            + d[i]*d[j]/(d[i]+d[j]) *( 3*tx[2][k]/(1-t[3])**2 + 6*t[2]*tx[3][k]/(1-t[3])**3 )              \
            + ( d[i]*d[j]/(d[i]+d[j]) )**2 * ( 4*t[2]*tx[2][k]/(1-t[3])**3 + 6*t[2]**2 *tx[3][k]/(1-t[3])**4 ) 
          DELx[i][j][k] = Gijxk * F[i][j] * K[i][j]

          
    # Find (X)x 
    Xx =  [ [0]*ncP for i in range(ncP) ]
    
    # method = 0   # by iteration
    method = 1     # by matrix
    
    if method == 0 :    # method = 0  by iteration (successive substitution)
      
      while 1 :
        Xxold = Xx[:]
        err = 0
        
        for i in range(1,ncP) :
          for k in range(1,ncP) :
            sum = rho * X[k] * DEL[i][k]
            for j in range(1,ncP) :
              sum += rho * x[j] * X[j] * DELx[i][j][k]   
              if j == i : continue
              sum += rho * x[j] * Xx[j][k] * DEL[i][j]
                 
            Xx[i][k] = - X[i]**2 / ( 1 + rho *x[i] *X[i]**2 *DEL[i][i] ) * sum
            err += abs( Xx[i][k] - Xxold[i][k] )
            
        if err/ncP/ncP < 1e-6 : break

      
    elif method == 1 :            # by solving matrix
    
      A = [ [0]*nc**2 for i in range(nc**2) ]  # from 0, 1, 2 ...
      B = [0]*nc**2    
      
      for i in range(1,ncP) :                  
        for k in range(1,ncP) :
          ik_ = (i-1)*nc + k - 1 
          A[ik_][ik_] = 1
          B[ik_]  = - X[i]**2 * rho * X[k]* DEL[i][k] 
          
          for j in range(1,ncP) :
            jk_ = (j-1)*nc + k - 1 
            A[ik_][jk_] += X[i]**2 * rho * x[j] * DEL[i][j]
            B[ik_]    += - X[i]**2 * rho * x[j] * X[j] * DELx[i][j][k]
            
      # Solve matrix equation    
      u = linalg.solve(A, B)
      
      # patch solution
      for i in range(1,ncP) :                  
        for k in range(1,ncP) :
          ik_ = (i-1)*nc + k - 1
          Xx[i][k] = u[ik_]

    # end if method
      
    
    Aassoc = 0
    for i in range(1,ncP) :
      Aassoc += x[i] * 2* ( log(X[i]) - X[i]/2 + 1/2 )
    
    Aassocx = [0]*ncP
    for k in range(1,ncP) :
      for i in range(1,ncP) :
        Aassocx[k] +=  x[i] * 2* ( 1/X[i] - 1/2 ) * Xx[i][k]
      Aassocx[k] +=  2* ( log(X[k]) - X[k]/2 + 1/2 )      

      
    # add to fugacity coefficient        
    for k in range(1,ncP) : 
      lnphi[k] += Aassoc + Aassocx[k]
      for j in range(1,ncP) :  lnphi[k] += -x[j] * Aassocx[j]
      
  #-- end of association --

  
  
  # polar term - Jog and Chapman

  if nc_pol > 0 and abs( A2p ) > 1e-10 :
    
    Apolar = A2p / ( 1 - A3p / A2p )
    
    dI2px = [0]*ncP
    dI3px = [0]*ncP    
    A2px  = [0]*ncP
    A3px  = [0]*ncP
    Apolx = [0]*ncP
    
    for k in range(1,ncP) :
      dI2px[k] = DI2p * rho * m[k] * d[k]**3
      dI3px[k] = DI3p * rho * m[k] * d[k]**3
    
    for k in range(1,ncP) :
      sum = 0
      for i in range(1,ncP) :
        sum += x[i] *m[i]*m[k] *xp[i]*xp[k] *mu2k[i]*mu2k[k] / dd[i][k]**3
        
      A2px[k] =  - 2*Pi/9 * rho / T**2 * ( 2 * sum * I2p  +  sumij * dI2px[k] )
    
    
    for k in range(1,ncP) :
      sum = 0
      for i in range(1,ncP) :
        for j in range(1,ncP) :
          sum += x[i]*x[j] *m[i]*m[j]*m[k] *xp[i]*xp[j]*xp[k] *mu2k[i]*mu2k[j]*mu2k[k]  \
               /( dd[i][j]*dd[j][k]*dd[i][k] )
    
      A3px[k] =  5*Pi**2 /162 *rho**2 /T**3 * ( 3 *sum *I3p  +  sumijl * dI3px[k] )
    
    for k in range(1,ncP) :
      Apolx[k] = 2/(1 - A3p/A2p ) *A2px[k]  - 1/(1 - A3p/A2p )**2 *( A2px[k] - A3px[k] ) 

      
    # add to fugacity coefficient        
    for k in range(1,ncP) : 
      lnphi[k] += Apolar + Apolx[k]
      for j in range(1,ncP) :  lnphi[k] += -x[j] * Apolx[j]

      
  #-- end of polar term


  phi = [0]*ncP
  for k in range(1,ncP) :  phi[k] = exp( lnphi[k] )
    
  return  phi
  









PC-SAFT equation of state

VLE_mixture 혼합물의 기체-액체 상평형 (vapor-liquid phase equilibrium) ¶ ...