00001
00002 #include <cmath>
00003 #include <iostream>
00004 #include <iomanip>
00005
00006
00007 #include "flash.h"
00008 #include "flashessential.h"
00009
00010 namespace Flash
00011 {
00012
00013 struct ThetaEquation
00014 {
00015 double zNaCl, zH2O, A, a, b, c;
00016 double operator()(double theta) { return (A * theta/(zH2O * theta + zNaCl * (A - 1)) - theta - 1) * std::exp(b * theta + c * theta*theta) - a; }
00017 };
00018
00019
00020 struct ThetaDerivativeEquation
00021 {
00022 double zNaCl, zH2O, A, a, b, c;
00023 double operator()(double theta)
00024 {
00025
00026 double den = zH2O * theta + zNaCl * (A - 1);
00027
00028 return expf(b * theta + c * theta * theta) *
00029 ( (A/den - A * theta * zH2O/(den * den) - 1) + (A * theta/den - theta - 1) * (b + 2 * c * theta) );
00030 }
00031 };
00032
00033
00034 double ComputeTheta(double zNaCl, double zH2O, double A, double a, double b, double c)
00035 {
00036
00037 double theta0 = zNaCl/zH2O;
00038
00039 ThetaEquation function;
00040
00041 ThetaDerivativeEquation derivative;
00042
00043 function.zNaCl = zNaCl; derivative.zNaCl = zNaCl;
00044 function.zH2O = zH2O; derivative.zH2O = zH2O;
00045 function.A = A; derivative.A = A;
00046 function.a = a; derivative.a = a;
00047 function.b = b; derivative.b = b;
00048 function.c = c; derivative.c = c;
00049
00050 return NewtonRaphson(function, derivative, theta0);
00051 }
00052
00053 void FlashBrineCO2H2S(BrineCO2H2SData& data, const BrineCO2Data& co2BrineData, double T, double P, double zH2O, double zCO2, double zH2S)
00054 {
00055
00056 data.stateCO2 = co2BrineData.stateCO2;
00057 data.stateH2S = DeterminePhysicalStateH2S(T, P);
00058
00059
00060 bool isH2SFullySolubilized;
00061
00062
00063 bool isThereAqueousPhase = (co2BrineData.betaA != 0.0);
00064
00065
00066 if(isThereAqueousPhase)
00067 {
00068
00069 double mNaCl = 55.508 * co2BrineData.xNaCl/co2BrineData.xH2O;
00070
00071
00072 double mCO2 = 55.508 * co2BrineData.xCO2/co2BrineData.xH2O;
00073
00074
00075 double saturatedMolalityH2S = (P - ComputeSaturatedPressureH2O(T))/std::exp(uRTH2S(T, P) - std::log(ComputeFugacityH2S(ComputeVolumeH2S(T, P, data.stateH2S), T, P)) + 2 * lambdaH2S(T, P) * mNaCl + zetaH2S * mNaCl * mNaCl);
00076
00077
00078 double mH2SFullySolubilized = 55.508 * zH2S/zH2O;
00079
00080
00081 double mH2S;
00082
00083
00084 if(mH2SFullySolubilized < saturatedMolalityH2S)
00085 {
00086 mH2S = mH2SFullySolubilized;
00087 isH2SFullySolubilized = true;
00088 }
00089 else
00090 {
00091 mH2S = saturatedMolalityH2S;
00092 isH2SFullySolubilized = false;
00093 }
00094
00095
00096 data.xH2O = 55.508/(55.508 + mNaCl + mCO2 + mH2S);
00097 data.xNaCl = data.xH2O * mNaCl/55.508;
00098 data.xCO2 = data.xH2O * mCO2/55.508;
00099 data.xH2S = 1 - data.xNaCl - data.xH2O - data.xCO2;
00100 }
00101 else
00102 {
00103
00104 data.xH2O = 0.0;
00105 data.xNaCl = 0.0;
00106 data.xCO2 = 0.0;
00107 data.xH2S = 0.0;
00108
00109
00110 isH2SFullySolubilized = false;
00111 }
00112
00113
00114 bool isThereCO2RichPhase = (co2BrineData.betaC != 0.0);
00115
00116
00117 bool isThereH2SRichPhase = (data.stateCO2 == LIQUID || data.stateH2S == LIQUID) && !isH2SFullySolubilized;
00118
00119
00120 data.betaS = co2BrineData.betaS * (1.0 - zH2S);
00121 data.betaA = co2BrineData.betaA * (1.0 - zH2S)/(1 - data.xH2S);
00122
00123 if(isThereH2SRichPhase)
00124 {
00125 data.betaC = co2BrineData.betaC * (1.0 - zH2S);
00126 data.betaH = 1.0 - data.betaS - data.betaA - data.betaC;
00127 }
00128 else
00129 {
00130 data.betaC = 1.0 - data.betaS - data.betaA;
00131 data.betaH = 0.0;
00132 }
00133
00134
00135 if(isThereCO2RichPhase)
00136 {
00137 data.yH2S = (isThereH2SRichPhase) ? 0.0 : (zH2S - data.xH2S * data.betaA)/data.betaC;
00138 data.yCO2 = (zCO2 - data.xCO2 * data.betaA)/data.betaC;
00139 data.yH2O = 1 - data.yCO2 - data.yH2S;
00140 }
00141 else
00142 {
00143 data.yH2S = 0.0;
00144 data.yCO2 = 0.0;
00145 data.yH2O = 0.0;
00146 }
00147 }
00148
00149 void ProgressiveFlashBrineCO2(BrineCO2Data& data, double T, double P, double zNaCl, double zH2O, double zCO2, unsigned int numCorrections)
00150 {
00151
00152 data.stateCO2 = DeterminePhysicalStateCO2(T, P);
00153
00154
00155 double v = ComputeVolumeCO2(T, P, data.stateCO2);
00156
00157
00158 double A = koH2O(T)/(ComputeFugacityH2O(v, T, P) * P) * exp((P - Po)/(R * T) * vH2O);
00159 double B = ComputeFugacityCO2(v, T, P) * P/(koCO2(T, data.stateCO2) * 55.508) * exp((Po - P)/(R * T) * vCO2(data.stateCO2));
00160
00161
00162 double lambda = lambdaCO2(T, P);
00163 double zeta = zetaCO2(T, P);
00164
00165
00166 double mNaClSaturated = ComputeSaturatedMolalityNaCl(T, P);
00167
00168
00169 double mNaClFullySolubilized = 55.508 * zNaCl/zH2O;
00170
00171
00172 double mCO2FullySolubilized = 55.508 * zCO2/zH2O;
00173
00174
00175 bool isThereSolidPhase, isThereAqueousPhase, isThereCO2RichPhase;
00176
00177
00178 double mNaCl, mCO2;
00179
00180
00181 if(mNaClFullySolubilized < mNaClSaturated)
00182 {
00183 mNaCl = mNaClFullySolubilized;
00184 isThereSolidPhase = false;
00185 }
00186 else
00187 {
00188 mNaCl = mNaClSaturated;
00189 isThereSolidPhase = true;
00190 }
00191
00192
00193 const double commonFactor = 55.508 * (1.0 - A)/(1.0 - B) * B;
00194
00195
00196 for(unsigned int counter = 0; counter < numCorrections; ++counter)
00197 {
00198
00199 double mCO2Saturated = commonFactor * std::exp(- mNaCl * (2.0 * lambda + zeta * mNaCl));
00200
00201
00202 if(mCO2FullySolubilized < mCO2Saturated)
00203 {
00204 mCO2 = mCO2FullySolubilized;
00205 isThereCO2RichPhase = false;
00206 }
00207 else
00208 {
00209 mCO2 = mCO2Saturated;
00210 isThereCO2RichPhase = true;
00211 }
00212
00213
00214 if(isThereCO2RichPhase)
00215 {
00216
00217 double xH2O = 55.508/(55.508 + mNaCl + mCO2);
00218 double xCO2 = mCO2/(55.508 + mNaCl + mCO2);
00219
00220
00221 double yH2O = A * xH2O;
00222 double yCO2 = 1.0 - yH2O;
00223
00224
00225 isThereAqueousPhase = ((yCO2 * zH2O - yH2O * zCO2)/(yCO2 * xH2O - yH2O * xCO2) > 0.0) ? true : false;
00226 }
00227 else
00228 {
00229
00230 isThereAqueousPhase = true;
00231 }
00232
00233
00234 if(isThereAqueousPhase && isThereCO2RichPhase && !isThereSolidPhase)
00235 {
00236 data.xNaCl = mNaCl/(55.508 + mNaCl + mCO2);
00237 data.xCO2 = mCO2/(55.508 + mNaCl + mCO2);
00238 data.xH2O = 1.0 - data.xNaCl - data.xCO2;
00239
00240 data.yH2O = A * data.xH2O;
00241 data.yCO2 = 1.0 - data.yH2O;
00242
00243 data.betaA = (data.yCO2 * zH2O - data.yH2O * zCO2)/(data.yCO2 * data.xH2O - data.yH2O * data.xCO2);
00244 data.betaC = 1.0 - data.betaA;
00245 data.betaS = 0.0;
00246
00247
00248 double mNaClFullySolubilizedCorrected = mNaClFullySolubilized * (1.0 + A * data.betaC/data.betaA);
00249
00250 if(mNaClFullySolubilizedCorrected < mNaClSaturated)
00251 {
00252 mNaCl = mNaClFullySolubilizedCorrected;
00253 isThereSolidPhase = false;
00254 }
00255 else
00256 {
00257 mNaCl = mNaClSaturated;
00258 isThereSolidPhase = true;
00259 }
00260 }
00261
00262 else if(isThereAqueousPhase && isThereCO2RichPhase && isThereSolidPhase)
00263 {
00264 data.xNaCl = mNaCl/(55.508 + mNaCl + mCO2);
00265 data.xCO2 = mCO2/(55.508 + mNaCl + mCO2);
00266 data.xH2O = 1.0 - data.xNaCl - data.xCO2;
00267
00268 data.yH2O = A * data.xH2O;
00269 data.yCO2 = 1.0 - data.yH2O;
00270
00271 data.betaA = (data.yCO2 * zH2O - data.yH2O * zCO2)/(data.yCO2 * data.xH2O - data.yH2O * data.xCO2);
00272 data.betaC = (data.xCO2 * zH2O - data.xH2O * zCO2)/(data.xCO2 * data.yH2O - data.xH2O * data.yCO2);
00273 data.betaS = 1.0 - data.betaA - data.betaC;
00274
00275 break;
00276 }
00277
00278 else if(isThereAqueousPhase && isThereSolidPhase && !isThereCO2RichPhase)
00279 {
00280 data.xNaCl = mNaCl/(55.508 + mNaCl + mCO2);
00281 data.xCO2 = mCO2/(55.508 + mNaCl + mCO2);
00282 data.xH2O = 1.0 - data.xNaCl - data.xCO2;
00283
00284 data.yH2O = 0.0;
00285 data.yCO2 = 0.0;
00286
00287 data.betaA = zH2O/data.xH2O;
00288 data.betaS = 1.0 - data.betaA;
00289 data.betaC = 0.0;
00290
00291 break;
00292 }
00293
00294 else if(isThereCO2RichPhase && !isThereAqueousPhase)
00295 {
00296 data.xNaCl = 0.0;
00297 data.xCO2 = 0.0;
00298 data.xH2O = 0.0;
00299
00300 data.yH2O = zH2O/(1.0 - zNaCl);
00301 data.yCO2 = 1.0 - data.yH2O;
00302
00303 data.betaS = zNaCl;
00304 data.betaC = 1.0 - zNaCl;
00305 data.betaA = 0.0;
00306
00307 break;
00308 }
00309
00310 else
00311 {
00312 data.xNaCl = zNaCl;
00313 data.xCO2 = zCO2;
00314 data.xH2O = zH2O;
00315
00316 data.yH2O = 0.0;
00317 data.yCO2 = 0.0;
00318
00319 data.betaA = 1.0;
00320 data.betaS = 0.0;
00321 data.betaC = 0.0;
00322
00323 break;
00324 }
00325 }
00326 }
00327
00328 void GeometricFlashBrineCO2(BrineCO2Data& data, double T, double P, double zNaCl, double zH2O, double zCO2)
00329 {
00330
00331 data.stateCO2 = DeterminePhysicalStateCO2(T, P);
00332
00333
00334 double v = ComputeVolumeCO2(T, P, data.stateCO2);
00335
00336
00337 double A = koH2O(T)/(ComputeFugacityH2O(v, T, P) * P) * std::exp((P - Po)/(R * T) * vH2O);
00338 double B = ComputeFugacityCO2(v, T, P) * P/(koCO2(T, data.stateCO2) * 55.508) * std::exp((Po - P)/(R * T) * vCO2(data.stateCO2));
00339
00340
00341 double lambda = lambdaCO2(T, P);
00342 double zeta = zetaCO2(T, P);
00343
00344
00345 double mNaClSaturated = ComputeSaturatedMolalityNaCl(T, P);
00346
00347
00348 double mCO2SaturatedBrine = 55.508 * (1.0 - A)/(1.0 - B) * B * std::exp(- mNaClSaturated * (2.0 * lambda + zeta * mNaClSaturated));
00349
00350
00351 double xNaClStar = mNaClSaturated/(55.508 + mNaClSaturated + mCO2SaturatedBrine);
00352 double xCO2Star = mCO2SaturatedBrine/(55.508 + mNaClSaturated + mCO2SaturatedBrine);
00353 double yH2OStar = A * (1 - xNaClStar - xCO2Star);
00354
00355
00356 double s[3] = {1.0, 0.0, 0.0};
00357 double x[3] = {xNaClStar, 1 - xNaClStar - xCO2Star, xCO2Star};
00358 double y[3] = {0.0, yH2OStar, 1 - yH2OStar};
00359 double z[3] = {zNaCl, zH2O, zCO2};
00360
00361
00362 if(((x[0] - y[0]) * (z[1] - y[1]) - (x[1] - y[1]) * (z[0] - y[0])) > 0)
00363 {
00364
00365 double mNaClA = 55.508 * zNaCl/zH2O;
00366 double mCO2A = 55.508 * zCO2/zH2O;
00367
00368
00369 double mCO2SaturatedA = 55.508 * (1.0 - A)/(1.0 - B) * B * std::exp(- mNaClA * (2.0 * lambda + zeta * mNaClA));
00370
00371
00372 if(mCO2A < mCO2SaturatedA)
00373 {
00374 data.xNaCl = zNaCl;
00375 data.xH2O = zH2O;
00376 data.xCO2 = zCO2;
00377 data.yH2O = 0;
00378 data.yCO2 = 0;
00379 data.betaS = 0;
00380 data.betaA = 1;
00381 data.betaC = 0;
00382 }
00383
00384 else
00385 {
00386
00387 if(zNaCl <= 0.0001)
00388 {
00389 data.xH2O = (1.0 - B)/(1.0 - A * B);
00390 data.xCO2 = 1.0 - data.xH2O;
00391 data.xNaCl = 0.0;
00392 }
00393 else
00394 {
00395
00396 double a = (1.0 - A)/(1.0 - B) * B;
00397 double b = 111.016 * lambda;
00398 double c = 3081.138064 * zeta;
00399
00400
00401 double theta = ComputeTheta(zNaCl, zH2O, A, a, b, c);
00402
00403 data.xH2O = (zNaCl * (A - 1) + zH2O * theta)/(A * theta);
00404 data.xNaCl = data.xH2O * theta;
00405 data.xCO2 = 1 - data.xNaCl - data.xH2O;
00406 }
00407
00408
00409 data.yH2O = A * data.xH2O;
00410 data.yCO2 = 1 - data.yH2O;
00411 data.betaS = 0;
00412 data.betaA = (data.yCO2 * zH2O - data.yH2O * zCO2)/(data.yCO2 * data.xH2O - data.yH2O * data.xCO2);
00413 data.betaC = 1 - data.betaA;
00414 }
00415 }
00416
00417 else if(((s[0] - x[0]) * (z[1] - x[1]) - (s[1] - x[1]) * (z[0] - x[0])) > 0)
00418 {
00419 data.xH2O = 1.0/(mNaClSaturated/55.508 + zCO2/zH2O + 1);
00420 data.xNaCl = data.xH2O * mNaClSaturated/55.508;
00421 data.xCO2 = 1 - data.xNaCl - data.xH2O;
00422 data.yH2O = 0;
00423 data.yCO2 = 0;
00424 data.betaS = 1 - zH2O/data.xH2O;
00425 data.betaA = 1 - data.betaS;
00426 data.betaC = 0;
00427 }
00428
00429 else if(((y[0] - s[0]) * (z[1] - s[1]) - (y[1] - s[1]) * (z[0] - s[0])) > 0)
00430 {
00431 data.xH2O = 0;
00432 data.xNaCl = 0;
00433 data.xCO2 = 0;
00434 data.yH2O = zH2O/(zH2O + zCO2);
00435 data.yCO2 = 1 - data.yH2O;
00436 data.betaS = zNaCl;
00437 data.betaA = 0;
00438 data.betaC = 1 - data.betaS;
00439 }
00440
00441 else
00442 {
00443 data.xNaCl = xNaClStar;
00444 data.xCO2 = xCO2Star;
00445 data.xH2O = 1 - xNaClStar - xCO2Star;
00446 data.yH2O = yH2OStar;
00447 data.yCO2 = 1 - yH2OStar;
00448 data.betaA = (data.yCO2 * zH2O - data.yH2O * zCO2)/(data.yCO2 * data.xH2O - data.yH2O * data.xCO2);
00449 data.betaC = (data.xCO2 * zH2O - data.xH2O * zCO2)/(data.xCO2 * data.yH2O - data.xH2O * data.yCO2);
00450 data.betaS = 1 - data.betaA - data.betaC;
00451 }
00452 }
00453
00454 void ProgressiveFlashBrineCO2H2S(BrineCO2H2SData& data, double T, double P, double zNaCl, double zH2O, double zCO2, double zH2S, unsigned int numCorrections)
00455 {
00456
00457 BrineCO2Data co2BrineData;
00458
00459 ProgressiveFlashBrineCO2(co2BrineData, T, P, zNaCl, zH2O, zCO2, numCorrections);
00460
00461 FlashBrineCO2H2S(data, co2BrineData, T, P, zH2O, zCO2, zH2S);
00462 }
00463
00464 void GeometricFlashBrineCO2H2S(BrineCO2H2SData& data, double T, double P, double zNaCl, double zH2O, double zCO2, double zH2S)
00465 {
00466
00467 BrineCO2Data co2BrineData;
00468
00469 GeometricFlashBrineCO2(co2BrineData, T, P, zNaCl, zH2O, zCO2);
00470
00471 FlashBrineCO2H2S(data, co2BrineData, T, P, zH2O, zCO2, zH2S);
00472 }
00473
00474 std::ostream& operator<<(std::ostream& out, BrineCO2Data& data)
00475 {
00476 out << "+--------------------------+" << std::endl;
00477 out << "+---- Brine-CO2 Phases ----+" << std::endl;
00478 out << "+--------------------------+" << std::endl << std::endl;
00479
00480 out << std::setprecision(6) << std::fixed << std::showpoint;
00481
00482 if(data.betaS > 0.0)
00483 {
00484 out << ":: Solid Phase, " << data.betaS * 100 << "%" << std::endl << std::endl;
00485
00486 out << "sNaCl = " << 100 << "%" << std::endl << std::endl;
00487 }
00488
00489 if(data.betaA > 0.0)
00490 {
00491 out << ":: Aqueous Phase, " << data.betaA * 100 << "%" << std::endl << std::endl;
00492
00493 out << "xNaCl = " << data.xNaCl * 100 << "%" << std::endl;
00494 out << "xH2O = " << data.xH2O * 100 << "%" << std::endl;
00495 out << "xCO2 = " << data.xCO2 * 100 << "%" << std::endl;
00496 out << "mNaCl = " << 55.508 * data.xNaCl / data.xH2O << std::endl;
00497 out << "mCO2 = " << 55.508 * data.xCO2 / data.xH2O << std::endl << std::endl;
00498 }
00499
00500 if(data.betaC > 0.0)
00501 {
00502 out << ((data.stateCO2 == LIQUID) ? ":: Liquid CO2-Rich Phase, " : (data.stateCO2 == GAS) ? ":: Gas CO2-Rich Phase, " : ":: Supercritical CO2-Rich Phase, ") << data.betaC * 100 << "%" << std::endl << std::endl;
00503
00504 out << "yH2O = " << data.yH2O * 100 << "%" << std::endl;
00505 out << "yCO2 = " << data.yCO2 * 100 << "%" << std::endl << std::endl;
00506 }
00507
00508 return out;
00509 }
00510
00511 std::ostream& operator<<(std::ostream& out, BrineCO2H2SData& data)
00512 {
00513 out << "+------------------------------+" << std::endl;
00514 out << "+---- Brine-CO2-H2S Phases ----+" << std::endl;
00515 out << "+------------------------------+" << std::endl << std::endl;
00516
00517 out << std::setprecision(6) << std::fixed << std::showpoint;
00518
00519 if(data.betaS > 0.0)
00520 {
00521 out << ":: Solid Phase, " << data.betaS * 100 << "%" << std::endl << std::endl;
00522
00523 out << "sNaCl = " << 100 << "%" << std::endl << std::endl;
00524 }
00525
00526 if(data.betaA > 0.0)
00527 {
00528 out << ":: Aqueous Phase, " << data.betaA * 100 << "%" << std::endl << std::endl;
00529
00530 out << "xNaCl = " << data.xNaCl * 100 << "%" << std::endl;
00531 out << "xH2O = " << data.xH2O * 100 << "%" << std::endl;
00532 out << "xCO2 = " << data.xCO2 * 100 << "%" << std::endl;
00533 out << "xH2S = " << data.xH2S * 100 << "%" << std::endl << std::endl;
00534 out << "mNaCl = " << 55.508 * data.xNaCl / data.xH2O << std::endl;
00535 out << "mCO2 = " << 55.508 * data.xCO2 / data.xH2O << std::endl;
00536 out << "mH2S = " << 55.508 * data.xH2S / data.xH2O << std::endl << std::endl;
00537 }
00538
00539 if(data.betaC > 0.0)
00540 {
00541 out << ((data.stateCO2 == LIQUID) ? ":: Liquid CO2-Rich Phase, " : (data.stateCO2 == GAS) ? ":: Gas CO2-Rich Phase, " : ":: Supercritical CO2-Rich Phase, ") << data.betaC * 100 << "%" << std::endl << std::endl;
00542
00543 out << "yH2O = " << data.yH2O * 100 << "%" << std::endl;
00544 out << "yCO2 = " << data.yCO2 * 100 << "%" << std::endl;
00545 out << "yH2S = " << data.yH2S * 100 << "%" << std::endl << std::endl;
00546 }
00547
00548 if(data.betaH > 0.0)
00549 {
00550 out << ((data.stateH2S == LIQUID) ? ":: Liquid H2S-Rich Phase, " : (data.stateH2S == GAS) ? ":: Gas H2S-Rich Phase, " : ":: Supercritical H2S-Rich Phase, ") << data.betaH * 100 << "%" << std::endl << std::endl;
00551
00552 out << "hH2S = " << 100 << "%" << std::endl;
00553 }
00554
00555 return out;
00556 }
00557 }