pH Stabilization and Corrosion Control
This guide covers the use of NeqSim’s Electrolyte CPA equation of state (Statoil model) for modeling pH stabilization, CO2 corrosion control, and protective FeCO3 (siderite) layer formation in oil and gas production systems.
Overview
The Corrosion Challenge
In CO2-rich production systems, carbonic acid formation leads to sweet corrosion:
\[\text{CO}_2 + \text{H}_2\text{O} \leftrightarrow \text{H}_2\text{CO}_3 \leftrightarrow \text{H}^+ + \text{HCO}_3^-\] \[\text{Fe} \rightarrow \text{Fe}^{2+} + 2e^-\]This can cause severe pipeline corrosion rates exceeding 10 mm/year without mitigation.
pH Stabilization Strategy
pH stabilization uses alkaline chemicals (typically NaOH) to:
- Raise the pH of the aqueous phase above 6.0-6.5
- Promote formation of a protective FeCO3 (siderite) layer on steel surfaces
- Reduce the rate of iron dissolution
The protective siderite layer forms when:
\[\text{Fe}^{2+} + \text{CO}_3^{2-} \rightarrow \text{FeCO}_3 \downarrow\]This layer acts as a barrier, reducing corrosion rates by 1-2 orders of magnitude.
Electrolyte CPA Equation of State
NeqSim’s Electrolyte CPA EoS (Statoil model) is specifically designed for aqueous electrolyte systems with dissolved gases. It combines:
- CPA (Cubic Plus Association): Handles polar/associating molecules (water, glycols)
- Electrolyte terms: Handles ionic species and their activity coefficients
- Gas solubility: Accurate CO2/H2S dissolution in brines
Key Capabilities
| Feature | Description |
|---|---|
| pH calculation | From ionic equilibria |
| Ion speciation | CO2/HCO3-/CO3– equilibrium |
| Scale potential | Saturation ratio for FeCO3, CaCO3, etc. |
| Gas solubility | CO2, H2S in brines |
| Activity coefficients | Debye-Hückel/Pitzer-type |
Basic pH Calculation
Java Example
import neqsim.thermo.system.SystemElectrolyteCPAstatoil;
import neqsim.thermodynamicoperations.ThermodynamicOperations;
public class PHCalculation {
public static void main(String[] args) {
// Create electrolyte system at reservoir conditions
SystemElectrolyteCPAstatoil fluid =
new SystemElectrolyteCPAstatoil(353.15, 50.0); // 80°C, 50 bara
// Add gas components
fluid.addComponent("methane", 0.85);
fluid.addComponent("CO2", 0.03); // 3 mol% CO2
fluid.addComponent("H2S", 0.001); // 0.1 mol% H2S
// Add water and ions (formation water composition)
fluid.addComponent("water", 0.10);
fluid.addComponent("Na+", 0.005); // Sodium
fluid.addComponent("Cl-", 0.005); // Chloride
fluid.addComponent("Ca++", 0.0002); // Calcium
fluid.addComponent("Fe++", 0.00001); // Dissolved iron
fluid.addComponent("HCO3-", 0.001); // Bicarbonate
// Initialize electrolyte system
fluid.chemicalReactionInit();
fluid.createDatabase(true);
fluid.setMixingRule(10); // Electrolyte CPA mixing rule
ThermodynamicOperations ops = new ThermodynamicOperations(fluid);
ops.TPflash();
fluid.init(3);
// Get aqueous phase pH
int aqPhase = fluid.getPhaseNumberOfPhase("aqueous");
double pH = fluid.getPhase(aqPhase).getpH();
System.out.printf("Calculated pH: %.2f%n", pH);
System.out.printf("CO2 partial pressure: %.2f bara%n",
fluid.getComponent("CO2").getx() * fluid.getPressure());
}
}
Python Example
from neqsim import jneqsim
# Import electrolyte CPA system
SystemElectrolyteCPAstatoil = jneqsim.thermo.system.SystemElectrolyteCPAstatoil
ThermodynamicOperations = jneqsim.thermodynamicoperations.ThermodynamicOperations
# Create fluid at 80°C, 50 bara
fluid = SystemElectrolyteCPAstatoil(273.15 + 80.0, 50.0)
# Add components
fluid.addComponent("methane", 0.85)
fluid.addComponent("CO2", 0.03)
fluid.addComponent("water", 0.10)
fluid.addComponent("Na+", 0.005)
fluid.addComponent("Cl-", 0.005)
fluid.addComponent("HCO3-", 0.001)
fluid.addComponent("Fe++", 0.00001)
# Initialize and flash
fluid.chemicalReactionInit()
fluid.createDatabase(True)
fluid.setMixingRule(10)
ops = ThermodynamicOperations(fluid)
ops.TPflash()
fluid.init(3)
# Get pH
aq_phase = fluid.getPhaseNumberOfPhase("aqueous")
pH = fluid.getPhase(aq_phase).getpH()
print(f"Aqueous phase pH: {pH:.2f}")
pH Stabilization with NaOH
Concept
Adding NaOH (sodium hydroxide) raises pH through:
\[\text{NaOH} \rightarrow \text{Na}^+ + \text{OH}^-\] \[\text{OH}^- + \text{H}^+ \rightarrow \text{H}_2\text{O}\]This shifts carbonate equilibrium towards CO3–, promoting FeCO3 precipitation.
Calculating Required NaOH Dose
import neqsim.thermo.system.SystemElectrolyteCPAstatoil;
import neqsim.thermodynamicoperations.ThermodynamicOperations;
public class NaOHDosing {
public static void main(String[] args) {
double targetpH = 6.5; // Target pH for siderite formation
// Base case - production fluid without treatment
SystemElectrolyteCPAstatoil fluid =
new SystemElectrolyteCPAstatoil(353.15, 50.0);
// Gas and water composition
fluid.addComponent("methane", 0.80);
fluid.addComponent("CO2", 0.05); // 5% CO2 - corrosive
fluid.addComponent("water", 0.12);
fluid.addComponent("Na+", 0.015);
fluid.addComponent("Cl-", 0.015);
fluid.addComponent("Fe++", 0.00002);
fluid.addComponent("HCO3-", 0.002);
fluid.chemicalReactionInit();
fluid.createDatabase(true);
fluid.setMixingRule(10);
ThermodynamicOperations ops = new ThermodynamicOperations(fluid);
ops.TPflash();
fluid.init(3);
int aqPhase = fluid.getPhaseNumberOfPhase("aqueous");
double basepH = fluid.getPhase(aqPhase).getpH();
System.out.printf("Initial pH: %.2f%n", basepH);
System.out.printf("Target pH: %.2f%n", targetpH);
System.out.println();
// Iterate to find NaOH dose
System.out.println("NaOH dose (mmol/kg) | pH");
System.out.println("--------------------|-----");
for (double naohDose = 0; naohDose <= 50; naohDose += 5) {
SystemElectrolyteCPAstatoil testFluid =
new SystemElectrolyteCPAstatoil(353.15, 50.0);
testFluid.addComponent("methane", 0.80);
testFluid.addComponent("CO2", 0.05);
testFluid.addComponent("water", 0.12);
testFluid.addComponent("Na+", 0.015 + naohDose/1000); // Added Na+
testFluid.addComponent("Cl-", 0.015);
testFluid.addComponent("OH-", naohDose/1000); // Added OH-
testFluid.addComponent("Fe++", 0.00002);
testFluid.addComponent("HCO3-", 0.002);
testFluid.chemicalReactionInit();
testFluid.createDatabase(true);
testFluid.setMixingRule(10);
ThermodynamicOperations testOps = new ThermodynamicOperations(testFluid);
testOps.TPflash();
testFluid.init(3);
int testAqPhase = testFluid.getPhaseNumberOfPhase("aqueous");
double testpH = testFluid.getPhase(testAqPhase).getpH();
String marker = (testpH >= targetpH && testpH < targetpH + 0.2) ? " <-- TARGET" : "";
System.out.printf("%18.1f | %.2f%s%n", naohDose, testpH, marker);
}
}
}
FeCO3 (Siderite) Protective Layer
Siderite Formation Conditions
The protective FeCO3 layer forms when the saturation ratio (SR) exceeds 1:
\[SR = \frac{[\text{Fe}^{2+}][\text{CO}_3^{2-}]}{K_{sp}}\]Where $K_{sp}$ for siderite is approximately 10^-10.89 at 25°C.
Key Factors for Protective Layer
| Factor | Favorable | Unfavorable |
|---|---|---|
| pH | > 6.0 | < 5.5 |
| Temperature | > 60°C | < 40°C |
| Fe++ concentration | > 1 ppm | < 0.1 ppm |
| CO2 partial pressure | Moderate | Very high |
| Flow velocity | Low/moderate | > 3 m/s (erosion) |
Calculating FeCO3 Saturation
import neqsim.thermo.system.SystemElectrolyteCPAstatoil;
import neqsim.thermodynamicoperations.ThermodynamicOperations;
public class SideriteSaturation {
public static void main(String[] args) {
SystemElectrolyteCPAstatoil fluid =
new SystemElectrolyteCPAstatoil(353.15, 50.0); // 80°C
// CO2-rich gas with formation water
fluid.addComponent("methane", 0.80);
fluid.addComponent("CO2", 0.05);
fluid.addComponent("water", 0.12);
fluid.addComponent("Na+", 0.02); // With NaOH treatment
fluid.addComponent("OH-", 0.005); // Added OH-
fluid.addComponent("Cl-", 0.015);
fluid.addComponent("Fe++", 0.00005); // 5 mg/L dissolved iron
fluid.addComponent("HCO3-", 0.003);
fluid.addComponent("CO3--", 0.0005);
fluid.chemicalReactionInit();
fluid.createDatabase(true);
fluid.setMixingRule(10);
ThermodynamicOperations ops = new ThermodynamicOperations(fluid);
ops.TPflash();
fluid.init(3);
// Check scale potential
int aqPhase = fluid.getPhaseNumberOfPhase("aqueous");
ops.checkScalePotential(aqPhase);
// Get results
String[][] results = ops.getResultTable();
System.out.println("=== SCALE POTENTIAL RESULTS ===");
System.out.println();
System.out.println("Salt | Saturation Ratio | Status");
System.out.println("---------------|------------------|--------");
for (int i = 1; i < results.length && results[i][0] != null &&
!results[i][0].isEmpty(); i++) {
String salt = results[i][0];
double sr = Double.parseDouble(results[i][1]);
String status;
if (salt.equals("FeCO3")) {
// For siderite, we WANT SR > 1 for protective layer
if (sr > 1.0) {
status = "✅ Protective layer forms";
} else if (sr > 0.5) {
status = "⚠️ Marginal - increase pH";
} else {
status = "❌ No protection";
}
} else {
// For other scales, SR > 1 is a problem
if (sr > 1.0) {
status = "⚠️ SCALE RISK";
} else {
status = "✅ No scaling";
}
}
System.out.printf("%-14s | %16.3f | %s%n", salt, sr, status);
}
// Also report pH
double pH = fluid.getPhase(aqPhase).getpH();
System.out.println();
System.out.printf("Aqueous phase pH: %.2f%n", pH);
}
}
Combined Hydrate Inhibition and pH Control
MEG with pH Stabilization
In subsea systems, monoethylene glycol (MEG) is often used for both:
- Hydrate inhibition (thermodynamic depression)
- pH stabilization (with NaOH addition)
This is called pH-stabilized MEG or buffered MEG.
import neqsim.thermo.system.SystemElectrolyteCPAstatoil;
import neqsim.thermodynamicoperations.ThermodynamicOperations;
public class MEGWithPHStabilization {
public static void main(String[] args) {
// Subsea conditions
double seabedTemp = 277.15; // 4°C
double pressure = 150.0; // bara
System.out.println("=== MEG + pH STABILIZATION ===");
System.out.println();
// Case 1: Without MEG or pH control
System.out.println("Case 1: No treatment");
analyzeCase(seabedTemp, pressure, 0.0, 0.0);
// Case 2: MEG only (30 wt%)
System.out.println("Case 2: 30% MEG only");
analyzeCase(seabedTemp, pressure, 0.30, 0.0);
// Case 3: MEG + pH stabilization
System.out.println("Case 3: 30% MEG + NaOH (pH stabilized)");
analyzeCase(seabedTemp, pressure, 0.30, 0.01);
}
static void analyzeCase(double T, double P, double megFrac, double naohDose) {
SystemElectrolyteCPAstatoil fluid =
new SystemElectrolyteCPAstatoil(T, P);
// Gas composition
fluid.addComponent("methane", 0.75);
fluid.addComponent("ethane", 0.05);
fluid.addComponent("propane", 0.02);
fluid.addComponent("CO2", 0.04);
// Water phase with MEG
double waterFrac = 0.12 * (1 - megFrac);
double megMolFrac = 0.12 * megFrac * (18.0/62.0); // Convert wt% to mol
fluid.addComponent("water", waterFrac);
if (megFrac > 0) {
fluid.addComponent("MEG", megMolFrac);
}
// Ions
fluid.addComponent("Na+", 0.01 + naohDose);
fluid.addComponent("Cl-", 0.01);
fluid.addComponent("Fe++", 0.00002);
fluid.addComponent("HCO3-", 0.002);
if (naohDose > 0) {
fluid.addComponent("OH-", naohDose);
}
fluid.chemicalReactionInit();
fluid.createDatabase(true);
fluid.setMixingRule(10);
fluid.setHydrateCheck(true);
ThermodynamicOperations ops = new ThermodynamicOperations(fluid);
// Calculate hydrate temperature
try {
ops.hydrateFormationTemperature();
double hydrateT = fluid.getTemperature() - 273.15;
System.out.printf(" Hydrate formation T: %.1f °C%n", hydrateT);
} catch (Exception e) {
System.out.println(" No hydrate at these conditions");
}
// Reset and flash at operating conditions
fluid.setTemperature(T);
fluid.setPressure(P);
ops.TPflash();
fluid.init(3);
// Get pH
try {
int aqPhase = fluid.getPhaseNumberOfPhase("aqueous");
double pH = fluid.getPhase(aqPhase).getpH();
System.out.printf(" Aqueous pH: %.2f%n", pH);
// Check FeCO3 saturation
ops.checkScalePotential(aqPhase);
String[][] results = ops.getResultTable();
for (int i = 1; i < results.length; i++) {
if (results[i][0] != null && results[i][0].equals("FeCO3")) {
double sr = Double.parseDouble(results[i][1]);
String status = sr > 1.0 ? "Protected" : "Unprotected";
System.out.printf(" FeCO3 saturation ratio: %.3f (%s)%n", sr, status);
break;
}
}
} catch (Exception e) {
System.out.println(" Could not determine pH/scale");
}
System.out.println();
}
}
Corrosion Rate Estimation
de Waard-Milliams Correlation
While NeqSim calculates thermodynamics, corrosion rate prediction requires empirical correlations. The de Waard-Milliams model is commonly used:
\[\log(CR) = 5.8 - \frac{1710}{T} + 0.67 \cdot \log(pCO_2)\]Where:
- $CR$ = corrosion rate (mm/year)
- $T$ = temperature (K)
- $pCO_2$ = CO2 partial pressure (bar)
Combined NeqSim + Corrosion Workflow
import neqsim.thermo.system.SystemElectrolyteCPAstatoil;
import neqsim.thermodynamicoperations.ThermodynamicOperations;
public class CorrosionEstimate {
public static void main(String[] args) {
// Pipeline conditions
double tempC = 60.0;
double pressure = 80.0;
double co2MolFrac = 0.03;
SystemElectrolyteCPAstatoil fluid =
new SystemElectrolyteCPAstatoil(tempC + 273.15, pressure);
fluid.addComponent("methane", 0.85);
fluid.addComponent("CO2", co2MolFrac);
fluid.addComponent("water", 0.10);
fluid.addComponent("Na+", 0.01);
fluid.addComponent("Cl-", 0.01);
fluid.addComponent("Fe++", 0.00002);
fluid.addComponent("HCO3-", 0.002);
fluid.chemicalReactionInit();
fluid.createDatabase(true);
fluid.setMixingRule(10);
ThermodynamicOperations ops = new ThermodynamicOperations(fluid);
ops.TPflash();
fluid.init(3);
// Get CO2 partial pressure (fugacity in aqueous phase is more accurate)
double pCO2 = fluid.getComponent("CO2").getx() * pressure;
// de Waard-Milliams correlation (simplified)
double tempK = tempC + 273.15;
double logCR = 5.8 - 1710.0/tempK + 0.67 * Math.log10(pCO2);
double corrosionRate = Math.pow(10, logCR);
// pH correction factor (higher pH = lower corrosion)
int aqPhase = fluid.getPhaseNumberOfPhase("aqueous");
double pH = fluid.getPhase(aqPhase).getpH();
// Simplified pH correction (actual models are more complex)
double phFactor = 1.0;
if (pH > 5.0) {
phFactor = Math.pow(10, -(pH - 5.0) * 0.5);
}
double correctedCR = corrosionRate * phFactor;
// FeCO3 protection factor
ops.checkScalePotential(aqPhase);
String[][] results = ops.getResultTable();
double feco3SR = 0;
for (int i = 1; i < results.length; i++) {
if (results[i][0] != null && results[i][0].equals("FeCO3")) {
feco3SR = Double.parseDouble(results[i][1]);
break;
}
}
// If FeCO3 layer forms, significant protection
if (feco3SR > 1.0) {
correctedCR *= 0.1; // 90% reduction (typical for good siderite layer)
}
System.out.println("=== CORROSION ASSESSMENT ===");
System.out.println();
System.out.printf("Temperature: %.0f °C%n", tempC);
System.out.printf("Pressure: %.0f bara%n", pressure);
System.out.printf("CO2 partial pressure: %.2f bar%n", pCO2);
System.out.printf("Aqueous pH: %.2f%n", pH);
System.out.printf("FeCO3 saturation ratio: %.3f%n", feco3SR);
System.out.println();
System.out.printf("Bare steel corrosion rate: %.2f mm/year%n", corrosionRate);
System.out.printf("pH-corrected rate: %.2f mm/year%n", corrosionRate * phFactor);
System.out.printf("With FeCO3 protection: %.2f mm/year%n", correctedCR);
// Classification
System.out.println();
if (correctedCR < 0.1) {
System.out.println("Risk: LOW - Acceptable without inhibitor");
} else if (correctedCR < 1.0) {
System.out.println("Risk: MODERATE - Consider pH stabilization");
} else if (correctedCR < 5.0) {
System.out.println("Risk: HIGH - pH stabilization required");
} else {
System.out.println("Risk: SEVERE - CRA materials may be needed");
}
}
}
Available Ions in NeqSim
The Electrolyte CPA model supports these ionic species:
Cations
| Ion | Name | Use Case |
|---|---|---|
Na+ |
Sodium | NaOH, NaCl brine |
K+ |
Potassium | Formation water |
Ca++ |
Calcium | CaCO3 scale, formation water |
Mg++ |
Magnesium | Formation water |
Fe++ |
Ferrous iron | Corrosion product, FeCO3 |
Ba++ |
Barium | BaSO4 scale |
Sr++ |
Strontium | Formation water |
H3O+ |
Hydronium | pH calculations |
Anions
| Ion | Name | Use Case |
|---|---|---|
Cl- |
Chloride | Brine salinity |
OH- |
Hydroxide | NaOH treatment |
HCO3- |
Bicarbonate | CO2 system equilibrium |
CO3-- |
Carbonate | Scale, pH stabilization |
SO4-- |
Sulfate | Formation water, BaSO4 scale |
S-- |
Sulfide | H2S systems |
Ac- |
Acetate | Organic acids |
Industry Best Practices
pH Stabilization Guidelines (NORSOK M-506)
| Parameter | Guideline |
|---|---|
| Target pH | 6.0 - 6.5 for optimal FeCO3 |
| NaOH purity | > 99% recommended |
| Injection point | Upstream of temperature drop |
| Monitoring | Continuous pH measurement |
| Iron control | 1-10 mg/L Fe++ optimal |
Siderite Layer Quality Indicators
| Indicator | Good Layer | Poor Layer |
|---|---|---|
| pH | 6.0-6.5 | < 5.5 |
| Temperature | > 60°C | < 40°C |
| Flow velocity | < 3 m/s | > 5 m/s |
| Scaling tendency (SR) | 1-10 | < 1 or > 100 |
| Fluid stability | Continuous flow | Slug flow |
References
-
de Waard, C. and Milliams, D.E. (1975). “Carbonic Acid Corrosion of Steel.” Corrosion, 31(5), 177-181.
-
Dugstad, A. (2006). “Fundamental Aspects of CO2 Metal Loss Corrosion.” NACE Corrosion 2006, Paper 06111.
-
Nyborg, R. (2010). “Guidelines for Prediction of CO2 Corrosion in Oil and Gas Production Systems.” IFE Report, IFE/KR/E-2010/003.
-
NORSOK M-506 (2017). “CO2 Corrosion Rate Calculation Model.” Norwegian Technology Standards Institution.
-
Nordsveen, M. et al. (2003). “A Mechanistic Model for Carbon Dioxide Corrosion of Mild Steel in the Presence of Protective Iron Carbonate Films.” Corrosion, 59(5), 443-456.
-
Olsen, S. and Dugstad, A. (1991). “Corrosion under Dewing Conditions.” NACE Corrosion 91, Paper 91472.
See Also
- Scale Potential Calculations - Detailed scale modeling
- Flow Assurance Overview - Integrated flow assurance
- Hydrate Models Guide - Hydrate equilibrium
- Component Reference - Available ions
- Chemical Reactions - Reaction equilibrium