CO2 Storage (eNRTL / Duan-Sun)¶
DWSIM Plus
Available with a DWSIM Plus (Patreon) subscription.
Property package for CO2 geological storage: CO2 with water and chloride brines at reservoir conditions. CO2 solubility in the brine from the Duan-Sun model, water from IAPWS-IF97, the CO2-rich phase from Peng-Robinson, brine density with dissolved CO2, carbonate speciation (pH) and saturation indices of carbonate minerals.
DWSIM.Extensions.PropertyPackages.CCUS.Storage.CO2StoragePropertyPackage
Assembly DWSIM.Extensions.PropertyPackages.CCUS.dll · Object ← PropertyPackage ← ElectrolyteBasePropertyPackage ← BaseElectrolytePropertyPackage ← CO2StoragePropertyPackage
Name in the flowsheet CO2 Storage (eNRTL/Duan-Sun) · FluentAPI PropertyPackages.Plus.CO2Storage
Scope¶
The package describes CO2 with water and chloride brines at the conditions of geological
storage: saline aquifer injection, CO2-enhanced oil recovery, and the brine chemistry that leads
to mineral trapping. The apparent compounds are water, CO2, other gases, and the ions or salts
of the brine (for instance Sodium (ion) and Chloride (ion), or a salt such as sodium chloride). Ions
and salts are non-volatile and stay in the liquid. All parameters are compiled into the package; it reads
no data files.
The phase equilibrium combines four parts, run through the standard vapor-liquid flash of DWSIM:
- CO2 in the aqueous phase from the model of Duan and Sun (2003), with the salting-out terms of Duan et al. (2006) for Na+, K+, Ca2+, Mg2+, Cl- and SO42-. The liquid fugacity of CO2 is referred to a water-saturated CO2 phase computed with Peng-Robinson, so under a CO2 phase the flash returns the Duan-Sun solubility (within 0.25 % in pure water). Other ions are counted by their charge as one of these classes.
- Water from IAPWS-IF97 (vapor pressure and liquid volume), with the Poynting correction and Raoult's law on the dissolved particles.
- The CO2-rich phase from Peng-Robinson, with kij(CO2-H2O) = 0.193 fitted to the water content of the Spycher, Pruess and Ennis-King (2003) model. A liquid without water (dense CO2) takes the fugacity of pure CO2 from Span-Wagner.
- H2S and the supercritical gases by Henry's law.
Density and enthalpy. The brine density is IAPWS-IF97 water plus the apparent molar volumes of NaCl, KCl, CaCl2 and MgCl2 (fitted to Al Ghafri, Maitland and Trusler 2012) and of dissolved CO2 (fitted to McBride-Wright, Maitland and Trusler 2015). The liquid enthalpy carries the heat of solution of CO2 from the temperature dependence of the Duan-Sun model.
pH and minerals. The pH and ionic strength come from a carbonate speciation at the total dissolved
carbon (constants of Edwards et al. 1978, Davies activity coefficients), returned by Speciate and
LastSpeciation. CalculateMineralEquilibrium gives the saturation indices of calcite, magnesite,
siderite and dolomite; dawsonite has a solubility product only, since the package carries no aluminium
species.
Validity. Duan-Sun covers 273 to 533 K and up to 2000 bar; the densities were fitted from 283 to 473 K and up to 700 bar; NaCl up to about 6 mol/kg.
Accuracy (NIST ThermoML data; no parameter was fitted to the CO2 solubilities):
| Quantity | Data | Average deviation |
|---|---|---|
| CO2 solubility in water | 346 points, 9 sets | 2.6 % |
| CO2 solubility in NaCl brines, 1 to 6 mol/kg | 580 points, 5 sets | 2.6 % |
| CO2 solubility in KCl, CaCl2, MgCl2 brines | Kamps 2007, Tong 2013, Messabeb 2017 | 18, 11 and 10 % low |
| Brine density, four salts | Al Ghafri 2012 (fit) | 0.4 to 0.7 kg/m3 |
| NaCl brine with CO2 | Song 2013 (check) | 1.6 kg/m3 |
| CO2-saturated water | Hebach 2004 (check) | 1.0 kg/m3 |
| Heat of solution of CO2 | Koschel 2006, 22 points | 0.30 kJ/mol |
| Water in the CO2 phase | Kim 2012, 25 points | 7.3 % |
| Water vapor pressure over NaCl brine | Nasirzadeh 2004, 42 points | 0.9 % |
Limitations¶
- Duan and Sun count K+ as Na+ and Mg2+ as Ca2+, so the model salts out more CO2 than measured in KCl, CaCl2 and MgCl2 brines, most at high molality and low temperature: up to 24 % at 6 mol/kg CaCl2 and 15 % at 5 mol/kg MgCl2.
- The flash forms no solid salt phase: a brine that dries out (at 473 K and 1 bar, for instance) does not precipitate its salt.
- The water content of the CO2 phase follows Spycher and Pruess and the dew points of Kim et al. (2012); the Raman data of Wang et al. (2018) lie well above both.
- Raoult's law carries no osmotic coefficient: the water vapor pressure over 3.6 mol/kg NaCl comes out 2.2 % high.
- The pH and the saturation indices use Davies activity coefficients and are indicative above an ionic strength of about 0.5 mol/kg.
Example¶
This code runs on every build of this site, and the output below is what it printed.
fs = (Flowsheet.Create("CO2StorageExample")
.WithCompounds("Water", "Carbon dioxide", "Sodium (ion)", "Chloride (ion)")
.WithPropertyPackage(PropertyPackages.Plus.CO2Storage))
water = 1.0 / 0.018015 # mol/s in 1 kg/s of water
def stream(name, nacl, co2):
# 1 kg/s of water with nacl mol/s of Na+ and Cl- (molality) and co2 mol/s of CO2,
# at reservoir conditions; 2 mol of CO2 per kg of water is more than dissolves
b = (fs.AddMaterialStream(name).At(Q.Celsius(60.0), Q.Bar(150.0))
.SetCompoundMolarFlow("Water", water)
.SetCompoundMolarFlow("Carbon dioxide", co2))
if nacl > 0.0:
b.SetCompoundMolarFlow("Sodium (ion)", nacl).SetCompoundMolarFlow("Chloride (ion)", nacl)
return b
brine_co2 = stream("Brine + CO2", 1.0, 2.0)
water_co2 = stream("Water + CO2", 0.0, 2.0)
brine = stream("Brine", 1.0, 0.0)
fs.Solve()
def co2_molality(liquid): # mol CO2 per kg of water
x = liquid.Compounds
return x["Carbon dioxide"].MoleFraction / (x["Water"].MoleFraction * 0.018015)
s = brine_co2.Object
aq, co2_phase = s.Phases[3], s.Phases[2] # the brine, and the CO2-rich phase
print(f"CO2 dissolved = {co2_molality(aq):.3f} mol/kg in the brine, "
f"{co2_molality(water_co2.Object.Phases[3]):.3f} mol/kg in pure water")
print(f"pH of the brine = {aq.Properties.pH:.2f} with CO2, {brine.Object.Phases[3].Properties.pH:.2f} without")
print(f"Brine density = {aq.Properties.density:.1f} kg/m3 with CO2, "
f"{brine.Object.Phases[3].Properties.density:.1f} without")
print(f"Water in the CO2-rich phase = {co2_phase.Compounds['Water'].MoleFraction * 100:.2f} mol%")
Output
CO2 dissolved = 0.949 mol/kg in the brine, 1.162 mol/kg in pure water
pH of the brine = 3.12 with CO2, 6.52 without
Brine density = 1033.3 kg/m3 with CO2, 1027.1 without
Water in the CO2-rich phase = 0.71 mol%
DWSIM 10.2.11.0, generated 2026-10-08.
Property methods¶
How the package calculates each property, as it reports it in PropertyMethodsInfo (the property package editor shows the same list).
| Property | Method |
|---|---|
| Vapor fugacity | Peng-Robinson EOS, kij(CO2-water) = 0.193 |
| Liquid fugacity | Duan-Sun (CO2), vapour pressure with Poynting correction and Raoult's law on the solutes (water), Henry's law (other gases) |
| Vapor enthalpy, entropy, Cp/Cv | Ideal Gas + Peng-Robinson residual |
| Liquid enthalpy, entropy, Cp/Cv | Ideal gas less heat of vaporization + heat of solution of CO2 (Duan-Sun) + brine excess enthalpy (eNRTL, when enabled); liquid without water: as the vapour; Cp: water and aqueous ion heat capacities, Cv = Cp |
| Vapor density | Ideal Gas |
| Liquid density | IAPWS-IF97 water + apparent molar volumes of salts and CO2; liquid without water: Peng-Robinson EOS |
| Vapor viscosity | Experimental / Lucas / Jossi-Stiel-Thodos |
| Liquid viscosity | Experimental / Letsou-Stiel (solvent) with the Jones-Dole correction for the ions |
| Vapor thermal conductivity | Experimental / Ely-Hanley |
| Liquid thermal conductivity | Experimental / Latini (solvent) with the Riedel correction for the ions |
| Surface tension | Experimental / Brock-Bird |
| Solid density | Experimental Data / User-Defined |
| Solid enthalpy, entropy, Cp/Cv | Experimental Solid Cp / From Liquid Phase Enthalpy + Enthalpy of Fusion |
Default flash algorithm: Nested_Loops_VLE.
Configuration saved with the flowsheet¶
The package writes its settings to the simulation file (SaveData) and reads them back on load (LoadData). The elements below are the ones this package adds to those of every property package, as written for the example.
| Element | Content in the example | What it holds |
|---|---|---|
ElectrolyteFlash_ReactionSetID |
DefaultSet |
Setting of the legacy electrolyte flash (ElectrolyteSVLE) that the base class saves; the flash of this package does not read it |
ElectrolyteFlash_Tolerance |
1E-07 |
The same, the tolerance of that flash |
ElectrolyteFlash_MaximumIterations |
200 |
The same, its iteration limit |
IncludeExcessEnthalpy |
false |
Adds the eNRTL excess enthalpy of the brine to the liquid enthalpy (off by default) |
GenerateSolubilityReactions |
false |
Salt dissolution equilibria of the base electrolyte package; not used here, since the package replaces its chemical equilibria |
UseReactiveKvalues |
false |
Not used here; the columns take the K-values the flash converges on |
Settings common to every property package, as saved for the example
| Element | Value |
|---|---|
Type |
72 characters |
ComponentName |
CO2 Storage (eNRTL/Duan-Sun) |
ComponentDescription |
69 characters |
Tag |
CO2 Storage (eNRTL/Duan-Sun) |
UseHenryConstants |
true |
AutoEstimateMissingNRTLUNIQUACParameters |
true |
UseImmiscibleListForLiquid2InitialEstimates |
true |
SingleCompoundCheckThreshold |
0.99999 |
OverrideKvalFugCoeff |
false |
OverrideEnthalpyCalculation |
false |
OverrideEntropyCalculation |
false |
LiquidDensityCalculationMode_Subcritical |
Rackett_and_ExpData |
LiquidDensityCalculationMode_Supercritical |
Rackett_and_ExpData |
LiquidDensity_CorrectExpDataForPressure |
true |
LiquidDensity_UsePenelouxVolumeTranslation |
true |
LiquidViscosityCalculationMode_Subcritical |
ExpData |
LiquidViscosityCalculationMode_Supercritical |
Letsou_Stiel |
LiquidViscosity_CorrectExpDataForPressure |
true |
LiquidViscosity_MixingRule |
MoleAverage |
VaporPhaseFugacityCalculationMode |
Ideal |
SolidPhaseFugacityCalculationMethod |
FromLiquidFugacity |
SolidPhaseFugacity_UseIdealLiquidPhaseFugacity |
false |
SolidPhaseEnthalpy_UsesCp |
false |
EnthalpyEntropyCpCvCalculationMode |
LeeKesler |
LiquidEnthalpyEntropyCpCvCalculationMode_EOS |
EOS |
LiquidFugacity_UsePoyntingCorrectionFactor |
true |
ActivityCoefficientModels_IgnoreMissingInteractionParameters |
false |
IgnoreVaporFractionLimit |
false |
IgnoreSalinityLimit |
false |
CalculateAdditionalMaterialStreamProperties |
true |
FlashCalculationApproach |
NestedLoops |
DisplayMissingCompoundPropertiesWarning |
false |
ForcedSolids |
[] |
PropertyOverrides |
{} |
FlashSettings |
36 Setting entries |
Learn more¶
-
User guide
-
Used with
API members¶
Public members declared by this class. Inherited members are documented on the base classes.
Constructors¶
CO2StoragePropertyPackage(bool)
| Parameter | Type | Description |
|---|---|---|
comode |
Boolean |
Properties¶
FlashBase: A vapour-liquid flash on DW_CalcFugCoeff, with the algorithm DWSIM picks for a molecular package.
A vapour-liquid flash on DW_CalcFugCoeff, with the algorithm DWSIM picks for a molecular package.
ImplementsAnalyticalDerivatives
ImplementsCrossPlatformEditor
LastSpeciation: Carbonate speciation of the last liquid whose pH or ionic strength was calculated, or null.
Carbonate speciation of the last liquid whose pH or ionic strength was calculated, or null.
ShouldUseKvalueMethod3: The columns take DW_CalcKvalue, the K-values the flash converges on, with UseReactiveKvalues on or off.
The columns take DW_CalcKvalue, the K-values the flash converges on, with UseReactiveKvalues on or off.
Methods¶
AUX_LIQDENS(double, Array, double, double, bool): Density of an aqueous liquid from BrineDensity: water, the chlorides of the brine's cations, dissolved CO2, and any...
Density of an aqueous liquid from BrineDensity: water, the chlorides of the brine's cations, dissolved CO2, and any other molecular solute at its own liquid molar volume. A liquid without water or ions (dense CO2 that a flash labels liquid) takes the Peng-Robinson density of the CO2-rich phase, as it takes its enthalpy, so that its label does not change its density. Any other liquid takes the electrolyte packages' density.
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
Vx |
Array |
|
P |
Double |
|
Pvp |
Double |
|
FORCE_EOS |
Boolean |
AUX_LIQDENS(double, double, double, int, bool): The phase overload the phase properties are calculated with, which the electrolyte base class answers on its own...
The phase overload the phase properties are calculated with, which the electrolyte base class answers on its own: the same density for an aqueous liquid and for dense CO2.
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
P |
Double |
|
Pvp |
Double |
|
phaseid |
Int32 |
|
FORCE_EOS |
Boolean |
AUX_VAPDENS(double, double): Vapour density: Peng-Robinson on the vapour phase, the equation of state of its fugacities and enthalpy.
Vapour density: Peng-Robinson on the vapour phase, the equation of state of its fugacities and enthalpy. The electrolyte packages' density was the ideal gas.
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
P |
Double |
Brine(Array, out double, out double): The ions of the liquid Vx on a molality basis, with the molalities of CO2 and of the other molecular solutes; null...
The ions of the liquid Vx on a molality basis, with the molalities of CO2 and of the other molecular solutes; null when the liquid holds no water.
| Parameter | Type | Description |
|---|---|---|
Vx |
Array |
|
mCO2 |
Double |
CO2 [mol/kg H2O] |
mOther |
Double |
molecular solutes other than CO2 [mol/kg H2O] |
CalculateChemicalEquilibria(double, double)
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
P |
Double |
CalculateChemicalEquilibria(double, double, double[])
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
P |
Double |
|
x |
Double[] |
CalculateCO2Solubility(double, double): CO2 solubility [mol/kg H2O] at (T [K], P [Pa]) in the brine of the stream's overall composition, under a CO2 phase...
CO2 solubility [mol/kg H2O] at (T [K], P [Pa]) in the brine of the stream's overall composition, under a CO2 phase with water at its vapour pressure.
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
P |
Double |
CalculateCO2Solubility(double, double, double)
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
P |
Double |
|
mNaCl |
Double |
CalculateMineralEquilibrium(double)
| Parameter | Type | Description |
|---|---|---|
T |
Double |
CalculateMineralEquilibrium(double, Dictionary<string, double>)
| Parameter | Type | Description |
|---|---|---|
T |
Double |
|
ionMolalities |
Dictionary<String,Double> |
Clone()
DW_CalcdKdT(double[], double[], double, double, string)
| Parameter | Type | Description |
|---|---|---|
Vx |
Double[] |
|
Vy |
Double[] |
|
T |
Double |
|
P |
Double |
|
type |
String |
DW_CalcdLnFugCoeffdn(double[], double, double, State): Composition (mole-number) derivative of the natural logarithm of the fugacity coefficients, d(ln phi_i)/dn_j, for...
Composition (mole-number) derivative of the natural logarithm of the fugacity coefficients, d(ln phi_i)/dn_j, for the given phase, evaluated on a total-moles = 1 basis. Returns an (nc x nc) matrix. Base implementation is a finite difference in mole numbers; EOS/activity packages override this with the closed-form partial-molar expression.
| Parameter | Type | Description |
|---|---|---|
Vx |
Double[] |
|
T |
Double |
|
P |
Double |
|
st |
State |
DW_CalcdLnFugCoeffdT(double[], double, double, State): Temperature derivative of the natural logarithm of the fugacity coefficients, d(ln phi_i)/dT, for the given phase.
Temperature derivative of the natural logarithm of the fugacity coefficients, d(ln phi_i)/dT, for the given phase. Base implementation is a forward finite difference; EOS/activity packages override this with a closed-form expression. Distinct from DW_CalcdFugCoeffdT, which returns d(phi)/dT (not the logarithm).
| Parameter | Type | Description |
|---|---|---|
Vx |
Double[] |
|
T |
Double |
|
P |
Double |
|
st |
State |
DW_CalcEnthalpy(Array, double, double, State): Liquid: dissolved CO2 sits on its ideal gas reference, without a heat of vaporisation, plus its heat of solution...
Liquid: dissolved CO2 sits on its ideal gas reference, without a heat of vaporisation, plus its heat of solution from the Duan-Sun chemical potential; the rest of the liquid is the electrolyte packages' enthalpy. Vapour: ideal gas plus the residual enthalpy of the Peng-Robinson fugacities the flash uses, about −10 kJ/mol for CO2 at 50 °C and 200 bar. A liquid without water (dense CO2 that a flash called liquid) takes the vapour's enthalpy on the same equation of state, so that its label does not change its enthalpy.
| Parameter | Type | Description |
|---|---|---|
Vx |
Array |
|
T |
Double |
|
P |
Double |
|
st |
State |
DW_CalcEntropy(Array, double, double, State): Entropy to match DW_CalcEnthalpy: the solution term enters as H/T, the entropy change of a step taken at...
Entropy to match DW_CalcEnthalpy: the solution term enters as H/T, the entropy change of a step taken at equilibrium; the vapour takes its residual entropy.
| Parameter | Type | Description |
|---|---|---|
Vx |
Array |
|
T |
Double |
|
P |
Double |
|
st |
State |
DW_CalcFugCoeff(Array, double, double, State): Liquid: water at its vapour pressure with the Poynting correction and Raoult's law on the dissolved particles; CO2...
Liquid: water at its vapour pressure with the Poynting correction and Raoult's law on the dissolved particles; CO2 by Duan-Sun when the liquid holds water, as pure CO2 by Span-Wagner when it does not; H2S and supercritical gases by Henry's law; ions and salts non-volatile. Vapour: Peng-Robinson on the molecular compounds.
| Parameter | Type | Description |
|---|---|---|
Vx |
Array |
|
T |
Double |
|
P |
Double |
|
st |
State |
DW_CalcKvalue3(double[], double[], double, double): DW_CalcKvalue on the normalised liquid and vapour, for a caller that asks for the reactive route by name.
DW_CalcKvalue on the normalised liquid and vapour, for a caller that asks for the reactive route by name. Amounts in, as the columns pass them; with no vapour the liquid stands in for it.
| Parameter | Type | Description |
|---|---|---|
Vnx |
Double[] |
|
Vny |
Double[] |
|
T |
Double |
|
P |
Double |
DW_CalcPhaseProps(Phase): The vapour's compressibility factor follows its density; the electrolyte packages wrote 1.
The vapour's compressibility factor follows its density; the electrolyte packages wrote 1.
| Parameter | Type | Description |
|---|---|---|
Phase |
Phase |
DW_CalcProp(string, Phase): pH and ionic strength of a liquid come from its carbonate speciation, which LastSpeciation then...
pH and ionic strength of a liquid come from its carbonate speciation, which LastSpeciation then holds; everything else is the electrolyte packages' own.
| Parameter | Type | Description |
|---|---|---|
property |
String |
|
phase |
Phase |
GetEditingForm(): Returns the binary interaction parameter editor of this package.
Returns the binary interaction parameter editor of this package.
PopulateCrossPlatformEditor(object): The package has no adjustable parameters: the panel lists the solubility model, the trapping minerals and the model...
The package has no adjustable parameters: the panel lists the solubility model, the trapping minerals and the model, mirroring the Windows editor.
| Parameter | Type | Description |
|---|---|---|
container |
Object |
RET_VNONVOLATILE(): Ions and salts are non-volatile: the flash keeps them in the liquid.
Ions and salts are non-volatile: the flash keeps them in the liquid.
RET_VPVAP(double): Vapour pressures the flash starts its K-values from.
Vapour pressures the flash starts its K-values from. With water in the compound list, CO2 and H2S take their Henry's constants on the mole-fraction scale instead, which is what they dissolve by: the extrapolated vapour pressure of CO2 is below a reservoir pressure, and the flash then took any feed with CO2 for an all-liquid one without a single K-value.
| Parameter | Type | Description |
|---|---|---|
T |
Double |
ReturnInstance(string)
| Parameter | Type | Description |
|---|---|---|
typename |
String |
RunPostMaterialStreamSetRoutine()
SaveData(): Writes IncludeExcessEnthalpy, GenerateSolubilityReactions and UseReactiveKvalues after the data the base package...
Writes IncludeExcessEnthalpy, GenerateSolubilityReactions and UseReactiveKvalues after the data the base package saves, so the three options travel with the flowsheet file and with Clone.
Speciate(Array, double, double): Carbonate speciation of the liquid Vx at (T, P [Pa]); null when it holds no water.
Carbonate speciation of the liquid Vx at (T, P [Pa]); null when it holds no water.
| Parameter | Type | Description |
|---|---|---|
Vx |
Array |
|
T |
Double |
|
P |
Double |
Fields¶