def calcFugDerv(qBub,pSat,tRes,Z,logK,clsEOS) : iPhs = 0 #-- Force cubic solver to pick root with lowest GFE qP = True #-- Don't Need Temp or Comp Derivatives, Just Pres qT = False qX = False #-- Incipient Phase ------------------------------------------------- K = NP.exp(logK) yMol = Z*K #-- Vector! #-- Get Fugacity Coeffs and their Pressure-Derivatives -------------- yNor = UT.Norm(yMol) fugZ,dZdP,dumV,dumM = CE.calcPhaseFugPTX(iPhs,qP,qT,qX,pSat,tRes,Z ,clsEOS) fugY,dYdP,dumV,dumM = CE.calcPhaseFugPTX(iPhs,qP,qT,qX,pSat,tRes,yNor,clsEOS) #== Residual and Derivative Difference ================================ res0 = fugZ - fugY - logK #-- Vector! dFdP = dYdP - dZdP #-- Vector! #== Calculate tol2 ===================================================== tol2 = calcTOL2(logK,res0) #== Return values ===================================================== return tol2,yMol,res0,dFdP
def flashFuncDervBFGS(AorB,qAlf,pRes,tRes,Z,clsEOS) : nCom = clsEOS.nComp qP = False #-- Don't need P, T or X LogPhi Derivatives qT = False qX = False iLiq = 1 ; iVap = -1 #== Calculate Vapour Fraction, Liquid & Vapour Moles ================== if qAlf : sumV,niL,niV = calcLiqVapMoles(Z,AorB) else : sumV,niL,niV = calcVapLiqMoles(Z,AorB) #== Mole Fractions of Liquid and Vapour =============================== X,Y = molNumToMolFrac(sumV,niL,niV) Xmin = 1.0E-20 X = NP.maximum(X,Xmin) Y = NP.maximum(Y,Xmin) logX = NP.log(X) logY = NP.log(Y) #== Log Fugacity Coefficients ========================================= fugX,dumV,dumV,dumM = CE.calcPhaseFugPTX(iLiq,qP,qT,qX,pRes,tRes,X,clsEOS) fugY,dumV,dumV,dumM = CE.calcPhaseFugPTX(iVap,qP,qT,qX,pRes,tRes,Y,clsEOS) #== Liquid and Vapour GFE ============================================= logX = logX + fugX #-- Vector! logY = logY + fugY #-- Vector! gfeL = NP.dot(niL,logX) gfeV = NP.dot(niV,logY) #== Total (2-Phase) GFE =============================================== gfeT = gfeL + gfeV #== Gradient Vector (in alfa-space) =================================== grad = logX - logY #-- Vector! aMul = NP.sqrt(niL*niV/Z) if qAlf : gMul = 1.0 else : gMul = -1.0 Grad = gMul*NP.multiply(aMul,grad) #-- Equation (3.197) #== Return Values ===================================================== return gfeT,Grad
def calcF1F2(pRes, tRes, zRef, lnKref, alfa, iDer, clsEOS, clsIO): nCom = clsEOS.nComp iLiq = 0 qXD = False #== Calculate the Incipient Phase Composition ========================= yMol, yNor = incipientY(alfa, zRef, lnKref, clsEOS, clsIO) #== Fugacity and its Pressure-Derivatives (if required) =============== if iDer == 1: qPD = True qTD = False elif iDer == -1: qPD = False qTD = True else: qPD = False qTD = False fugZ, dZdP, dZdT, dumM = CE.calcPhaseFugPTX(iLiq, qPD, qTD, qXD, pRes, tRes, zRef, clsEOS) fugY, dYdP, dYdT, dumM = CE.calcPhaseFugPTX(iLiq, qPD, qTD, qXD, pRes, tRes, yNor, clsEOS) #== Calculate Functions =============================================== f1 = 0.0 f2 = 0.0 eD = 0.0 fR = NP.log(yNor) + fugY - NP.log(zRef) - fugZ #-- Vector f1 = NP.dot(yNor, fR) f2 = NP.dot(zRef, fR) fR = NP.multiply(fR, fR) eD = NP.dot(zRef, fR) df1dX = 0.0 df2dX = 0.0 if iDer == 1: pDiff = dYdP - dZdP #-- Vector df1dX = NP.dot(yNor, pDiff) df2dX = NP.dot(zRef, pDiff) elif iDer == -1: tDiff = dYdT - dZdT #-- Vector df1dX = NP.dot(yNor, tDiff) df2dX = NP.dot(zRef, tDiff) #== Return values ===================================================== return f1, f2, eD, df1dX, df2dX
def calcGFE2(pRes,tRes,V,X,Y,clsEOS) : iLiq = 1 iVap = -1 qP = False #-- Don't need Pres, Temp or Comp LogPhi Derivatives qT = False qX = False xyMin = 1.0E-20 #-- Liquid Moles ---------------------------------------------------- L = 1.0 - V #-- Protect against zero (X,Y) -------------------------------------- X = NP.maximum(X,xyMin) Y = NP.maximum(Y,xyMin) #-- Log Mole Fractions ---------------------------------------------- logX = NP.log(X) logY = NP.log(Y) #-- Liquid and Vapour Log(Fugacity) Coefficients -------------------- fugX,dumV,dumV,dumM = \ CE.calcPhaseFugPTX(iLiq,qP,qT,qX,pRes,tRes,X,clsEOS) fugY,dumV,dumV,dumM = \ CE.calcPhaseFugPTX(iVap,qP,qT,qX,pRes,tRes,Y,clsEOS) #-- Log Mole Fractions now become 'Chemical Potential' -------------- logX = logX + fugX #-- Vector! logY = logY + fugY #-- Vector! #== 2-Phase GFE ======================================================= gfeL = NP.dot(X,logX) gfeV = NP.dot(Y,logY) gfe2 = L*gfeL + V*gfeV #== Gradient (wrt Liquid Moles) ======================================= dGdL = logX - logY #-- Vector! #== Return Values ===================================================== return gfe2,dGdL
def setupCalcGRDCoefsP(iLiq, pRes, tRes, XY, clsEOS): nCom = clsEOS.NC iPhs = 0 pByT = pRes / tRes qP = True #-- Onlt want Pres-derivatives, not Temp or Comp qT = False qX = False #-- Calculate the log(Fugacity) Coefficients ------------------------ FiC, FiP, dumV, dumX = CE.calcPhaseFugPTX(iPhs, qP, qT, qX, pRes, tRes, XY, clsEOS) #-- EoS B[i] Coefficients ------------------------------------------- BiC = pByT * clsEOS.bI #== Augment with the Volume Shift Parameters ========================== for iC in range(nCom): cCor = BiC[iC] * clsEOS.gPP("SS", iC) #-- bi*p/(RT)*si = Bi*si FiC[iC] = pRes * XY[iC] * exp(FiC[iC]) #-- log(phi) -> Fugacity FiP[iC] = FiC[iC] * (FiP[iC] + 1.0 / pRes) #-- dFug/dP FiC[iC] = FiC[iC] * exp(-cCor) #-- Volume Shift Corr FiP[iC] = FiP[iC] * exp(-cCor) * (1.0 - cCor / pRes ) #-- Volume Shift Corr #== Return values ===================================================== return FiC, FiP
def stabMini(self, alfa, *args): iLiq = 0 qP = False #-- No Pres, Temp or Comp Derivatives Needed qT = False qX = False #== Unpack the arguments in *args ===================================== pRes = args[0] tRes = args[1] Z = args[2] h = args[3] clsEOS = args[4] #== Un-Normalised & Normalised Mole Fractions ========================= Y = 0.25 * alfa * alfa y, SY = UT.NormSum(Y) #-- log(fugacity) coefficients of Trial phase ----------------------- fugY,dumV,dumV,dumM = \ CE.calcPhaseFugPTX(iLiq,qP,qT,qX,pRes,tRes,y,clsEOS) #== Gstar and Gradient-Vector ========================================= logY = NP.log(Y) #-- Vector dGdY = logY + fugY - h #-- Vector gStr = 1.0 - SY + NP.dot(Y, dGdY) #-- Michelsen Eqn.(14) dGda = 0.5 * alfa * dGdY #-- Michelsen Eqn.(25) #== Michelsen Approach to Triviality: Eqn.(23) & (24) ================= YmZ = Y - Z beta = NP.dot(YmZ, dGdY) r = 2.0 * gStr / beta if gStr < 1.0E-3 and abs(r - 1.0) < 0.2: self.isTriv = True #print("gStr,r,Triv {:10.3e} {:10.3e} {:d}".format(gStr,r,self.isTriv)) #== Return function and its derivative (as tuple) ===================== return gStr, dGda
def setupCalcGRDCoefsP(iLiq, pRes, tRes, XY, clsEOS): nCom = clsEOS.nComp iPhs = 0 pByT = pRes / tRes recP = 1.0 / pRes qP = True #-- Onlt want Pres-derivatives, not Temp or Comp qT = False qX = False #-- Calculate the log(Fugacity) Coefficients ------------------------ f2, f2P, dumV, dumX = CE.calcPhaseFugPTX(iPhs, qP, qT, qX, pRes, tRes, XY, clsEOS) f2X = NP.exp(f2) #-- Vec: exp(lnPhi) #-- EoS B[i] Coefficients ------------------------------------------- BI = pByT * clsEOS.bI #-- Vec: Bi*(p/RT) cI = BI * clsEOS.vS #-- Vec: Ci = si*Bi XI = NP.exp(cI) #-- Vec: exp(Ci) cIP = cI * recP #-- Vec: Ci/p #== 3-Parameter Fugacity and its P-Derivative ========================= f3 = pRes * XY * f2X * XI #-- Vec: p*xi*exp(lnPhi)*exp(Ci) f3P = recP + f2P + cIP f3P = f3 * f3P #== Return values ===================================================== return f3, f3P
def twoSidedStabCheck(qSat, pRes, tRes, Z, clsEOS, clsIO): nCom = clsEOS.nComp qP = False #-- No Pres, Temp or Comp Derivatives Needed qT = False qX = False if clsIO.Deb["STAB2"] > 0: qDeb = True fDeb = clsIO.fDeb else: qDeb = False #-- Feed Log(Fugacity) Coefficients --------------------------------- iLiq = 0 fugZ,dumV,dumV,dumM = \ CE.calcPhaseFugPTX(iLiq,qP,qT,qX,pRes,tRes,Z,clsEOS) #-- Wilson K-Values ------------------------------------------------- wilK = UT.wilsonK(pRes, tRes, clsEOS) #-- Trial Compositions ---------------------------------------------- lnKV = NP.log(wilK) lnKL = -lnKV #-- Assume Feed is Liquid, Try to Split-Off Vapour ------------------ iVap = -1 iSTV,sumV,gVap,tolV,triV,lnKV = \ oneSidedStabCheck(iVap,pRes,tRes,Z,fugZ,lnKV,clsEOS,clsIO) qVap = gVap < 0.0 and triV > 1.0E-04 if qDeb: sOut = "Vap-Like Split: pRes,tRes,iSTV,gVap,tolV,triV {:10.3f} {:8.3f} {:3d} {:10.3e} {:10.3e} {:10.3e}\n".format( pRes, tRes, iSTV, gVap, tolV, triV) fDeb.write(sOut) #KV = NP.exp(lnKV) #WO.writeArrayDebug(fDeb,KV,"twoSidedStab: Wilson-KV") #-- Assume Feed is Liquid, Try to Split-Off Vapour ------------------ iLiq = 1 iSTL,sumL,gLiq,tolL,triL,lnKL = \ oneSidedStabCheck(iLiq,pRes,tRes,Z,fugZ,lnKL,clsEOS,clsIO) qLiq = gLiq < 0.0 and triL > 1.0E-04 if qDeb: sOut = "Liq-Like Split: pRes,tRes,iSTL,gLiq,tolL,triL {:10.3f} {:8.3f} {:3d} {:10.3e} {:10.3e} {:10.3e}\n".format( pRes, tRes, iSTL, gLiq, tolL, triL) fDeb.write(sOut) #KL = NP.exp(lnKL) #WO.writeArrayDebug(fDeb,KL,"twoSidedStab: Wilson-KL") #== Process the 1-Sided Tests ========================================= #print("2Stab: pRes,tRes,gLiq,triL,gVap,triV {:10.3f} {:8.3f} {:10.3e} {:10.3e} {:10.3e} {:10.3e}".format(pRes,tRes,gLiq,triL,gVap,triV)) if qLiq and qVap: difK = lnKL - lnKV #-- Vector triV = NP.dot(difK, difK) if triV > 1.0E-04: #-- Are the solutions distinct? iTyp = -3 if qSat: if gLiq < gVap: #-- Called from Psat - Liquid Lower-GFE bstK = NP.exp(lnKL) bstT = triL iTyp = -1 else: #-- Called from Psat - Vapour Lower-GFE bstK = NP.exp(lnKV) bstT = triV iTyp = -2 else: #-- Called from Flash - Take Ratio bstK = NP.exp(difK) bstT = 0.0 elif lnKV[nCom - 1] < 0.0: #-- logK-Vap 'Right' Way-Up iTyp = -2 bstK = NP.exp(lnKV) bstT = triV else: #-- LogK-Liq must be 'Right' Way-Up iTyp = -1 bstK = NP.exp(lnKL) bstT = triL elif qVap: #-- Only Vapour-Like Unstable iTyp = 2 bstK = NP.exp(lnKV) bstT = triV elif qLiq: #-- Only Liquid-Like Unstable iTyp = 1 bstK = NP.exp(lnKL) bstT = triL else: #-- Is Stable 1-Phase at this (p,T) iTyp = 0 bstK = NP.zeros(nCom) bstT = 0.0 #== Ensure the K-Values are the "Right" Way Up ======================== if not qSat and bstK[nCom - 1] > 1.0: bstK = NP.divide(1.0, bstK) #print("Stab2S: Flipped K-values at iTyp,pRes,tRes {:2d} {:10.3f} {:8.3f}".format(iTyp,pRes,tRes)) iTyp = abs(iTyp) return iTyp, bstT, bstK
def stab2SidedBFGS(qSat, pRes, tRes, Z, clsEOS, clsIO): if clsIO.Deb["STAB2"] > 0: qDeb = True fDeb = clsIO.fDeb else: qDeb = False nCom = clsEOS.nComp qP = False #-- No Pres, Temp or Comp Derivatives Needed qT = False qX = False iNeu = 0 iLiq = 1 iVap = -1 #-- Feed Log(Fugacity) Coefficients --------------------------------- fugZ,dumV,dumV,dumM = \ CE.calcPhaseFugPTX(iNeu,qP,qT,qX,pRes,tRes,Z,clsEOS) hVec = NP.log(Z) + fugZ #-- Vector! #-- Wilson K-Values ------------------------------------------------- wilK = UT.wilsonK(pRes, tRes, clsEOS) #-- Trial Compositions ---------------------------------------------- yVap = Z * wilK #-- Vector: Vapour-Like Trial yLiq = Z / wilK #-- Vector: Liquid-Like Trial #== Create (lower,upper) bounds for the alpha's ===================== bndA = [] for iC in range(nCom): bndA.append((1.0E-20, None)) #== Test SciPy Minimize Routine ===================================== aVap = 2.0 * NP.sqrt(yVap) qConV, yVap = stabDriv(iVap, aVap, bndA, pRes, tRes, Z, hVec, clsEOS) aLiq = 2.0 * NP.sqrt(yLiq) qConL, yLiq = stabDriv(iLiq, aLiq, bndA, pRes, tRes, Z, hVec, clsEOS) #print("qConL,yLiq ",qConL,yLiq) #print("qConV,yVap ",qConL,yVap) #== Process the 1-Sided Tests ========================================= if qConL and qConV: #-- If both trials unstable, take ratio unless they are the same ---- iTyp = 3 bstK = yVap / yLiq qSam = True for iC in range(nCom): if abs(bstK[iC] - 1.0) > 1.0E-03: qSam = False break if qSam: #print("iTyp=3 and All K = 1") if gStrV < gStrL: bstK = yVap / Z #-- Vector else: bstK = Z / yLiq #-- Vector elif qConV: iTyp = 2 bstK = yVap / Z elif qConL: iTyp = 1 bstK = Z / yLiq else: iTyp = 0 bstK = NP.ones(nCom) #== Ensure the K-Values are the "Right" Way Up ======================== if bstK[nCom - 1] > 1.0: bstK = 1.0 / bstK #-- Vector if qDeb: fDeb.write("Stab2: iTyp " + str(iTyp) + "\n") bstT = 0.0 return iTyp, bstT, bstK
def oneSidedStabCheck(iPhs, pRes, tRes, Z, fugZ, logK, clsEOS, clsIO): mSTB = 101 nCom = clsEOS.nComp qP = False #-- No Pres, Temp or Comp Derivatives Needed qT = False qX = False res0 = NP.zeros(nCom) iSTB = 0 if clsIO.Deb["STAB1"] > 0: qDeb = True fDeb = clsIO.fDeb else: qDeb = False if qDeb > 0: sOut = "1-Sided SC at pRes,tRes {:10.3f} {:8.3f}\n".format(pRes, tRes) K = NP.exp(logK) WO.writeArrayDebug(fDeb, K, "1SidedStab: K") WO.writeArrayDebug(fDeb, Z, "1SidedStab: Z") fDeb.write(sOut) #== Successive Substitution/GDEM Loop ================================= while iSTB < mSTB: iSTB += 1 #-- Mole Numbers ---------------------------------------------------- K = NP.exp(logK) yMol = Z * K #-- Mole composition (normalised) ----------------------------------- yNor, sumY = UT.NormSum(yMol) fugY,dumV,dumV,dumM = \ CE.calcPhaseFugPTX(iPhs,qP,qT,qX,pRes,tRes,yNor,clsEOS) gStr = 1.0 - sumY #-- Residuals ------------------------------------------------------- res1 = res0 res0 = fugZ - fugY - logK tolR = NP.dot(res0, res0) gStr = gStr - NP.dot(yMol, res0) #-- GDEM Step? ------------------------------------------------------ if iSTB % UT.mGDEM1 > 0: eigV = 1.0 else: eigV = UT.GDEM1(res0, res1, clsIO) if eigV < 1.0: eigV = 1.0 #-- Update logK with/without GDEM Acceleration ---------------------- eVr0 = eigV * res0 logK = logK + eVr0 triV = NP.dot(logK, logK) if qDeb: sOut = "Stab1S: iSTB,sumY,gStr,tolR,triV,eigV {:3d} {:10.3e} {:10.3e} {:10.3e} {:10.3e} {:8.4f}\n" \ .format(iSTB,sumY,gStr,tolR,triV,eigV) fDeb.write(sOut) #K = NP.exp(logK) #WO.writeArrayDebug(fDeb,K,"1SidedStab: K") #-- Trivial or Converged? ------------------------------------------- if triV < 1.0E-04: break elif tolR < 1.0E-12: break #== Return the Gstar ================================================== if qDeb > 0: WO.writeArrayDebug(fDeb, K, "End 1S-SC: K") return iSTB, sumY, gStr, tolR, triV, logK
def calcPsatBFGS(pEst,tRes,Z,clsEOS,clsIO) : nCom = clsEOS.nComp iNeu = 0 #-- Minimum and Maximum Pressure(in psia) to consider for Psat ------ p2PH = 10.0 p1PH = 15010.0 #== The Sat-Type probably depends on the mol% of C7+ ================== mC7P = UT.moleFracC7P(Z,clsEOS) print("calcPsatBFGS: mC7P {:8.5f}".format(mC7P)) if mC7P > 0.125 : #-- Volatile oils have z(C7+) > 12.5% qBub = True iLiq = 1 iVap = -1 else : #-- Else is Gas Condensate qBub = False iLiq = -1 iVap = 1 print("iLiq,iVap ",iLiq,iVap) #== Initialise pSat and Liq/Vap Estimates ============================= #---------------------------------------------------------------------- # Pre-Sweep using trial Liquid using BFGS Stability Test #---------------------------------------------------------------------- qLOK = False qVOK = False nBel = 0 pBel = [] gBel = [] if pEst == None : pSat = (2.0*p2PH + p1PH)/3.0 #-- Bias towards low-P else : pSat = pEst while (p1PH - p2PH) > 100.0 : print("p2PH,pSat,p1PH {:10.3f} {:10.3f} {:10.3f}".format(p2PH,pSat,p1PH)) #-- Wilson-K Values at current (p,T) -------------------------------- wilK = UT.wilsonK(pSat,tRes,clsEOS) if qLOK : yLiq = NP.copy(yLOK) else : yLiq = NP.divide(Z,wilK) if qVOK : yVap = NP.copy(yVOK) else : yVap = NP.multiply(Z,wilK) #-- Feed Log Fugacity Coefficients ---------------------------------- fugZ,dumV,dumV,dumM = \ CE.calcPhaseFugPTX(iNeu,qP,qT,qX,pSat,tRes,Z,clsEOS) #-- Try to split a Liquid off what is assumed to be Vapour ---------- qConL,gStrL,yLiq = \ stabCheckBFGS(iLiq,pSat,tRes,Z,fugZ,yLiq,clsEOS) if qConL : qLOK = True yLOK = NP.copy(yLiq) #-- Try to split a Vapour off what is assumed to be Liquid ---------- qConV,gStrV,yVap = \ stabCheckBFGS(iVap,pSat,tRes,Z,fugZ,yVap,clsEOS) if qConV : qVOK = True yVOK = NP.copy(yVap) #-- Did either trial converge? -------------------------------------- if qConL or qConV : p2PH = pSat pBel.append(pSat) if qConL and qConV : gStr = min(gStrL,gStrV) elif qConL : gStr = gStrL elif qConV : gStr = gStrV gBel.append(gStr) nBel += 1 else : p1PH = pSat #-- Two or more solutions below gStr = 0 => pSat -------------------- if nBel >= 2 : dPrs = pBel[nBel-1] - pBel[nBel-2] dGst = gBel[nBel-1] - gBel[nBel-2] grad = dGst/dPrs intr = gBel[nBel-1] - pBel[nBel-1]*grad pSat = -intr/grad else : pSat = 0.5*(p2PH + p1PH) return qBub,pSat
def refineInTempOnly(betO, pRes, tRes, Z, K, clsEOS): nCom = clsEOS.nComp iLiq = 1 iVap = -1 #-- c-Vector: c = 1/(1-K[i]) ---------------------------------------- c = calcCvalues(K) logK = NP.log(K) #== Rachford-Rice Function G(beta) using required beta ================ G = 0.0 for iC in range(nCom): G = G + Z[iC] / (betO - c[iC]) #-- Liquid and Vapour Mole Numbers and Mole Fractions --------------- X, Y = calcLiqVapMoleFrac(betO, Z, K, c) sumX = NP.sum(X) sumY = NP.sum(Y) recX = 1.0 / sumX recY = 1.0 / sumY xNor = NP.multiply(recX, X) yNor = NP.multiply(recY, Y) #-- Liquid and Vapour Fugacities and their P-Derivatives ------------ qPD = False qTD = True qXD = False fugX, dumV, dXdT, dumM = CE.calcPhaseFugPTX(iLiq, qPD, qTD, qXD, pRes, tRes, xNor, clsEOS) fugY, dumV, dYdT, dumM = CE.calcPhaseFugPTX(iVap, qPD, qTD, qXD, pRes, tRes, yNor, clsEOS) #== Form residual and various sums ==================================== GF = 0.0 GFP = 0.0 fRes = NP.zeros(nCom) dfdT = NP.zeros(nCom) for iC in range(nCom): fRes[iC] = logK[iC] + fugY[iC] - fugX[iC] dfdT[iC] = dYdT[iC] - dXdT[iC] xyz = xNor[iC] * yNor[iC] / Z[iC] GF = GF + xyz * fRes[iC] GFP = GFP + xyz * dfdT[iC] #== Find delP ========================================================= Grhs = G - GF if abs(GFP) > UT.macEPS: delT = Grhs / GFP else: delT = 0.0 #-- If update takes us too far, cut-back ... ------------------------ if tRes + delT < 0.0: delT = 0.5 * delT #print("tRes,Grhs,GFP,delT {:10.3f} {:10.3e} {:10.3e} {:10.3e}".format(tRes,Grhs,GFP,delT)) tRes = tRes + delT #== Calculate d(lnK): Equation (14) =================================== for iC in range(nCom): dlnK = -fRes[iC] - delT * dfdT[iC] logK[iC] = logK[iC] + dlnK #== Return Information ================================================ K = NP.exp(logK) c = calcCvalues(K) return tRes, K, c
def findVapFracPT(alfa, betO, pRes, tRes, Z, lnKref, clsEOS): nCom = clsEOS.nComp iLiq = 1 iVap = -1 qPD = True qTD = True qXD = False #---------------------------------------------------------------------- # Iterate in (P,T) #---------------------------------------------------------------------- fSSQ = 1.0 while fSSQ > 1.0E-12: #== Solve Equation (20) to find theta ================================= theta, K, c = findTheta(betO, alfa, Z, lnKref) #-- Liquid and Vapour Mole Fractions -------------------------------- X, Y = calcLiqVapMoleFrac(betO, Z, K, c) #== Compute the Liquid & Vapour Fugacities and their (P,T)-Derivatives fugX, dXdP, dXdT, dumM = CE.calcPhaseFugPTX(iLiq, qPD, qTD, qXD, pRes, tRes, X, clsEOS) fugY, dYdP, dYdT, dumM = CE.calcPhaseFugPTX(iVap, qPD, qTD, qXD, pRes, tRes, Y, clsEOS) logX = NP.log(X) logY = NP.log(Y) #== Residuals and their (P,T)-Derivatives ============================= dRes = logY + fugY - logX - fugX #-- Vector dPrs = dYdP - dXdP #-- Vector dTem = dYdT - dXdT #-- Vector f1 = NP.dot(Y, dRes) f2 = NP.dot(X, dRes) df1dP = NP.dot(Y, dPrs) df2dP = NP.dot(X, dPrs) df1dT = NP.dot(Y, dTem) df2dT = NP.dot(X, dTem) #== Solve the 2x2 Matrix ============================================== dPrs, dTem = solve2x2(f1, f2, df1dP, df1dT, df2dP, df2dT) fSSQ = 0.5 * (dPrs * dPrs + dTem * dTem) pRes = pRes + dPrs tRes = tRes + dTem if pRes < 0.0 or tRes < 0.0: break #== Return Information ================================================ return pRes, tRes
def updatePTK(pRes, tRes, Z, Ze, logK, fcP2e, clsEOS, clsIO): nCom = clsEOS.nComp iNeu = 0 qPD = True qTD = True qXD = False #-- Trial composition ----------------------------------------------- K = NP.exp(logK) Y = Z * K #-- fc+1: Equation (11) --------------------------------------------- sumY = NP.sum(Y) fcP1 = sumY - 1.0 #print("sumY {:10.5f}".format(sumY)) #-- fc+2: Equation (13) --------------------------------------------- fcP2 = -fcP2e + NP.dot(Ze, logK) #-- Liquid and Vapour Fugacities and their P-Derivatives ------------ yNor = Y / sumY #-- Vector fugZ, dZdP, dZdT, dumM = CE.calcPhaseFugPTX(iNeu, qPD, qTD, qXD, pRes, tRes, Z, clsEOS) fugY, dYdP, dYdT, dumM = CE.calcPhaseFugPTX(iNeu, qPD, qTD, qXD, pRes, tRes, yNor, clsEOS) #== Construct Jacobian and RHS terms ================================== fRes = fugY - fugZ #-- All Vector Operations dfdP = dYdP - dZdP dfdT = dYdT - dZdT fRes = logK + fRes sumf1 = NP.dot(Y, fRes) sumf2 = NP.dot(Ze, fRes) df1dP = NP.dot(Y, dfdP) df2dP = NP.dot(Ze, dfdP) df1dT = NP.dot(Y, dfdT) df2dT = NP.dot(Ze, dfdT) #== RHS-Terms ========================================================= fcP1 = fcP1 - sumf1 fcP2 = fcP2 - sumf2 #== Invert (2x2) Matrix =============================================== dPrs, dTem = solve2x2(fcP1, fcP2, df1dP, df1dT, df2dP, df2dT) fSSQ = 0.5 * (dPrs * dPrs + dTem * dTem) fDeb = clsIO.fDeb sOut = "fcP1,fcP2,fSSQ,dPrs,dTem {:10.3e} {:10.3e} {:10.3e} {:10.3e} {:10.3e}\n".\ format(fcP1,fcP2,fSSQ,dPrs,dTem) fDeb.write(sOut) #== P,T & logK-Updates ================================================ pRes = pRes + dPrs tRes = tRes + dTem for iC in range(nCom): dlnK = -fRes[iC] - dPrs * dfdP[iC] - dTem * dfdT[iC] sOut = "iC,lnK,dlnK {:2d} {:10.3e} {:10.3e}\n".format( iC, logK[iC], dlnK) fDeb.write(sOut) logK[iC] = logK[iC] + dlnK #== Return information ================================================ return fSSQ, pRes, tRes, logK
def calcTsat(pRes,clsEOS,clsSAM,clsIO) : nCom = clsEOS.NC mTSA = 101 if clsIO.Deb["TSAT"] > 0 : qDeb = True fDeb = clsIO.fDeb else : qDeb = False #-- Load Feed Composition ------------------------------------------- Z = NP.zeros(nCom) for iC in range(nCom) : Z[iC] = clsSAM.gZI(iC) #== Pre-Sweep; 2-Phase/1-Phase Temperature Bounds ===================== t2PH,t1PH,logK = boundTsat(pRes,Z,clsEOS,clsIO) tRes = t2PH #-- This will be our iteration parameter if qDeb : sOut = "calcTsat: pRes,t2PH,t1PH {:8.3f} {:10.3f} {:10.3f}\n".format(pRes,t2PH,t1PH) fDeb.write(sOut) K = NP.exp(logK) WO.writeArrayDebug(fDeb,K,"Post-boundTsat: K") #---------------------------------------------------------------------- # Main Iterative Loop #---------------------------------------------------------------------- iTSA = 0 iPhs = 0 qPD = False qTD = True qXD = False K = NP.exp(logK) yMol = Z*K while iTSA < mTSA : iTSA += 1 if iTSA > 1 : res1 = res0 #-- Copy 'last' residual for GDEM #== Log Fug Coefs and their T-derivatives for Feed and Trial Phase ==== fugZ,dZdP,dZdT,dumM = CE.calcPhaseFugPTX(iPhs,qPD,qTD,qXD,pRes,tRes,Z,clsEOS) #-- Get Normalised Trial Composition -------------------------------- yNor = UT.Norm(yMol) fugY,dYdP,dYdT,dumM = CE.calcPhaseFugPTX(iPhs,qPD,qTD,qXD,pRes,tRes,yNor,clsEOS) #== Residual and Derivative Difference ================================ res0 = fugZ - fugY - logK #-- Vector dFdT = dYdT - dZdT #-- Vector #== Calculate tol2 ===================================================== tol2 = calcTOL2(logK,res0) resX = NP.exp(res0) #== Is this a GDEM Step? If so, calculate Single Eigenvalue ========== if iTSA % CO.mGDEM1 > 0 : eigV = 1.0 else : eigV = UT.GDEM1(res0,res1,clsIO) #== Perform Update; Calculate Various Sums ============================ yMol = yMol*resX #-- W&B Eqn.(4.83) with lambda=1 dQdT = NP.dot(yMol,dFdT) #-- W&B Eqn.(4.86) qFun = NP.sum(yMol) #-- W&B Eqn.(4.87a) wrk0 = eigV*res0 #-- W&B Eqn.(4.83) with any lambda logK = logK + wrk0 #-- Test for Trivial Solution [all K's -> 1] ------------------------ triV = NP.dot(logK,logK) #-- W&B Eqn.(4.88) #== SS or GDEM step? If GDEM, re-compute Yi afresh =================== if eigV == 1.0 : sumY = qFun else : K = NP.exp(logK) yMol = Z*K sumY = NP.sum(yMol) if qDeb : WO.writeArrayDebug(fDeb,K,"Post-GDEM: K") qFun = 1.0 - qFun tol1 = abs(1.0 - sumY) #-- W&B Eqn.(4.87a) #== Pressure Update (Newton) ========================================== dTem = - qFun/dQdT tOld = tRes tRes = tRes + dTem #-- W&B Eqn.(4.85) if qDeb : sOut = "iTSA,qFun,dQdT,to11,tol2,triV,eigV,dTem,tRes {:3d} {:10.3e} {:10.3e} {:10.3e} {:10.3e} {:10.3e} {:10.3e} {:10.3e} {:10.4f}\n"\ .format(iTSA,qFun,dQdT,tol1,tol2,triV,eigV,dTem,tRes) fDeb.write(sOut) #== Converged? ======================================================== if tol1 < 1.0E-12 and tol2 < 1.0E-08 : #-- Hard to acheive 1E-13 qCon = True break #== Trivial Solution? ================================================= if triV < 1.0E-04 : qTrv = True print("calcPsat: Trivial Solution?") break #======================================================================== # Converged: Calculate K-Values #======================================================================== Ksat = NP.exp(logK) #== Ensure the K-Values are the 'Right-Way-Up' ======================== if Ksat[nCom-1] > 1.0 : qBub = False #qBub = not qBub #Ksat = NP.divide(1.0,Ksat) if qDeb : sOut = "End-calcTsat: K-Values NOT Flipped\n" fDeb.write(sOut) else : qBub = True if qDeb : sOut = "End-calcTsat: K-Values OK\n" fDeb.write(sOut) #== Return values ===================================================== return qBub,tRes,Ksat