Extraction of exchangeable cations in dune sand

Extraction of exchangeable cations in dune sand¶

Written by Svetlana Kyas (ETH Zurich) on Mar 10th, 2022

This tutorial models an extraction of exchangeable cations in dune sand discussed in the previous tutorial. The extractant is represented by the 1-molal NaCl brine. Usually, the extraction must be repeated more than once since not all CaX2 is displaced in the first step.

Note

According to [Appelo and Postma, 2005], due to the low CEC (cation exchange capacity) of the sample, the low concentrations can be analyzed only if pure NaCl is used in the extraction.

Below, we set up the chemical system based on the PHREEQC database phreeqc.dat.

from reaktoro import *

db = PhreeqcDatabase("phreeqc.dat")

Unlike the previous tutorial, the aqueous phase of this chemical system is defined by the list of selected species (that matter the most for this numerical experiment).

# Define an aqueous phase
solution = AqueousPhase("H2O Na+ Cl- H+ OH- K+ Ca+2 Mg+2")
solution.setActivityModel(ActivityModelHKF())

The ion exchange phase is represented by the three species, which activities are to be calculated by the Vanselow approach (using species’ molar fractions).

# Define an ion exchange phase
exchange = IonExchangePhase("CaX2 MgX2 NaX")
exchange.setActivityModel(ActivityModelIonExchangeVanselow())

# Create the chemical system
system = ChemicalSystem(db, solution, exchange)

# Calculate aqueous and exchange properties
aqprops = AqueousProps(system)
exprops = IonExchangeProps(system)
solver = EquilibriumSolver(system)

As the first step, we try to reproduce the following PHREEQC script:

# Define exchanger and solutions
SOLUTION 1 # Pore water
Na 1.1     # mmols
Mg 0.48
Ca 1.9
EXCHANGE 1 # 5 g soil with CEC = 10 meq/kg, or 0.05 mmol X-
-equilibrate 1
X 0.05e-3 # moles
END

In particular, we equilibrate 5 g of sand with 1 kg of porewater. Since the exchanger is given by the NaX amount, we remove the corresponding amount of Na+ to achieve results similar to PHREEQC.

# Define porewater
state = ChemicalState(system)
state.setTemperature(25.0, "celsius")
state.setPressure(1.0, "atm")
state.add("H2O", 1.0, "kg")
state.add("Na+", 1.1, "mmol")
state.add("Mg+2", 0.48, "mmol")
state.add("Ca+2", 1.9, "mmol")
state.add("NaX", 0.05e-3, "mol")
state.add("Na+", -0.05e-3, "mol")

Next, we equilibrate this chemical state and update aqueous and exchange properties, respectively.

solver.solve(state)
aqprops.update(state)
exprops.update(state)

Instead of printing out a complete table of the aqueous and exchange properties, one can fetch only concrete properties, e.g., species amounts, equivalence fractions, etc. Below, we output ionic strength, the charge of the obtained solution, and its pH. Besides, we are also interested to track the amount of exchangeable cations CaX2 and MgX2 alongside NaX.

print("Porewater in equilibrium with exchanger X:")
print("------------------------------------------")
print(f"I  = {aqprops.ionicStrength()} mol/kgw")
print(f"Z  = {state.charge()} eq/kgw")
print(f"pH = {aqprops.pH()}\n")

print(f"n(CaX2) = {exprops.speciesAmount('CaX2')} mole")
print(f"n(MgX2) = {exprops.speciesAmount('MgX2')} mole")
print(f"n(NaX)  = {exprops.speciesAmount('NaX')} mole\n")
Porewater in equilibrium with exchanger X:
------------------------------------------
I  = 0.00526044 mol/kgw
Z  = 0.00581 eq/kgw
pH = 6.99712

n(CaX2) = 2.14623e-05 mole
n(MgX2) = 3.42022e-06 mole
n(NaX)  = 2.349e-07 mole

As the second step, we try to replicate the PHREEQC script

# Mix exchanger with 20 g of extractant
SOLUTION 2 # Extractant solution
Na 1e3
Cl 1e3
USE exchange 1   # ... put 5 g soil in centrifuge tube calculated before
MIX
2 20e-3          # ... add 20 g 1M NaCl
SAVE exchange 1
SAVE solution 3 # centrifuge and decant
END

Here, we place 5 g of soil in a centrifuge tube and extract cations with 20 mL 1-molal NaCl-brine. This equilibration step is accompanied by the set of the following exchange reactions:

\[\begin{split} \begin{alignat}{2} {\rm Na}^+ + \tfrac{1}{2} {\rm CaX}_2 & \rightleftharpoons \tfrac{1}{2} {\rm Ca^{+2}} + {\rm NaX}\\ {\rm Na}^+ + \tfrac{1}{2} {\rm MgX}_2 & \rightleftharpoons \tfrac{1}{2} {\rm Mg^{+2}} + {\rm NaX} \end{alignat} \end{split}\]
scale = 20e-3
state.add("H2O", scale * 1.0, "kg")
state.add("Na+", scale * 1e3, "mmol")
state.add("Cl-", scale * 1e3, "mmol")

solver.solve(state)
aqprops.update(state)
exprops.update(state)

This time, in addition to the ion exchange properties of concrete species, we can inspect the amounts of aqueous ions.

print("Mix exchanger with 20 g of extractant:")
print("--------------------------------------")
print(f"I  = {aqprops.ionicStrength()} mol/kgw")
print(f"Z  = {state.charge()} eq/kgw")
print(f"pH = {aqprops.pH()}\n")

print(f"n(CaX2) = {exprops.speciesAmount('CaX2')} mole")
print(f"n(MgX2) = {exprops.speciesAmount('MgX2')} mole")
print(f"n(NaX)  = {exprops.speciesAmount('NaX')} mole\n")

print(f"m(Ca+2) = {aqprops.speciesMolality('Ca+2')} molal")
print(f"m(Mg+2) = {aqprops.speciesMolality('Mg+2')} molal")
print(f"m(Cl-)  = {aqprops.speciesMolality('Cl-')} molal")
print(f"m(Na+)  = {aqprops.speciesMolality('Na+')} molal")
Mix exchanger with 20 g of extractant:
--------------------------------------
I  = 0.024768 mol/kgw
Z  = 0.00581 eq/kgw
pH = 6.99674

n(CaX2) = 1.96721e-05 mole
n(MgX2) = 3.09676e-06 mole
n(NaX)  = 4.46219e-06 mole

m(Ca+2) = 0.00184353 molal
m(Mg+2) = 0.000467571 molal
m(Cl-)  = 0.0196086 molal
m(Na+)  = 0.0206827 molal

Due to the presence of 1-molal brine, we obtain the one order of magnitude higher ionic strength after the second equilibration. The charge remains the same since the added extractant is neutral. We see that in comparison to the first equilibration, the amount of CaX2 and MgX2 species decrease due to the extensive presence of sodium ions (provided by the extractant), whereas the amount of NaX became one order of magnitude higher.

As the third step, we try to replicate the PHREEQC script

USE exchange 1 # repeat extraction, 2nd time
MIX # 20 g new extractant, 2.5 g of the old extractant
2 20e-3
3 0.125 # 2.5 / 20 part of the old extractant
SAVE solution 4
END

or perform the so-called 2nd extraction, i.e., a fraction 2.5 / 20 = 0.125 of the first extraction is added to the second one.

# Consider 2.5 / 20 part of the old extractant
state.scaleMass(0.125, "kg")

# Take 20 g of the new extractant
scale = 20e-3
state.add("H2O", scale * 1.0, "kg")
state.add("Na+", scale * 1e3, "mmol")
state.add("Cl-", scale * 1e3, "mmol")

solver.solve(state)
aqprops.update(state)
exprops.update(state)

print("Second extraction:")
print("--------------------------------------")
print(f"I  = {aqprops.ionicStrength()} mol/kgw")
print(f"Z  = {state.charge()} eq/kgw")
print(f"pH = {aqprops.pH()}\n")

print(f"n(CaX2) = {exprops.speciesAmount('CaX2')} mole")
print(f"n(MgX2) = {exprops.speciesAmount('MgX2')} mole")
print(f"n(NaX)  = {exprops.speciesAmount('NaX')} mole\n")
Second extraction:
--------------------------------------
I  = 0.159445 mol/kgw
Z  = 0.000711116 eq/kgw
pH = 6.99884

n(CaX2) = 1.08239e-06 mole
n(MgX2) = 1.63006e-07 mole
n(NaX)  = 3.62897e-06 mole

Again, we see an increase of the ionic strength of the mixture by one order of magnitude and further decrease of all ionic species amounts.

Note

Alternatively, the extraction can be performed with NH4Cl-brine.