Skip to main content

Electrolyser Brine Acidification Simulation Using Reaktoro

Originally posted on

In chlor-alkali electrolysis, controlling feed brine pH via acidification is crucial to minimize byproduct formation (like chlorate) and optimize current efficiency. However, raw brine contains impurities such as carbonates (\( \text{CO}_3^{2-} \)) and sulfates (\( \text{SO}_4^{2-} \)) that act as buffers, altering the expected pH response curve.

This post uses Reaktoro, a powerful computational framework for geochemical and chemical equilibrium modeling, to simulate HCl dosage and predict exact pH drops while accounting for operating temperatures, aqueous complexation and speciation.

How Reaktoro Operates #

When modeling complex geochemistry or high-salinity process chemistry (like chlor-alkali brines), Reaktoro structures the calculation into four distinct phases:

  1. Definition of the Chemical System: Choosing the thermodynamic engine (SupcrtDatabase), selecting the specific chemical species that will participate in the reactions, and applying an activity model (like Pitzer) to correct for non-ideal high-ionic-strength interactions.
  2. State Construction & Loading: Defining the physical conditions (temperature, pressure) and feeding the raw chemical inventory mass into a baseline ChemicalState object.
  3. Equilibrium Resolution: Compiling specialized numerical specifications (EquilibriumSpecs) and bringing the unstable starting composition into a state of minimum Gibbs free energy via the EquilibriumSolver.
  4. Perturbation / Step Titration: Cloning the stable baseline state, adding mass increments (such as \( \text{H}^+ \) and \( \text{Cl}^- \)), and re-solving thermodynamic equilibrium at every incremental step to trace out species tracking histories.

Below is the execution flow represented as a Mermaid diagram for your blog:

graph TD subgraph Phase_1 ["Thermodynamic System Definition"] DB["Load Database
rkt.SupcrtDatabase('supcrtbl')"] Species["Define Aqueous Phase
rkt.speciate(...)"] Model["Set Activity Model
rkt.ActivityModelPitzer()"] Sys["Compile Chemical System
rkt.ChemicalSystem(db, phase)"] DB --> Species Species --> Model Model --> Sys end subgraph Phase_2 ["Chemical State & Inventory Initialization"] CState["Instantiate State Block
rkt.ChemicalState(system)"] BC["Set Boundary Conditions
Temperature (60°C) & Pressure (1 bar)"] Mass["Define Mass Inventory Constraints
H₂O, Na⁺, Cl⁻, CO₃²⁻, SO₄²⁻"] Sys --> CState CState --> BC BC --> Mass end subgraph Phase_3 ["Baseline Equilibrium Resolution"] Specs["Define Equilibrium Specs
rkt.EquilibriumSpecs(system)"] Solver["Instantiate Solver Block
rkt.EquilibriumSolver(specs)"] Equil["Execute Initial Solve
solver.solve(state)"] BasePH["Output Initial Baseline pH"] Mass --> Specs Specs --> Solver Solver --> Equil Equil --> BasePH end subgraph Phase_4 ["Titration Loop (Perturbation)"] LoopStart["For each HCl Increment in Array
np.linspace(0.0, 0.02, 100)"] Clone["Clone Baseline State Obj
step_state = rkt.ChemicalState(state)"] Inject["Perturb System Mass Matrix
step_state.add('H+'), step_state.add('Cl-')"] ReSolve["Re-equilibrate Thermodynamic State
solver.solve(step_state)"] Harvest["Extract Speciation Vectors
pH, HCO₃⁻, CO₂(aq), HSO₄⁻"] LoopEnd["Append to list or array"] BasePH --> LoopStart LoopStart --> Clone Clone --> Inject Inject --> ReSolve ReSolve --> Harvest Harvest --> LoopEnd LoopEnd -- "Next Step" --> LoopStart end %% Styling nodes for a clean process block look style Sys fill:#f97316,color:#fff,stroke:#333,stroke-width:2px style Equil fill:#2563eb,color:#fff,stroke:#333,stroke-width:2px style ReSolve fill:#16a34a,color:#fff,stroke:#333,stroke-width:2px

Speciation & Buffer Intensity Analysis #

When plotting the titration curve, distinct phases emerge based on the decomposition of impurities:

1. The Carbonate to Bicarbonate Transition #

Initial acid drops transform carbonate ions into bicarbonate: \[ \text{CO}_3^{2-} + \text{H}^+ \rightarrow \text{HCO}_3^- \]

2. The Bicarbonate Plateau (pH 6.5 - 4.5) #

A prominent flattening occurs where pH drops slowly. This is the primary buffering zone where bicarbonate is converted into aqueous carbon dioxide: \[ \text{HCO}_3^- + \text{H}^+ \rightarrow \text{CO}_2\text{(aq)} + \text{H}_2\text{O} \]

In an open industrial system, this phase is followed by vacuum dechlorination or air-stripping towers to physically remove gas bubbles, breaking the buffer.

3. The Bisulfate Buffer (pH < 2) #

At highly acidic values, secondary buffering is encountered as sulfate converts to bisulfate: \[ \text{SO}_4^{2-} + \text{H}^+ \rightarrow \text{HSO}_4^- \]

Brine ImpurityActive pH RangeChemical Transformation
Carbonate\( > 8.0 \)Converts into soluble bicarbonate species.
Bicarbonate\( 6.5 \rightarrow 4.5 \)Converts to aqueous \( \text{CO}_2 \); forms main buffering plateau.
Sulfate\( < 2.5 \)Absorbs protons to yield intermediate bisulfate ion.
Process Control Tip: Operating automated acid dosing loops inside the steep inflection zone (between pH 4 and 3) requires high-gain tuning configurations or advanced feedforward logic because minimal variations in acid stroke yield massive shifts in output pH.

Source IPython Notebook

Reaktoro Brine Acidification Notebook
Author
Usama Khan
Chemical Engineer by day & Coder at night