Extraction of exchangeable cations in dune sand#

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

Attention

Always make sure you are using the latest version of Reaktoro. Otherwise, some new features documented on this website will not work on your machine and you may receive unintuitive errors. Follow these update instructions to get the latest version of Reaktoro!

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.set(ActivityModelPhreeqc(db))

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.set(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.005260225592837035 mol/kgw
Z  = 0.0058100000000005005 eq/kgw
pH = 6.995546487156604

n(CaX2) = 2.1449479331759283e-05 mole
n(MgX2) = 3.43330910881372e-06 mole
n(NaX)  = 2.3442311935399766e-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.02476699441156581 mol/kgw
Z  = 0.0058100000000005005 eq/kgw
pH = 6.9909386673993295

n(CaX2) = 1.964122525465311e-05 mole
n(MgX2) = 3.1426794128612414e-06 mole
n(NaX)  = 4.4321906654713035e-06 mole

m(Ca+2) = 0.0018434889986957228 molal
m(Mg+2) = 0.0004675071780220438 molal
m(Cl-)  = 0.019607843178174295 molal
m(Na+)  = 0.020681929267998757 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.15943811677096814 mol/kgw
Z  = 0.0007111160541952141 eq/kgw
pH = 6.975444726222138

n(CaX2) = 1.0962956481328021e-06 mole
n(MgX2) = 1.750325903035551e-07 mole
n(NaX)  = 3.5771030257277935e-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.