ionize is a Python module for calculating the properties of ions and electrolyte solutions.
# Setup
from __future__ import print_function, absolute_import, division
import ionize
import pprint
from matplotlib import pyplot as plot
%matplotlib inline
import numpy as np
np.set_printoptions(precision=3)
The basic building block that ionize is the a single ionic species. Small ions (as opposed to ion complexes or polyions) are represented by the Ion class.
An Ion has the following properties that can be set on initialization.
Only the name, valence, pKa, and mobility are required parameters. All other parameters are optional.
acid = ionize.Ion('myAcid', [-1], [5], [-25e-9])
base = ionize.Ion('myBase', [1], [8], [20e-9])
print(acid) # The string includes only the class and name.
print(repr(base)) # The representation contains enough information to reconstruct the ion.
Ion('myAcid') Ion(valence=[1], heat_capacity=None, reference_temperature=25.0, name='myBase', molecular_weight=None, reference_pKa=[8.0], enthalpy=None, reference_mobility=[2e-08], nightingale_data=None, alias=None)
The guranteed interfaces of ion species are:
In addition, BaseIon subclasses usually have some unique interfaced.
print('myAcid Ka at (I=0 M) =', acid.acidity())
print('myAcid Ka at (I=0.5 M) =', acid.acidity(ionic_strength=0.5))
pH = np.linspace(0,14)
for I in [None, 0., 0.001, 0.01, 0.1]:
mu = [base.mobility(p, I) for p in pH]
if I is not None:
label = 'I={} M'.format(I)
else:
label = 'I=None'
plot.plot(pH, mu, label=label)
plot.xlabel('pH'); plot.xlim(0, 14)
plot.ylabel('effective mobility (m^2/v/s)'); plot.ylim(-.1e-8, 2.1e-8)
plot.legend()
plot.show()
myAcid Ka at (I=0 M) = [ 1.001e-05] myAcid Ka at (I=0.5 M) = [ 1.781e-05]
Note the difference between ionic_strength parameters here. If ionic_strength is 0, the numerical value of 0 is used in each calculation. However, it is impossible to have a solution of pH 0 with ionic_strength of 0.
When the default value of None is used for ionic_strength, ionize uses the minimum ionic strength at the selected pH.
Individually initializing ions is error-prone and time-consuming. To simplify the process, load ions from the database by initializing the database, and accessing the database like a dictionary.
db = ionize.Database()
histidine = db['histidine']
print(repr(histidine))
for ionic_strength in (None, 0):
mu_histidine = [histidine.mobility(p, ionic_strength=ionic_strength) for p in pH]
plot.plot(pH, mu_histidine, label="I={}".format(ionic_strength))
plot.xlabel('pH'); plot.xlim([0, 14])
plot.ylabel('effective mobility (m^2/v/s)')
plot.legend()
plot.show()
Ion(valence=[-1, 1, 2], heat_capacity=[-233.0, 176.0, 0.0], reference_temperature=25.0, name='histidine', molecular_weight=155.15, reference_pKa=[9.33, 6.04, 2.0], enthalpy=[43800.0, 29500.0, 3600.0], reference_mobility=[-2.8300000000000002e-08, 2.8800000000000003e-08, 4.47e-08], nightingale_data=None, alias=None)
You can also search for ions in the database by name using search() method of Database. search() will return a list of ion names, so load the ion when you find what you want.
print("Search results for 'amino'\n--------------------------")
pprint.pprint(db.search('amino'))
print("\nSearch results for 'chloric'\n----------------------------")
pprint.pprint(db.search('chloric'))
print("\nSearch results for 'per'\n------------------------")
pprint.pprint(db.search('per'))
print('\nOh, copper is what I was looking for.')
print(db.load('copper'))
Search results for 'amino' -------------------------- ('2-amino-2-methyl-1-propanol', 'e-aminocaproic acid', 'gamma-aminobutyric acid', 'o-aminobenzoic acid', 'p-aminobenzoic acid') Search results for 'chloric' ---------------------------- ('chloric acid', 'hydrochloric acid', 'perchloric acid') Search results for 'per' ------------------------ ('copper', 'diperodone', 'perchloric acid', 'periodic acid', 'permanganic acid', 'peroxysulfuric acid', 'perrhenic acid', 'piperidine') Oh, copper is what I was looking for. Ion('copper')
You can get the database data as a dictionary using the data method.
print(len(db.data), 'ions in database.')
522 ions in database.
Getting the properties of a single ionic species in solution is useful, but the real challenge of dealing with aqueous solutions of ions is finding properties based on the equilibrium state of multiple ionic species. ionize can perform those calculations using the Solution class. Solution objects are initialized using ionize.Solution(ions, concentrations), where ions is a list of Ion objects and concentration is a list concentrations of the ions, with concentrations in molar.
buffer=ionize.Solution([db['tris'], db['chloride']], [0.1, 0.085])
print('pH =', buffer.pH)
print('I =', buffer.ionic_strength, 'M')
print('conductivity =', buffer.conductivity(), 'S/m')
print('buffering capacity =', buffer.buffering_capacity(), 'M')
print('debye length =', buffer.debye(), 'm')
pH = 7.417824869226217 I = 0.08500041539867613 M conductivity = 0.813690187538 S/m buffering capacity = 0.029570419065 M debye length = 1.4742500535996404e-09 m
/home/lewis/Documents/github/ionize/ionize/Ion/BaseIon.py:63: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future. assert getattr(self, prop) == getattr(other, prop)
Solutions can be initialized with ion names instead of ion objects.
sol = ionize.Solution(['bis-tris', 'acetic acid'], [0.1, 0.03])
print([ion.name for ion in sol.ions])
print(sol.concentration('acetic acid'))
['bis-tris', 'acetic acid'] 0.03
/home/lewis/Documents/github/ionize/ionize/Ion/BaseIon.py:63: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future. assert getattr(self, prop) == getattr(other, prop)
We can iterate through solutions to calculate the pH of a titration between two ions.
c_tris = 0.1
c_hcl = np.linspace(0.0, 0.2, 50)
t_pH = [ionize.Solution(['tris', 'hydrochloric acid'], [c_tris, c_h]).pH for c_h in c_hcl]
plot.plot(c_hcl/c_tris, t_pH)
plot.xlabel('[HCl]/[Tris]')
plot.ylabel('pH')
plot.show()
/home/lewis/Documents/github/ionize/ionize/Ion/BaseIon.py:63: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future. assert getattr(self, prop) == getattr(other, prop)
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) <ipython-input-9-41987a31970c> in <module>() 1 c_tris = 0.1 2 c_hcl = np.linspace(0.0, 0.2, 50) ----> 3 t_pH = [ionize.Solution(['tris', 'hydrochloric acid'], [c_tris, c_h]).pH for c_h in c_hcl] 4 5 plot.plot(c_hcl/c_tris, t_pH) <ipython-input-9-41987a31970c> in <listcomp>(.0) 1 c_tris = 0.1 2 c_hcl = np.linspace(0.0, 0.2, 50) ----> 3 t_pH = [ionize.Solution(['tris', 'hydrochloric acid'], [c_tris, c_h]).pH for c_h in c_hcl] 4 5 plot.plot(c_hcl/c_tris, t_pH) /home/lewis/Documents/github/ionize/ionize/Solution/__init__.py in __init__(self, ions, concentrations) 128 "Solvent components cannot be manually added to Solution." 129 --> 130 self._equilibrate() 131 132 def temperature(self, temperature=None): /home/lewis/Documents/github/ionize/ionize/Solution/equilibrium.py in _equilibrate(self) 152 153 if I_small < I/2.: --> 154 raise RuntimeError('Large ions may be contributing to charge, ' 155 'pH estimate is inaccurate.') 156 RuntimeError: Large ions may be contributing to charge, pH estimate is inaccurate.
A Solution can also be initialized without ions, e.g. as water.
water = ionize.Solution()
print('I =', water.ionic_strength, 'M')
print('pH =', water.pH)
print('conductivity =', water.conductivity(), 'S/m')
A Solution can also be added and multiplied. This can be useful when calculating the results of diltuions, as below.
print('Stock:', buffer)
dilution = 0.5 * buffer + 0.5 * water
print('Dilution:', dilution)
Solutions can be titrated to a specified pH. To do so, make a solution, and then specify a titrant, a property, and a target.
buff = ionize.Solution(['tris'], 0.1)
print(buff.titrate('hydrochloric acid', 8.2))
print(buff.titrate('hydrochloric acid', 3))
print(buff.conductivity())
print(repr(buff.titrate('hydrochloric acid', 3, titration_property = 'conductivity')))
print(repr(buff.titrate('hydrochloric acid', 8)))