<jupyter_start><jupyter_text>[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/CovingtonResearchGroup/olm-examples/blob/master/Basic-carbonate-calculations.ipynb)  An Introduction to geochemical calculations in the Olm python package The python [*olm*](https://github.com/CovingtonResearchGroup/olm) package provides a variety of functions to make basic geochemical calculations for carbonate waters.<jupyter_code>#To install olm in the current session, use pip
!pip install olm-karst<jupyter_output><empty_output><jupyter_text>Solution objects Many of the functions either create or make use of a [`solution`](https://olm.readthedocs.io/en/master/olm.general.solution.htmlolm.general.solution) object, which stores the chemical concentrations and other parameters (e.g. temperature, conductance) of the solution of interest. You can obtain the list of possible ions within a solution object using the `olm.general.getProperties()`<jupyter_code>from olm.general import getProperties
#The dictionary returned contains the available species, their weights, charges, and radii.
getProperties()<jupyter_output><empty_output><jupyter_text>You can create solution objects manually, as below, though this would be a relatively uncommon workflow.<jupyter_code>from olm.general import solution

#We will create a solution with a few of the species listed above
ions = ['Ca','Cl','H2CO3']
concentrations = [15,2.5,0.0006]
sol = solution(ions,concentrations, units='mg/L')<jupyter_output><empty_output><jupyter_text>Solutions have several convenience functions attached to them.<jupyter_code>#You can retrieve the concentration in mg/L, mol/L or meq using mg(), mol(), or meq()
print("Cl concentration is =", sol.mg('Cl'), 'mg/L' )
print("Cl concentration is =", sol.mol('Cl'), 'mol/L' )
print("Cl concentration is =", sol.meq('Cl'), 'meq/L' )
print(" ")

#You can retrieve the activity of a given species
print("Activity of Ca is =",sol.activity('Ca'),"mol/L")

#You can calculate the charge balance
print("The charge balance of the solution is", sol.charge_balance(), '%')
#Note that the charge doesn't balance well because we used a random set of species concentrations
# that aren't physically realistic.<jupyter_output><empty_output><jupyter_text>The olm.general module In addition to defining the `solution` object, the [`olm.general`](https://olm.readthedocs.io/en/master/olm.htmlolm-general-module) module contains functions that are useful for some common geochemical calculations. A few examples are given below.  Temperature conversion<jupyter_code>from olm.general import CtoK, KtoC
print('25 C is equal to', CtoK(25), 'K')<jupyter_output><empty_output><jupyter_text>Concentration conversions<jupyter_code>from olm.general import molL_to_mgL, mgL_to_molL, mmolL_to_meqL, molL_to_meqL

#Any of the above functions can be used to convert concentration units for any 
#species in the getProperties() dictionary shown above using the string that defines 
#the species.

#For example, to convert mg/L to mol/L, just specify the concentration and a string that contains the 
# species name
print("15 mg/L Ca is", mgL_to_molL(15, 'Ca'),'mol/L Ca' )<jupyter_output><empty_output><jupyter_text>Dealing with specific conductance data The conductivity of a solution is often standardized to specific conductance at 25 C. Most field conductivity meters or data loggers can provide raw conductivity or specific conductance corrected to 25 C. The function [`condTo25()`](https://olm.readthedocs.io/en/master/olm.general.condTo25.htmlolm.general.condTo25) will convert raw conductivity values to specific conductance at 25 C.<jupyter_code>from olm.general import condTo25
raw_cond = 155
temp = 4.3
#The first argument is the raw conductivity value, the second is the temp
#You can also provide an array or pandas Series object rather than a single value.
condTo25(raw_cond, temp)<jupyter_output><empty_output><jupyter_text>Within carbonate waters, specific conductance is often used to estimate the hardness or calcium concentration. `olm.general` provides a couple of functions to make these estimates using a global relation from Krawczyk and Ford (2006). *While this approximation provides a rough number, such estimations are always better when made using a relationship built from water chemistry samples at the field site where you are working.* The two available functions are [`olm.general.condTo25()`](https://olm.readthedocs.io/en/master/olm.general.condTo25.htmlolm.general.condTo25) and [`olm.general.HardnessFromCond()`](https://olm.readthedocs.io/en/master/olm.general.HardnessFromCond.htmlolm.general.HardnessFromCond)<jupyter_code>from olm.general import CaFromCond, HardnessFromCond

#For example, we could use our value of 155 above to estimate Ca concentration 
#(assuming low concentration of Mg and other unrelated ions, such as Cl and NO3)

#Here we use a raw conductivity of 155 at 4.3 C and specify mol_L=False in order to obtain mg/L
print('Ca concentration is approximately =',CaFromCond(155, T_C=4.3, mol_L=False),'mg/L')<jupyter_output><empty_output><jupyter_text>The olm.calcite module The [`olm.calcite`](https://olm.readthedocs.io/en/master/olm.htmlolm-calcite-module) contains much of the central functionality of *olm*. It allows one to create `solution` objects from limited information, such as hardness and pCO2 or pH. The functions that create solutions undertake speciation calculations with the provided information assuming a pure $H_2 O-CO_2-CaCO_3$ system, which is a reasonable approximation for many carbonate waters.  Creating `solution` objects from limited chemical parameters<jupyter_code>from olm.calcite import solutionFromCaPCO2, solutionFrompHCaRelaxed
Ca_mg_L = 33.5
Ca_mol_L = mgL_to_molL(Ca_mg_L, 'Ca')
pCO2 = 0.005 #atm
#Create a solution object from Ca concentration (mol/L) and PCO2 (atm)
sol1 = solutionFromCaPCO2(Ca_mol_L, pCO2, T_C=15.)<jupyter_output><empty_output><jupyter_text>Below we can see a dictionary containing all of the species that were added to the solution in the speciation calculation conducted by *olm*.<jupyter_code>sol1.ions
#Olm also calculates the pH and stores it in the solution object
sol1.pH<jupyter_output><empty_output><jupyter_text>Similarly, we could have used pH rather than pCO2.<jupyter_code>pH = 7.26
sol2 = solutionFrompHCaRelaxed(Ca_mol_L, pH, T_C=15.)
sol2.ions<jupyter_output><empty_output><jupyter_text>Calcite saturation state The `olm.calcite` module contains many functions used to calculate solution equilibria with respect to calcite.<jupyter_code>from olm.calcite import concCaEqFromPCO2, concCaEqFromSolution, PCO2EqFromCa
#You can calculate equilibrium Ca concentrations using pCO2
print('Equilibrium Ca concentration at pCO2=0.0001 atm',concCaEqFromPCO2(0.005, T_C=15.),'mol/L')
#Or equilibrium pCO2 for a given Ca concentration.
print('Equilibrium pCO2 for Ca concentration of 0.000378 mol/L',PCO2EqFromCa(0.00148, T_C=15.),'atm')
#You can also calculate equilibrium Ca for a given solution object
CaEq = concCaEqFromSolution(sol1)
Ca = sol1.ions['Ca']['conc_mol']
print('Equilibrium Ca concentration for sol1 is ',CaEq, 'mol/L')

print(' ')

print('Saturation ratio for sol1 is Ca/Ca_eq =', Ca/CaEq)<jupyter_output><empty_output><jupyter_text>Using *olm* to estimate calcite dissolution rates *Olm* contains two main methods for estimating calcite dissolution rates. One uses the **PWP equation** (three variations of this method are available), the other uses the equation from **Palmer (1991)**, which is based on the same experimental results but fits the data more closely near saturation. While it is possible to calculate PWP rates from activities of the relevant species, the simplest methods for calculating dissolution rates use solution objects.<jupyter_code>from olm.calcite import pwpFromSolution, palmerFromSolution, pwp_to_mm_yr
#Calculating pwp rate from a solution object
pwp_rate = pwpFromSolution(sol1)

print('PWP rate =',pwp_rate,'mmol/cm^2/s')

pwp_rate_mm_yr = pwp_to_mm_yr(pwp_rate, rho=2.6)#need density of calcite
print('PWP rate = ',pwp_rate_mm_yr, 'mm/yr')

#Calculating Palmer rate from a solution object
palmer_rate = palmerFromSolution(sol1, impure=True)
#impure sets whether to use Palmer equation for impure calcite (limestone) or pure calcite

print('Palmer rate = ',palmer_rate, 'mm/yr')

#One can also estimate dissolution rates directly from Ca and PCO2 values
from olm.calcite import dissRateFromCaPCO2
Ca = 33.5
pCO2 = 0.005
T_C = 15
dissRateFromCaPCO2(Ca, pCO2, T_C, method = 'Palmer')<jupyter_output><empty_output><jupyter_text>Estimating errors on dissolution rates [`olm.calcite.dissRateFromCaPCO2`](https://olm.readthedocs.io/en/master/olm.calcite.dissRateFromCaPCO2.htmlolm.calcite.dissRateFromCaPCO2) enables estimation of uncertainty in dissolution rates given uncertainties in Ca and PCO2. These estimates are made using Monte Carlo error propagation.<jupyter_code>#To estimate uncertainties you set error=True and provide percent errors on Ca and pCO2 (1=100%)
rate, rate_err = dissRateFromCaPCO2(Ca,
                                    pCO2, 
                                    T_C, 
                                    method='Palmer', 
                                    error=True, 
                                    Ca_err=0.1, 
                                    PCO2_err=0.15)

print('Dissolution rate is ',rate,'+/-',rate_err,'mm/yr')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08788198103266597, 'fraction_numerical': 0.018018967334035827, 'mean_word_length': 5.408507765023633, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 57, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '121845', 'license': 'MIT', 'n_tokens_mistral': 3079, 'n_tokens_neox': 2816, 'n_words': 981, 'path': 'Basic-carbonate-calculations.ipynb', 'repo_name': 'CovingtonResearchGroup/olm-examples', 'size': '9490'}