Source code for pygcc.solid_solution

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 17 16:02:22 2021

@author: adedapo.awolayo and Ben Tutolo, University of Calgary

Copyright (c) 2020 - 2021, Adedapo Awolayo and Ben Tutolo, University of Calgary

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""

import math, os
import numpy as np
from .water_eos import iapws95, ZhangDuan, water_dielec, convert_temperature
from .species_eos import heatcap, supcrtaq
from .read_db import db_reader

[docs]def roundup_tenth(x): return int(math.ceil(x / 10.0)) * 10
[docs]def Molecularweight(): """ This function stores the Molecular weight of all elements """ MW = {'O': 15.9994, 'Ag': 107.8682, 'Al': 26.98154, 'Am': 243.0, 'Ar': 39.948, 'Au': 196.96654, 'B': 10.811, 'Ba': 137.327, 'Be': 9.01218, 'Br': 79.904, 'Ca': 40.078, 'Cd': 112.411, 'Ce': 140.115, 'Cl': 35.4527, 'Co': 58.9332, 'Cr': 51.9961, 'Cs': 132.90543, 'Cu': 63.546, 'Dy': 162.5, 'Er': 167.26, 'Eu': 151.965, 'F': 18.9984, 'Fe': 55.847, 'Ga': 69.723, 'Gd': 157.25, 'H': 1.00794, 'As': 74.92159, 'C': 12.011, 'P': 30.97376, 'He': 4.0026, 'Hg': 200.59, 'Ho': 164.93032, 'I': 126.90447, 'In': 114.82, 'K': 39.0983, 'Kr': 83.8, 'La': 138.9055, 'Li': 6.941, 'Lu': 174.967, 'Mg': 24.305, 'Mn': 54.93805, 'Mo': 95.94, 'N': 14.00674, 'Na': 22.98977, 'Nd': 144.24, 'Ne': 20.1797, 'Ni': 58.69, 'Np': 237.048, 'Pb': 207.2, 'Pd': 106.42, 'Pr': 140.90765, 'Pu': 244.0, 'Ra': 226.025, 'Rb': 85.4678, 'Re': 186.207, 'Rn': 222.0, 'Ru': 101.07, 'S': 32.066, 'Sb': 121.75, 'Sc': 44.95591, 'Se': 78.96, 'Si': 28.0855, 'Sm': 150.36, 'Sn': 118.71, 'Sr': 87.62, 'Tb': 158.92534, 'Tc': 98.0, 'Th': 232.0381, 'Ti': 47.88, 'Tl': 204.3833, 'Tm': 168.93421, 'U': 238.0289, 'V': 50.9415, 'W': 183.85, 'Xe': 131.29, 'Y': 88.90585, 'Yb': 173.04, 'Zn': 65.39, 'Zr': 91.224} return MW
MW = Molecularweight() J_to_cal = 4.184
[docs]class solidsolution_thermo(): """Class to calculate Ideal mixing model for solid-solutions, supporting multisite ideal formalism Parameters ---------- X : float End member mineral volume fraction or mole fraction of Mg in clinopyroxene cpx_Ca : float number of moles of Ca in clinopyroxene formula unit (=1 for Di, Hed) \n T : float, vector Temperature [°C] \n P : float, vector Pressure [bar] \n dbaccessdic : dict dictionary of species from direct-access database, optional, default is speq21 \n solidsolution_type : string specify either 'All' or 'plagioclase' or 'olivine' or 'pyroxene' or 'cpx' or 'alk-feldspar' or 'biotite' to carry-out all or any solid-solution calculations, default is 'All' Dielec_method : string specify either 'FGL97' or 'JN91' or 'DEW' as the method to calculate dielectric constant (optional), if not specified default - 'JN91' ThermoInUnit : string specify either 'cal' or 'KJ' as the input units for species properties (optional), particularly used to covert KJ data to cal by supcrtaq function if not specified default - 'cal' Al_Si : string specify either 'pygcc' or 'Arnórsson_Stefánsson' as the input to express Al and Si species in solid solution (optional), 'Arnórsson_Stefánsson' expresses them as 'Al(OH)4-' and 'H4SiO4(aq)', respectively while pygcc uses 'Al3+' and 'SiO2(aq)', respectively if not specified default - 'pygcc' rhoEG : dict dictionary of water properties like density (rho), dielectric factor (E) and Gibbs Energy (optional) Returns ------- if 'All' is specified for solidsolution_type, each case has the follwowing prefix - AnAb_, FoFa_, EnFe_, cpx_, AlkFed_, Biotite_ \n logK : float, vector logarithmic K values \n Rxn : dict The calculated dictionary of reaction thermodynamic properties has the following properties: * type: solid-solution mineral type, [-] * name: solid-solution name, [K] * formula: solid-solution mineral formula, [-] * MW: Molecular weight, [g/mol] * min: solid-solution mineral properties, ['formula', 'source date', dG[cal/ml], dH[cal/mol], S[cal/mol-K], V[cm3/mol], a[cal/mol-K], b[10^3 cal/mol/K^2], c[10^-5 cal/mol/K]] * spec: list of species, [-] * coeff: list of corresponding coefficients of species above, [-] * nSpec: Total number of species, [-] * V: Molar volume, [cm3/mol] * source: Source of thermo data, [kJ/kg·K] * elements: list of elements and their total numbers, [-] Usage ---------- The general usage of water_dielec is as follows: \n (1) For water dielectric properties at any Temperature and Pressure: \n ss = solidsolution_thermo(T = T, P = P, X = X, solidsolution_type = 'All', Dielec_method = 'JN91'), \n where T is temperature in celsius and P is pressure in bar (2) For water dielectric properties at any Temperature and density : \n ss = solidsolution_thermo(T = T, rho = rho, X = X, solidsolution_type = 'All', Dielec_method = 'JN91'), \n where T is temperature in celsius and rho is density in kg/m³ (3) For water dielectric properties at any Temperature and Pressure on steam saturation curve: \n ss = solidsolution_thermo(T = T, P = 'T', X = X, solidsolution_type = 'All', Dielec_method = 'JN91'), \n where T is temperature in celsius and P is assigned a quoted character 'T' to reflect steam saturation pressure \n ss = solidsolution_thermo(P = P, T = 'P', X = X, solidsolution_type = 'All', Dielec_method = 'JN91'), \n where P is pressure in bar and T is assigned a quoted character 'P' to reflect steam saturation temperature Examples -------- >>> ss = solidsolution_thermo(cpx_Ca = 0.5, X = 0.85, T = 30, P = 50, solidsolution_type = 'All') >>> ss.AnAb_logK, ss.FoFa_logK, ss.EnFe_logK, ss.cpx_logK, ss.AlkFed_logK, ss.Biotite_logK 21.750590, 26.570902, 10.793372, 20.577152, 3.756792, 32.96363 >>> ss = solidsolution_thermo(cpx_Ca = 1, X = 0.85, T = 50, P = 100, solidsolution_type = 'cpx') >>> ss.logK 18.81769186 >>> ss.Rxn {'type': 'cpx', 'name': 'Di85Hed15', 'formula': 'Ca1.00Mg0.85Fe0.15Si2O6', 'MW': 221.2817, 'min': ['Ca1.00Mg0.85Fe0.15Si2O6', ' R&H95, Stef2001', -711392.7391800061, nan, 37.032850185405415, 66.369, 106.66383843212236, -0.019588551625239002, -16326.4818355, -1052.9517208413004, 5.714746653919693e-06], 'spec': ['H+', 'Ca++', 'Mg++', 'Fe++', 'SiO2(aq)', 'H2O'], 'coeff': [-4, 1.0, 0.85, 0.15, 2, 2], 'nSpec': 6, 'V': 66.369, 'source': ' R&H95, Stef2001', 'elements': ['1.0000', 'Ca', '0.8500', 'Mg', '0.1500', 'Fe', '2.0000', 'Si', '6.0000', 'O']} >>> ss = solidsolution_thermo(X = 0.5, T = 50, P = 100, solidsolution_type = 'plagioclase') >>> ss.logK 12.62488394 >>> ss.Rxn {'type': 'plag', 'name': 'An50', 'formula': 'Ca0.50Na0.50Al1.50Si2.50O8', 'MW': 270.215145, 'min': ['Ca0.50Na0.50Al1.50Si2.50O8', ' R&H95, Stef2001', -919965.222804649, nan, 47.33081919017781, 100.43, 131.53908221797, -0.022148661567686426, 32265.761759082205, -1316.0836615678777, 7.719885114722753e-06], 'spec': ['H+', 'Al+++', 'Na+', 'Ca++', 'SiO2(aq)', 'H2O'], 'coeff': [-6.0, 1.5, 0.5, 0.5, 2.5, 3.0], 'nSpec': 6, 'V': 100.43, 'source': ' R&H95, Stef2001', 'elements': ['0.5000', 'Ca', '0.5000', 'Na', '1.5000', 'Al', '2.5000', 'Si', '8.0000', 'O']} """ kwargs = {"X": None, "cpx_Ca": 0.5, "T": None, "P": None, "ThermoInUnit": 'cal', "Dielec_method": None, "solidsolution_type": 'All', "rhoEG": None, "dbaccessdic": None, "Al_Si": 'pygcc'} def __init__(self, **kwargs): self.kwargs = solidsolution_thermo.kwargs.copy() self.__calc__(**kwargs)
[docs] def __calc__(self, **kwargs): # self.kwargs.update(kwargs) self.X = self.kwargs["X"] self.cpx_Ca = self.kwargs["cpx_Ca"] self.ThermoInUnit = self.kwargs["ThermoInUnit"] self.Al_Si = self.kwargs["Al_Si"] self.__checker__(**kwargs) if self.kwargs["solidsolution_type"].lower() == 'all': self.AnAb_logK, self.AnAb_Rxn = self.calclogKAnAb(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) self.FoFa_logK, self.FoFa_Rxn = self.calclogKFoFa(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) self.EnFe_logK, self.EnFe_Rxn = self.calclogKEnFe(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) self.cpx_logK, self.cpx_Rxn = self.calclogKDiHedEnFe(self.cpx_Ca, self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) self.AlkFed_logK, self.AlkFed_Rxn = self.calclogKAbOr(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) self.Biotite_logK, self.Biotite_Rxn = self.calclogKBiotite(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) elif self.kwargs["solidsolution_type"].lower().startswith('plag'): self.logK, self.Rxn = self.calclogKAnAb(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) elif self.kwargs["solidsolution_type"].lower().startswith('ol'): self.logK, self.Rxn = self.calclogKFoFa(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) elif self.kwargs["solidsolution_type"].lower().startswith('py'): self.logK, self.Rxn = self.calclogKEnFe(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) elif self.kwargs["solidsolution_type"].lower().startswith('cpx'): if self.cpx_Ca > 0: self.logK, self.Rxn = self.calclogKDiHedEnFe(self.cpx_Ca, self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) else: self.logK, self.Rxn = self.calclogKEnFe(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) elif self.kwargs["solidsolution_type"].lower().startswith('alk'): self.logK, self.Rxn = self.calclogKAbOr(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method) elif self.kwargs["solidsolution_type"].lower().startswith('bio'): self.logK, self.Rxn = self.calclogKBiotite(self.X, self.T, self.P, self.dbaccessdic, self.rhoEG, Dielec_method = self.Dielec_method)
[docs] def __checker__(self, **kwargs): self.T = self.kwargs["T"] self.P = self.kwargs["P"] self.rhoEG = self.kwargs['rhoEG'] self.Dielec_method = 'JN91' if self.kwargs["Dielec_method"] is None else self.kwargs["Dielec_method"] if (type(self.P) == str) or (type(self.T) == str): if self.P == 'T': self.P = iapws95(T = self.TC).P self.P[np.isnan(self.P) | (self.P < 1)] = 1.0133 elif self.TC == 'P': self.TC = iapws95(P = self.P).TC if np.ndim(self.T) == 0 : self.T = np.array(self.T).ravel() # elif np.size(self.T) == 2: # self.T = np.array([roundup_tenth(j) if j != 0 else 0.01 for j in np.linspace(self.T[0], self.T[-1], 8)]) if np.size(self.P) <= 2: self.P = np.ravel(self.P) self.P = self.P[0]*np.ones(np.size(self.T)) if self.rhoEG is None: if self.Dielec_method.upper() == 'DEW': water = ZhangDuan(T = self.T, P = self.P) else: water = iapws95(T = self.T, P = self.P) rho, dGH2O = water.rho, water.G E = water_dielec(T = self.T, P = self.P, Dielec_method = self.Dielec_method).E self.rhoEG = {'rho': rho, 'E': E, 'dGH2O': dGH2O} if self.kwargs['dbaccessdic'] == None: dbaccess_dir = './default_db/speq21.dat' dbaccess_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), dbaccess_dir) self.dbaccessdic = db_reader(dbaccess = dbaccess_dir).dbaccessdic else: self.dbaccessdic = self.kwargs['dbaccessdic']
[docs] def calclogKAnAb(self, XAn, TC, P, dbaccessdic, rhoEG, Dielec_method = None): """ This function calculates thermodynamic properties of solid solution of Plagioclase minerals Parameters ---------- XAn : volume fraction of Anorthite \n TC : temperature [°C] \n P : pressure [bar] \n dbaccessdic : dictionary of species from direct-access database \n Dielec_method : specify either 'FGL97' or 'JN91' or 'DEW' as the method to calculate dielectric constant (optional), if not specified default - 'JN91' rhoEG : dictionary of water properties like density (rho), dielectric factor (E) and Gibbs Energy (optional) Returns ------- logKplag : logarithmic K values \n Rxn : dictionary of reaction thermodynamic properties Usage ------- The general usage of calclogKAnAb without the optional argument is as follows: \n (1) Not on steam saturation curve: \n [logK, Rxn] = calclogKAnAb(XAn, TC, P, dbaccessdic), \n where T is temperature in celsius and P is pressure in bar; (2) On steam saturation curve: \n [logK, Rxn] = calclogKAnAb(XAn, TC, 'T', dbaccessdic), \n where T is temperature in celsius, followed with a quoted char 'T' \n [logK, Rxn] = calclogKAnAb(XAn, P, 'P', dbaccessdic), \n where P is pressure in bar, followed with a quoted char 'P'. (3) Meanwhile, usage with any specific dielectric constant method ('FGL97') for condition not on steam saturation curve is as follows. Default method is 'JN91' \n [logK, Rxn] = calclogKAnAb(XAn, TC, P, dbaccessdic, Dielec_method = 'FGL97') """ dGH2O = rhoEG['dGH2O'].ravel() TK = convert_temperature( TC, Out_Unit = 'K' ) Rxn = {} Rxn['type'] = 'plag' XAb = 1 - XAn nH = (8 - 4*XAb) nAl = (2 - XAb) nH2O = (4 - 2*XAb) nNa = XAb nCa = (1 - XAb) nSi = (2 + XAb) if XAn == 1: Rxn['name'] ='Anorthite' Rxn['formula'] ='CaAl2(SiO4)2' elif XAn == 0: Rxn['name'] ='Albite' Rxn['formula'] ='NaAlSi3O8' else: Rxn['name'] = 'An%d' % (XAn*100) Rxn['formula'] = 'Ca%1.2f' % nCa + 'Na%1.2f' % nNa + 'Al%1.2f' % nAl + 'Si%1.2f' % nSi + 'O8' Rxn['MW'] = nCa*MW['Ca'] + nNa* MW['Na'] + nAl*MW['Al'] + nSi*MW['Si'] + 8*MW['O'] R = 1.9872041 if self.Al_Si == 'pygcc': dGAl = supcrtaq(TC, P, dbaccessdic['Al+++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGSiO2aq = supcrtaq(TC, P, dbaccessdic['SiO2(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGH = supcrtaq(TC, P, dbaccessdic['H+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) elif self.Al_Si == 'Arnórsson_Stefánsson': dGAl = supcrtaq(TC, P, dbaccessdic['Al(OH)4-'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGSiO2aq = supcrtaq(TC, P, dbaccessdic['H4SiO4(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) nH2O = nH2O + 2*nSi dGNa = supcrtaq(TC, P, dbaccessdic['Na+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGCa = supcrtaq(TC, P, dbaccessdic['Ca++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) if (XAn < 1) & (XAn != 0): Smix=-R*(XAb*np.log((XAb*(4-XAb**2)*(2+XAb)**2)/27) + \ XAn*np.log((XAn*(1+XAn)**2*(3-XAn)**2)/16)) else: Smix=0 dGplag = XAb*dbaccessdic['ss_Albite_high'][2] + XAn*dbaccessdic['ss_Anorthite'][2] + R*298.15*Smix Splag = XAb*dbaccessdic['ss_Albite_high'][4] + XAn*dbaccessdic['ss_Anorthite'][4] - R*Smix Vplag = XAb*dbaccessdic['ss_Albite_high'][5] + XAn*dbaccessdic['ss_Anorthite'][5] Cpplag = [a + b for a, b in zip([XAb*x for x in dbaccessdic['ss_Albite_high'][6:11] ], [XAn*y for y in dbaccessdic['ss_Anorthite'][6:11] ])] Rxn['min'] = [dGplag, np.nan, Splag, Vplag] + Cpplag Rxn['min'].insert(0, Rxn['formula']) Rxn['min'].insert(1,' R&H95, Stef2001') plag = Rxn['min'] dGplagTP = heatcap( T = TC, P = P, method = 'HF76', Species_ppt = plag).dG if self.Al_Si == 'pygcc': coeff = [-nH, nAl, nNa, nCa, nSi, nH2O] spec = ['H+', 'Al+++', 'Na+', 'Ca++', 'SiO2(aq)', 'H2O'] elif self.Al_Si == 'Arnórsson_Stefánsson': coeff = [nAl, nNa, nCa, nSi, -nH2O] spec = ['Al(OH)4-', 'Na+', 'Ca++', 'H4SiO4(aq)', 'H2O'] Rxn['spec'] = [x for x, y in zip(spec, coeff) if y!=0] Rxn['coeff'] = [y for y in coeff if y!=0] Rxn['nSpec'] = len(Rxn['coeff']) if self.Al_Si == 'pygcc': dGrxn = -dGplagTP - nH*dGH + nAl*dGAl + nH2O*dGH2O + nNa*dGNa+ nCa*dGCa + nSi*dGSiO2aq elif self.Al_Si == 'Arnórsson_Stefánsson': dGrxn = -dGplagTP + nAl*dGAl - nH2O*dGH2O + nNa*dGNa+ nCa*dGCa + nSi*dGSiO2aq logKplag = (-dGrxn/R/(TK)/np.log(10)) #np.log10(np.exp(-dGrxn/R/(TK))) Rxn['V'] = Rxn['min'][5] #,'%8.3f') Rxn['source'] = ' R&H95, Stef2001' elements = ['%.4f' % nCa, 'Ca', '%.4f' % nNa, 'Na', '%.4f' % nAl, 'Al', '%.4f' % nSi, 'Si', '8.0000', 'O'] filters = [y for x, y in enumerate(elements) if x%2 == 0 and float(y) == 0] filters = [[x, x+1] for x, y in enumerate(elements) if y in filters] filters = [num for elem in filters for num in elem] Rxn['elements'] = [y for x, y in enumerate(elements) if x not in filters] return logKplag, Rxn
[docs] def calclogKFoFa( self, XFo, TC, P, dbaccessdic, rhoEG, Dielec_method = None ): """ This function calculates thermodynamic properties of solid solution of olivine minerals \n Parameters ---------- XFo : volume fraction of Forsterite \n TC : temperature [°C] \n P : pressure [bar] \n dbaccessdic : dictionary of species from direct-access database \n Dielec_method : specify either 'FGL97' or 'JN91' or 'DEW' as the method to calculate dielectric constant (optional), if not specified default - 'JN91' rhoEG : dictionary of water properties like density (rho), dielectric factor (E) and Gibbs Energy (optional) Returns ------- logK_ol : logarithmic K values \n Rxn : dictionary of reaction thermodynamic properties Usage ------- The general usage of calclogKFoFa without the optional argument is as follows: \n (1) Not on steam saturation curve: \n [logK, Rxn] = calclogKFoFa(XFo, TC, P, dbaccessdic), \n where T is temperature in celsius and P is pressure in bar; (2) On steam saturation curve: \n [logK, Rxn] = calclogKFoFa(XFo, TC, 'T', dbaccessdic), \n where T is temperature in celsius, followed with a quoted char 'T' \n [logK, Rxn] = calclogKFoFa(XFo, P, 'P', dbaccessdic), \n where P is pressure in bar, followed with a quoted char 'P'. (3) Meanwhile, usage with any specific dielectric constant method ('FGL97') for condition not on steam saturation curve is as follows. Default method is 'JN91' \n [logK, Rxn] = calclogKFoFa(XFo, TC, P, dbaccessdic, Dielec_method = 'FGL97') """ Tref = 25; Pref = 1 rho = rhoEG['rho'].ravel(); E = rhoEG['E'].ravel() dGH2O = rhoEG['dGH2O'].ravel() # if no reference Temperature and Pressure is found, append to the bottom if any((TC == Tref) & (P == Pref)) == False: water = iapws95(T = Tref, P = Pref) rhoref, dGH2Oref = water.rho, water.G Eref = water_dielec(T = Tref, P = Pref, Dielec_method = Dielec_method).E TC = np.concatenate([TC, np.ravel(Tref)]) P = np.concatenate([P, np.ravel(Pref)]) rho = np.concatenate([rho, np.ravel(rhoref)]) E = np.concatenate([E, np.ravel(Eref)]) dGH2O = np.concatenate([dGH2O, np.ravel(dGH2Oref)]) rhoEG = {'rho': rho, 'E': E, 'dGH2O': dGH2O} TK = convert_temperature( TC, Out_Unit = 'K' ) Rxn = {} Rxn['type']='ol' XFa = 1-XFo nH = 4 nMg = 2*XFo nFe = 2*XFa nSi = 1 nH2O = 2 if XFo == 1: Rxn['name']='Forsterite' Rxn['formula']='Mg2SiO4' elif XFo == 0: Rxn['name']='Fayalite' Rxn['formula']='Fe2SiO4' else: Rxn['name'] = 'Fo%d' % (XFo*100) Rxn['formula']= 'Mg%1.2f' % nMg + 'Fe%1.2f' % nFe + 'Si%s' % nSi + 'O4' Rxn['MW'] = nMg*MW['Mg'] + nFe*MW['Fe'] + nSi*MW['Si'] + 4*MW['O'] R = 1.9872041 WH = 10366.2/J_to_cal WS = 4/J_to_cal if self.Al_Si == 'pygcc': dGSiO2aq = supcrtaq(TC, P, dbaccessdic['SiO2(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) elif self.Al_Si == 'Arnórsson_Stefánsson': dGSiO2aq = supcrtaq(TC, P, dbaccessdic['H4SiO4(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) nH2O = nH2O - 2*nSi dGH = supcrtaq(TC, P, dbaccessdic['H+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGFe = supcrtaq(TC, P, dbaccessdic['Fe++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGMg = supcrtaq(TC, P, dbaccessdic['Mg++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) if (XFo == 1) | (XFo == 0): Sconf = 0 else: Sconf = -2*R*(XFo*np.log(XFo) + XFa*np.log(XFa)) dG_ol = XFa*dbaccessdic['ss_Fayalite'][2] + XFo*dbaccessdic['ss_Forsterite'][2] S_ol = XFa*dbaccessdic['ss_Fayalite'][4] + XFo*dbaccessdic['ss_Forsterite'][4] + Sconf V_ol = XFa*dbaccessdic['ss_Fayalite'][5] + XFo*dbaccessdic['ss_Forsterite'][5] Cp_ol = [a + b for a, b in zip([XFa*x for x in dbaccessdic['ss_Fayalite'][6:11] ], [XFo*y for y in dbaccessdic['ss_Forsterite'][6:11] ])] WG = WH - TK*WS Gex = WG*XFo*XFa dG_ol = dG_ol + Gex - 298.15*Sconf Rxn['min'] = [dG_ol, np.nan, S_ol, V_ol] + Cp_ol Rxn['min'].insert(0, Rxn['formula']) Rxn['min'].insert(1,' R&H95, Stef2001') if self.Al_Si == 'pygcc': coeff = [-nH, nMg, nFe, nSi, nH2O] spec = ['H+', 'Mg++', 'Fe++', 'SiO2(aq)', 'H2O'] elif self.Al_Si == 'Arnórsson_Stefánsson': coeff = [-nH, nMg, nFe, nSi] spec = ['H+', 'Mg++', 'Fe++', 'H4SiO4(aq)'] Rxn['spec'] = [x for x, y in zip(spec, coeff) if y!=0] Rxn['coeff'] = [y for y in coeff if y!=0] Rxn['nSpec'] = len(Rxn['coeff']) logK_ol = np.zeros([len(TK), 1]) ol = Rxn['min'] dGolTP = heatcap( T = TC, P = P, method = 'HF76', Species_ppt = ol).dG dGrxn = -dGolTP - nH*dGH + nMg*dGMg + nFe*dGFe + nH2O*dGH2O + nSi*dGSiO2aq logK_ol = (-dGrxn/R/(TK)/np.log(10)) #np.log10(np.exp(-dGrxn/R/(TK))) Rxn['min'][2] = dG_ol[(TC == Tref) & (P == Pref)][0] Rxn['V'] = Rxn['min'][5] #,'%8.3f') Rxn['source'] = ' R&H95, Stef2001' elements = ['%.4f' % nMg, 'Mg', '%.4f' % nFe, 'Fe', '%.4f' % nSi, 'Si', '4.0000', 'O'] filters = [y for x, y in enumerate(elements) if x%2 == 0 and float(y) == 0] filters = [[x, x+1] for x, y in enumerate(elements) if y in filters] filters = [num for elem in filters for num in elem] Rxn['elements'] = [y for x, y in enumerate(elements) if x not in filters] if (TC[-1] == Tref) & (P[-1] == Pref): logK_ol = logK_ol[:-1] return logK_ol, Rxn
[docs] def calclogKEnFe( self, XEn, TC, P, dbaccessdic, rhoEG, Dielec_method = None ): """ This function calculates thermodynamic properties of solid solution of pyroxene minerals \n Parameters ---------- XEn : volume fraction of Enstatite \n TC : temperature [°C] \n P : pressure [bar] \n dbaccessdic : dictionary of species from direct-access database \n Dielec_method : specify either 'FGL97' or 'JN91' or 'DEW' as the method to calculate dielectric constant (optional), if not specified default - 'JN91' rhoEG : dictionary of water properties like density (rho), dielectric factor (E) and Gibbs Energy (optional) Returns ------- logK_opx : logarithmic K values \n Rxn : dictionary of reaction thermodynamic properties Usage ------- The general usage of calclogKEnFe without the optional argument is as follows: \n (1) Not on steam saturation curve: \n [logK, Rxn] = calclogKEnFe(XEn, TC, P, dbaccessdic), \n where T is temperature in celsius and P is pressure in bar; (2) On steam saturation curve: \n [logK, Rxn] = calclogKEnFe(XEn, TC, 'T', dbaccessdic), \n where T is temperature in celsius, followed with a quoted char 'T' \n [logK, Rxn] = calclogKEnFe(XEn, P, 'P', dbaccessdic), \n where P is pressure in bar, followed with a quoted char 'P'. (3) Meanwhile, usage with any specific dielectric constant method ('FGL97') for condition not on steam saturation curve is as follows. Default method is 'JN91' \n [logK, Rxn] = calclogKEnFe(XEn, TC, P, dbaccessdic, Dielec_method = 'FGL97') """ rho = rhoEG['rho'].ravel() E = rhoEG['E'].ravel() dGH2O = rhoEG['dGH2O'].ravel() Tref = 25; Pref = 1 # if no reference Temperature and Pressure is found, append to the bottom if any((TC == Tref) & (P == Pref)) == False: water = iapws95(T = Tref, P = Pref) rhoref, dGH2Oref = water.rho, water.G Eref = water_dielec(T = Tref, P = Pref, Dielec_method = Dielec_method).E TC = np.concatenate([TC, np.ravel(Tref)]) P = np.concatenate([P, np.ravel(Pref)]) rho = np.concatenate([rho, np.ravel(rhoref)]) E = np.concatenate([E, np.ravel(Eref)]) dGH2O = np.concatenate([dGH2O, np.ravel(dGH2Oref)]) rhoEG = {'rho': rho, 'E': E, 'dGH2O': dGH2O} TK = convert_temperature( TC, Out_Unit = 'K' ) Rxn = {} Rxn['type'] = 'opx' XFe = 1 - XEn nH = 2 nMg = XEn nFe = XFe nSi = 1 nH2O = 1 if XEn == 1: Rxn['name'] = 'Enstatite' Rxn['formula'] = 'MgSiO3' elif XEn == 0: Rxn['name'] = 'Ferrosilite' Rxn['formula'] = 'FeSiO3' else: Rxn['name'] = 'En%d' % (XEn*100) Rxn['formula'] = 'Mg%1.2f' % nMg + 'Fe%1.2f' % nFe + 'Si%s' % nSi + 'O3' Rxn['MW'] = nMg*MW['Mg'] + nFe*MW['Fe'] + nSi*MW['Si'] + 3*MW['O'] R = 1.9872041 WH = -2600.4/J_to_cal WS = -1.34/J_to_cal if self.Al_Si == 'pygcc': dGSiO2aq = supcrtaq(TC, P, dbaccessdic['SiO2(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) elif self.Al_Si == 'Arnórsson_Stefánsson': dGSiO2aq = supcrtaq(TC, P, dbaccessdic['H4SiO4(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) nH2O = nH2O - 2*nSi dGH = supcrtaq(TC, P, dbaccessdic['H+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGFe = supcrtaq(TC, P, dbaccessdic['Fe++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGMg = supcrtaq(TC, P, dbaccessdic['Mg++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) if (XEn == 1) | (XEn == 0): Sconf = 0 else: Sconf = -1*R*(XEn*np.log(XEn) + XFe*np.log(XFe)) dG_opx = XEn*dbaccessdic['ss_Enstatite'][2] + XFe*dbaccessdic['ss_Ferrosilite'][2] S_opx = XEn*dbaccessdic['ss_Enstatite'][4] + XFe*dbaccessdic['ss_Ferrosilite'][4] + Sconf V_opx = XEn*dbaccessdic['ss_Enstatite'][5] + XFe*dbaccessdic['ss_Ferrosilite'][5] Cp_opx = [a + b for a, b in zip([XEn*x for x in dbaccessdic['ss_Enstatite'][6:11] ], [XFe*y for y in dbaccessdic['ss_Ferrosilite'][6:11] ])] WG = WH-TK*WS Gex = WG*XEn*XFe dG_opx = dG_opx + Gex - 298.15*Sconf Rxn['min'] = [dG_opx, np.nan, S_opx, V_opx] + Cp_opx Rxn['min'].insert(0, Rxn['formula']) Rxn['min'].insert(1, ' R&H95, Stef2001') if self.Al_Si == 'pygcc': coeff = [-nH, nMg, nFe, nSi, nH2O] spec = ['H+', 'Mg++', 'Fe++', 'SiO2(aq)', 'H2O'] elif self.Al_Si == 'Arnórsson_Stefánsson': coeff = [-nH, nMg, nFe, nSi, nH2O] spec = ['H+', 'Mg++', 'Fe++', 'H4SiO4(aq)', 'H2O'] Rxn['spec'] = [x for x, y in zip(spec, coeff) if y != 0] Rxn['coeff'] = [y for y in coeff if y != 0] Rxn['nSpec'] = len(Rxn['coeff']) opx = Rxn['min'] dGopxTP = heatcap( T = TC, P = P, method = 'HF76', Species_ppt = opx).dG dGrxn = -dGopxTP - nH*dGH + nMg*dGMg + nFe*dGFe + nH2O*dGH2O + nSi*dGSiO2aq logK_opx = (-dGrxn/R/(TK)/np.log(10)) # np.log10(np.exp(-dGrxn/R/(TK))) Rxn['min'][2] = dG_opx[(TC == Tref) & (P == Pref)][0] Rxn['V'] = Rxn['min'][5] #,'%8.3f') Rxn['source'] = ' R&H95, Stef2001' elements = ['%.4f' % nMg, 'Mg', '%.4f' % nFe, 'Fe', '%.4f' % nSi, 'Si', '3.0000', 'O'] filters = [y for x, y in enumerate(elements) if x%2 == 0 and float(y) == 0] filters = [[x, x+1] for x, y in enumerate(elements) if y in filters] filters = [num for elem in filters for num in elem] Rxn['elements'] = [y for x, y in enumerate(elements) if x not in filters] if (TC[-1] == Tref) & (P[-1] == Pref): logK_opx = logK_opx[:-1] return logK_opx, Rxn
[docs] def calclogKDiHedEnFe( self, nCa, XMg, TC, P, dbaccessdic, rhoEG, Dielec_method = None ): """ This function calculates thermodynamic properties of solid solution of clinopyroxene minerals (Di, Hed, En and Fe) \n Parameters ---------- nCa : number of moles of Ca in formula unit (=1 for Di, Hed) and must be greater than zero \n XMg : mole fraction of Mg XMg = (nMg/(nFe + nMg)) \n TC : temperature [°C] \n P : pressure [bar] \n dbaccessdic : dictionary of species from direct-access database \n Dielec_method : specify either 'FGL97' or 'JN91' or 'DEW' as the method to calculate dielectric constant (optional), if not specified default - 'JN91' rhoEG : dictionary of water properties like density (rho), dielectric factor (E) and Gibbs Energy (optional) Returns ------- logK_cpx : logarithmic K values \n Rxn : dictionary of reaction thermodynamic properties Usage ------- The general usage of calclogKDiHedEnFe without the optional argument is as follows: \n (1) Not on steam saturation curve: \n [logK, Rxn] = calclogKDiHedEnFe(nCa, XMg, TC, P, dbaccessdic), \n where T is temperature in celsius and P is pressure in bar; (2) On steam saturation curve: \n [logK, Rxn] = calclogKDiHedEnFe(nCa, XMg, TC, 'T', dbaccessdic), \n where T is temperature in celsius, followed with a quoted char 'T' \n [logK, Rxn] = calclogKDiHedEnFe(nCa, XMg, P, 'P', dbaccessdic), \n where P is pressure in bar, followed with a quoted char 'P'. (3) Meanwhile, usage with any specific dielectric constant method ('FGL97') for condition not on steam saturation curve is as follows. Default method is 'JN91' \n [logK, Rxn] = calclogKDiHedEnFe(nCa, XMg, TC, P, dbaccessdic, Dielec_method = 'FGL97') """ rho = rhoEG['rho'].ravel() E = rhoEG['E'].ravel() dGH2O = rhoEG['dGH2O'].ravel() Tref = 25; Pref = 1 # if no reference Temperature and Pressure is found, append to the bottom if any((TC == Tref) & (P == Pref)) == False: water = iapws95(T = Tref, P = Pref) rhoref, dGH2Oref = water.rho, water.G Eref = water_dielec(T = Tref, P = Pref, Dielec_method = Dielec_method).E TC = np.concatenate([TC, np.ravel(Tref)]) P = np.concatenate([P, np.ravel(Pref)]) rho = np.concatenate([rho, np.ravel(rhoref)]) E = np.concatenate([E, np.ravel(Eref)]) dGH2O = np.concatenate([dGH2O, np.ravel(dGH2Oref)]) rhoEG = {'rho': rho, 'E': E, 'dGH2O': dGH2O} TK = convert_temperature( TC, Out_Unit = 'K' ) R = 1.9872041 if nCa >= 1: XDi = XMg XHed = 1-XDi XEn = 0 XFs = 0 else: XDi = XMg*nCa XHed = (1 - XMg)*nCa Xopx = 1 -nCa XEn = XMg*Xopx XFs = (1 - XMg)*Xopx #start on M1 parms WH_M1 = -1910/J_to_cal WS_M1 = -1.05/J_to_cal if nCa < 1: # for the M1 (i.e., opx) component of cpx Mg_M1 = XEn*2 # %multiplying by 2 b/c thermo props multiplied by 2 Fe_M1 = XFs*2 # %multiplying by 2 b/c thermo props multiplied by 2 XFe_M1 = Fe_M1/(Fe_M1 + Mg_M1) XMg_M1 = Mg_M1/(Fe_M1 + Mg_M1) if (XEn == 0) | (XFs == 0): Sconf_M1 = 0 # if you don't do this, you take log of 0 else: Sconf_M1 = -1*R*(XFe_M1*np.log(XFe_M1) + XMg_M1*np.log(XMg_M1)) else: #No M1 if nCa >=1 XMg_M1 = 0; Mg_M1 = 0 XFe_M1 = 0; Fe_M1 = 0 Sconf_M1 = 0 WG_M1 = WH_M1 - TK*WS_M1 Gex_M1 = WG_M1*XFe_M1*XMg_M1 WG_M1_25 = WH_M1 - 298.15*WS_M1 Gex_M1_25 = WG_M1_25*XFe_M1*XMg_M1 # now start on the M2 part WH_M2 = 0./J_to_cal WS_M2 = 0./J_to_cal Fe_M2 = (XHed) Mg_M2 = (XDi) Ca_M2 = (XHed + XDi) Tot_M2 = Fe_M2 + Mg_M2 + Ca_M2 XFe_M2 = Fe_M2/Tot_M2 XMg_M2 = Mg_M2/Tot_M2 XCa_M2 = Ca_M2/Tot_M2 if (XHed == 0) | (XDi == 0): Sconf_M2 = 0 else: #because of the entropy term in WG, Gex and dGol are T-dependent, thus must #do the logK calculation on a piecewise basis. Sconf_M2 = -1*R*( XFe_M2*np.log(XFe_M2) + XMg_M2*np.log(XMg_M2) + \ XCa_M2*np.log(XCa_M2)) WG_M2 = WH_M2 - TK*WS_M2 Gex_M2 = WG_M2*XDi*XHed WG_M2_25 = WH_M2 - 298.15*WS_M2 Gex_M2_25 = WG_M2_25*XFe_M2*XMg_M2 Rxn = {} Rxn['type']='cpx' nH = 4 nMg = round(Mg_M1 + Mg_M2, 2) nFe = round(Fe_M1 + Fe_M2, 2) nCa = round(Ca_M2, 2) nSi = 2 nH2O = 2 if (XEn == 0): if (XFs == 0): if XDi == 1: Rxn['name'] = 'Diopside' elif XHed == 1: Rxn['name'] = 'Hedenbergite' else: Rxn['name'] = 'Di%d' % round(XDi*100) + 'Hed%d' % round(XHed*100) elif (XHed == 0): Rxn['name'] = 'Di%d' % round(XDi*100) + 'Fe%d' % round(XFs*100) elif (XDi == 0): Rxn['name'] = 'Hed%d' % round(XHed*100) + 'Fe%d' % round(XFs*100) else: Rxn['name'] = 'Di%d' % round(XDi*100) + 'Hed%d' % round(XHed*100) + 'Fe%d' % round(XFs*100) elif (XFs == 0): if (XHed == 0): Rxn['name'] = 'Di%d' % round(XDi*100) + 'En%d' % round(XEn*100) elif (XDi == 0): Rxn['name'] = 'Hed%d' % round(XHed*100) + 'En%d' % round(XEn*100) else: Rxn['name'] = 'Di%d' % round(XDi*100) + 'Hed%d' % round(XHed*100) + 'En%d' % round(XEn*100) elif (XHed == 0): Rxn['name'] = 'Di%d' % round(XDi*100) + 'En%d' % round(XEn*100) + 'Fe%d' % round(XFs*100) elif (XDi == 0): Rxn['name'] = 'Hed%d' % round(XHed*100) + 'En%d' % round(XEn*100) + 'Fe%d' % round(XFs*100) else: Rxn['name'] = 'Di%d' % round(XDi*100) + 'Hed%d' % round(XHed*100) + 'En%d' % round(XEn*100) + 'Fe%d' % round(XFs*100) if (nCa == 0): Rxn['formula'] = 'Mg%1.2f' % nMg + 'Fe%1.2f' % nFe + 'Si%s' % nSi + 'O6' elif (nMg == 0): if (nCa == 1) and (nFe == 1): Rxn['formula'] = 'CaFe' + 'Si%s' % nSi + 'O6' else: Rxn['formula'] = 'Ca%1.2f' % nCa + 'Fe%1.2f' % nFe + 'Si%s' % nSi + 'O6' elif (nFe == 0): if (nCa == 1) and (nMg == 1): Rxn['formula'] = 'CaMg' + 'Si%s' % nSi + 'O6' else: Rxn['formula'] = 'Ca%1.2f' % nCa + 'Mg%1.2f' % nMg + 'Si%s' % nSi + 'O6' else: Rxn['formula'] = 'Ca%1.2f' % nCa + 'Mg%1.2f' % nMg + 'Fe%1.2f' % nFe + 'Si%s' % nSi + 'O6' Rxn['MW'] = nCa*MW['Ca'] + nMg*MW['Mg'] + nFe*MW['Fe'] + nSi*MW['Si'] + 6*MW['O'] R = 1.9872041 if self.Al_Si == 'pygcc': dGSiO2aq = supcrtaq(TC, P, dbaccessdic['SiO2(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) elif self.Al_Si == 'Arnórsson_Stefánsson': dGSiO2aq = supcrtaq(TC, P, dbaccessdic['H4SiO4(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) nH2O = nH2O - 2*nSi dGH = supcrtaq(TC, P, dbaccessdic['H+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGFe = supcrtaq(TC, P, dbaccessdic['Fe++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGMg = supcrtaq(TC, P, dbaccessdic['Mg++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGCa = supcrtaq(TC, P, dbaccessdic['Ca++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) # Addition of M1 and M2 sites Sconf = Sconf_M1 + Sconf_M2 dG_cpx_noGex = 2*XFs*dbaccessdic['ss_Ferrosilite'][2] + 2*XEn*dbaccessdic['ss_Clinoenstatite'][2] + \ XHed*dbaccessdic['ss_Hedenbergite'][2] + XDi*dbaccessdic['ss_Diopside'][2] dG_cpx_25 = dG_cpx_noGex + Gex_M1_25 + Gex_M2_25 S_cpx = 2*XFs*dbaccessdic['ss_Ferrosilite'][4] + 2*XEn*dbaccessdic['ss_Clinoenstatite'][4] + \ XHed*dbaccessdic['ss_Hedenbergite'][4] + XDi*dbaccessdic['ss_Diopside'][4] + Sconf V_cpx = 2*XFs*dbaccessdic['ss_Ferrosilite'][5] + 2*XEn*dbaccessdic['ss_Clinoenstatite'][5] + \ XHed*dbaccessdic['ss_Hedenbergite'][5] + XDi*dbaccessdic['ss_Diopside'][5] Cp_cpx = [i + j + k + l for i, j, k, l in zip([2*XFs*a for a in dbaccessdic['ss_Ferrosilite'][6:11] ], [2*XEn*b for b in dbaccessdic['ss_Clinoenstatite'][6:11] ], [XHed*c for c in dbaccessdic['ss_Hedenbergite'][6:11] ], [XDi*d for d in dbaccessdic['ss_Diopside'][6:11] ] ) ] # because of the entropy term in WG, Gex and dGol are T-dependent, thus must do the logK calculation on a piecewise basis. dG_cpx = dG_cpx_25 + Gex_M1 + Gex_M2 - TK*Sconf Rxn['min'] = [dG_cpx, np.nan, S_cpx, V_cpx] + Cp_cpx Rxn['min'].insert(0, Rxn['formula']) Rxn['min'].insert(1,' R&H95, Stef2001') if self.Al_Si == 'pygcc': coeff = [-nH, nCa, nMg, nFe, nSi, nH2O] spec = ['H+', 'Ca++', 'Mg++', 'Fe++', 'SiO2(aq)', 'H2O'] elif self.Al_Si == 'Arnórsson_Stefánsson': coeff = [-nH, nCa, nMg, nFe, nSi, nH2O] spec = ['H+', 'Ca++', 'Mg++', 'Fe++', 'H4SiO4(aq)', 'H2O'] Rxn['spec'] = [x for x, y in zip(spec, coeff) if y != 0] Rxn['coeff'] = [y for y in coeff if y != 0] Rxn['nSpec'] = len(Rxn['coeff']) cpx = Rxn['min'] dGcpxTP = heatcap( T = TC, P = P, method = 'HF76', Species_ppt = cpx).dG dGrxn = - dGcpxTP - nH*dGH + nCa*dGCa + nMg*dGMg + nFe*dGFe + nH2O*dGH2O + nSi*dGSiO2aq logK_cpx = (-dGrxn/R/(TK)/np.log(10)) # np.log10(np.exp(-dGrxn/R/(TK))) Rxn['min'][2] = dG_cpx[(TC == Tref) & (P == Pref)][0] Rxn['V'] = Rxn['min'][5] Rxn['source'] = ' R&H95, Stef2001' elements = ['%.4f' % nCa, 'Ca', '%.4f' % nMg, 'Mg', '%.4f' % nFe, 'Fe', '%.4f' % nSi, 'Si', '6.0000', 'O'] filters = [y for x, y in enumerate(elements) if x%2 == 0 and float(y) == 0] filters = [[x, x+1] for x, y in enumerate(elements) if y in filters] filters = [num for elem in filters for num in elem] Rxn['elements'] = [y for x, y in enumerate(elements) if x not in filters] if (TC[-1] == Tref) & (P[-1] == Pref): logK_cpx = logK_cpx[:-1] return logK_cpx, Rxn
[docs] def calclogKAbOr( self, XAb, TC, P, dbaccessdic, rhoEG, Dielec_method = None): """ This function calculates thermodynamic properties of solid solution of Alkaline-Feldspar minerals Parameters ---------- XAb : volume fraction of Albite \n TC : temperature [°C] \n P : pressure [bar] \n dbaccessdic : dictionary of species from direct-access database \n Dielec_method : specify either 'FGL97' or 'JN91' or 'DEW' as the method to calculate dielectric constant (optional), if not specified default - 'JN91' rhoEG : dictionary of water properties like density (rho), dielectric factor (E) and Gibbs Energy (optional) Returns ------- logKalkfeld: logarithmic K values \n Rxn : dictionary of reaction thermodynamic properties Usage ------- The general usage of calclogKAnAb without the optional argument is as follows: \n (1) Not on steam saturation curve: \n [logK, Rxn] = calclogKAbOr(XAb, TC, P, dbaccessdic), \n where T is temperature in celsius and P is pressure in bar; (2) On steam saturation curve: \n [logK, Rxn] = calclogKAbOr(XAb, TC, 'T', dbaccessdic), \n where T is temperature in celsius, followed with a quoted char 'T' \n [logK, Rxn] = calclogKAbOr(XAb, P, 'P', dbaccessdic), \n where P is pressure in bar, followed with a quoted char 'P'. (3) Meanwhile, usage with any specific dielectric constant method ('FGL97') for condition not on steam saturation curve is as follows. Default method is 'JN91' \n [logK, Rxn] = calclogKAbOr(XAb, TC, P, dbaccessdic, Dielec_method = 'FGL97') """ rho = rhoEG['rho'].ravel() E = rhoEG['E'].ravel() dGH2O = rhoEG['dGH2O'].ravel() Tref = 25; Pref = 1 # if no reference Temperature and Pressure is found, append to the bottom if any((TC == Tref) & (P == Pref)) == False: water = iapws95(T = Tref, P = Pref) rhoref, dGH2Oref = water.rho, water.G Eref = water_dielec(T = Tref, P = Pref, Dielec_method = Dielec_method).E TC = np.concatenate([TC, np.ravel(Tref)]) P = np.concatenate([P, np.ravel(Pref)]) rho = np.concatenate([rho, np.ravel(rhoref)]) E = np.concatenate([E, np.ravel(Eref)]) dGH2O = np.concatenate([dGH2O, np.ravel(dGH2Oref)]) rhoEG = {'rho': rho, 'E': E, 'dGH2O': dGH2O} TK = convert_temperature( TC, Out_Unit = 'K' ) Rxn = {} Rxn['type'] = 'Alk feldspar' XOr = 1 - XAb nNa = XAb nK = (1 - XAb) nH = 4 nAl = 1 nH2O = 2 nSi = 3 if XAb == 1: Rxn['name'] ='Albite' Rxn['formula'] ='NaAlSi3O8' elif XAb == 0: Rxn['name'] ='K-Feldspar' Rxn['formula'] ='KAlSi3O8' else: Rxn['name'] = 'Alkfeld%d' % (XAb*100) Rxn['formula'] = 'Na%1.2f' % nNa + 'K%1.2f' % nK + 'Al%1.2f' % nAl + 'Si%1.2f' % nSi + 'O8' Rxn['MW'] = nNa* MW['Na'] + nK*MW['K'] + nAl*MW['Al'] + nSi*MW['Si'] + 8*MW['O'] R = 1.9872041 WH = 23800/J_to_cal WS = 0/J_to_cal if self.Al_Si == 'pygcc': dGAl = supcrtaq(TC, P, dbaccessdic['Al+++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGSiO2aq = supcrtaq(TC, P, dbaccessdic['SiO2(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGH = supcrtaq(TC, P, dbaccessdic['H+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) elif self.Al_Si == 'Arnórsson_Stefánsson': dGAl = supcrtaq(TC, P, dbaccessdic['Al(OH)4-'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGSiO2aq = supcrtaq(TC, P, dbaccessdic['H4SiO4(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) nH2O = nH2O + 2*nSi dGNa = supcrtaq(TC, P, dbaccessdic['Na+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGK = supcrtaq(TC, P, dbaccessdic['K+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) if (XAb < 1) & (XAb != 0): Sconf = -R*(XOr*np.log(XOr) + XAb*np.log(XAb)) else: Sconf = 0 dGalk = XOr*dbaccessdic['ss_K-feldspar'][2] + XAb*dbaccessdic['ss_Albite_high'][2] Salk = XOr*dbaccessdic['ss_K-feldspar'][4] + XAb*dbaccessdic['ss_Albite_high'][4] + Sconf Valk = (XOr*dbaccessdic['ss_K-feldspar'][5] + XAb*dbaccessdic['ss_Albite_high'][5]) #*(P - 1) Cpalk = [a + b for a, b in zip([XOr*x for x in dbaccessdic['ss_K-feldspar'][6:11] ], [XAb*y for y in dbaccessdic['ss_Albite_high'][6:11] ])] WG = WH - TK*WS Gex = WG*XAb*XOr dGalk = dGalk + Gex - 298.15*Sconf Rxn['min'] = [dGalk, np.nan, Salk, Valk] + Cpalk Rxn['min'].insert(0, Rxn['formula']) Rxn['min'].insert(1,' R&H95, A&S99') alk = Rxn['min'] dGalkTP = heatcap( T = TC, P = P, method = 'HF76', Species_ppt = alk).dG if self.Al_Si == 'pygcc': coeff = [-nH, nAl, nNa, nK, nSi, nH2O] spec = ['H+', 'Al+++', 'Na+', 'K+', 'SiO2(aq)', 'H2O'] elif self.Al_Si == 'Arnórsson_Stefánsson': coeff = [nAl, nNa, nK, nSi, -nH2O] spec = ['Al(OH)4-', 'Na+', 'K+', 'H4SiO4(aq)', 'H2O'] Rxn['spec'] = [x for x, y in zip(spec, coeff) if y!=0] Rxn['coeff'] = [y for y in coeff if y!=0] Rxn['nSpec'] = len(Rxn['coeff']) if self.Al_Si == 'pygcc': dGrxn = - dGalkTP - nH*dGH + nAl*dGAl + nH2O*dGH2O + nNa*dGNa+ nK*dGK + nSi*dGSiO2aq elif self.Al_Si == 'Arnórsson_Stefánsson': dGrxn = -dGalkTP + nAl*dGAl - nH2O*dGH2O + nNa*dGNa+ nK*dGK + nSi*dGSiO2aq logKalkfeld = (-dGrxn/R/(TK)/np.log(10)) # np.log10(np.exp(-dGrxn/R/(TK))) Rxn['min'][2] = dGalk[(TC == Tref) & (P == Pref)][0] Rxn['V'] = Rxn['min'][5] #,'%8.3f') Rxn['source'] = ' R&H95, A&S99' elements = ['%.4f' % nNa, 'Na', '%.4f' % nK, 'K', '%.4f' % nAl, 'Al', '%.4f' % nSi, 'Si', '8.0000', 'O'] filters = [y for x, y in enumerate(elements) if x%2 == 0 and float(y) == 0] filters = [[x, x+1] for x, y in enumerate(elements) if y in filters] filters = [num for elem in filters for num in elem] Rxn['elements'] = [y for x, y in enumerate(elements) if x not in filters] if (TC[-1] == Tref) & (P[-1] == Pref): logKalkfeld = logKalkfeld[:-1] return logKalkfeld, Rxn
[docs] def calclogKBiotite( self, XPh, TC, P, dbaccessdic, rhoEG, Dielec_method = None): """ This function calculates thermodynamic properties of solid solution of Biotite minerals Parameters ---------- XAb : volume fraction of Albite \n TC : temperature [°C] \n P : pressure [bar] \n dbaccessdic : dictionary of species from direct-access database \n Dielec_method : specify either 'FGL97' or 'JN91' or 'DEW' as the method to calculate dielectric constant (optional), if not specified default - 'JN91' rhoEG : dictionary of water properties like density (rho), dielectric factor (E) and Gibbs Energy (optional) Returns ------- logKalkfeld: logarithmic K values \n Rxn : dictionary of reaction thermodynamic properties Usage ------- The general usage of calclogKAnAb without the optional argument is as follows: \n (1) Not on steam saturation curve: \n [logK, Rxn] = calclogKAbOr(XAb, TC, P, dbaccessdic), \n where T is temperature in celsius and P is pressure in bar; (2) On steam saturation curve: \n [logK, Rxn] = calclogKAbOr(XAb, TC, 'T', dbaccessdic), \n where T is temperature in celsius, followed with a quoted char 'T' \n [logK, Rxn] = calclogKAbOr(XAb, P, 'P', dbaccessdic), \n where P is pressure in bar, followed with a quoted char 'P'. (3) Meanwhile, usage with any specific dielectric constant method ('FGL97') for condition not on steam saturation curve is as follows. Default method is 'JN91' \n [logK, Rxn] = calclogKAbOr(XAb, TC, P, dbaccessdic, Dielec_method = 'FGL97') """ rho = rhoEG['rho'].ravel() E = rhoEG['E'].ravel() dGH2O = rhoEG['dGH2O'].ravel() Tref = 25; Pref = 1 # if no reference Temperature and Pressure is found, append to the bottom if any((TC == Tref) & (P == Pref)) == False: water = iapws95(T = Tref, P = Pref) rhoref, dGH2Oref = water.rho, water.G Eref = water_dielec(T = Tref, P = Pref, Dielec_method = Dielec_method).E TC = np.concatenate([TC, np.ravel(Tref)]) P = np.concatenate([P, np.ravel(Pref)]) rho = np.concatenate([rho, np.ravel(rhoref)]) E = np.concatenate([E, np.ravel(Eref)]) dGH2O = np.concatenate([dGH2O, np.ravel(dGH2Oref)]) rhoEG = {'rho': rho, 'E': E, 'dGH2O': dGH2O} TK = convert_temperature( TC, Out_Unit = 'K' ) Rxn = {} Rxn['type'] = 'Biotite' XAn = 1 - XPh nFe = 3*XAn nMg = 3*XPh nK = 1 nH = 10 nAl = 1 nH2O = 6 nSi = 3 if XPh == 0: Rxn['name'] ='Annite' Rxn['formula'] ='KFe3AlSi3O10(OH)2' elif XPh == 1: Rxn['name'] ='Phlogopite' Rxn['formula'] ='KMg3AlSi3O10(OH)2' else: Rxn['name'] = 'Biotite_Mg_Fe_ratio%s' % np.round(nMg/nFe, 2) Rxn['formula'] = 'K%1.2f' % nK + 'Mg%1.2f' % nMg + 'Fe%1.2f' % nFe + 'Al%1.2f' % nAl + 'Si%1.2f' % nSi + 'O10(OH)2' Rxn['MW'] = nK*MW['K'] + nMg* MW['Mg'] + nFe* MW['Fe'] + nAl*MW['Al'] + nSi*MW['Si'] + 12*MW['O'] + 2*MW['H'] R = 1.9872041 WH = 9000/J_to_cal WS = 0/J_to_cal dGAl = supcrtaq(TC, P, dbaccessdic['Al+++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGSiO2aq = supcrtaq(TC, P, dbaccessdic['SiO2(aq)'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGH = supcrtaq(TC, P, dbaccessdic['H+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGMg = supcrtaq(TC, P, dbaccessdic['Mg++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGFe = supcrtaq(TC, P, dbaccessdic['Fe++'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) dGK = supcrtaq(TC, P, dbaccessdic['K+'], Dielec_method = Dielec_method, ThermoInUnit = self.ThermoInUnit, **rhoEG) if (XAn < 1) & (XAn != 0): Sconf = -3*R*(XAn*np.log(XAn) + XPh*np.log(XPh)) else: Sconf = 0 dGbiot = XAn*dbaccessdic['ss_Annite'][2] + XPh*dbaccessdic['ss_Phlogopite'][2] Sbiot = XAn*dbaccessdic['ss_Annite'][4] + XPh*dbaccessdic['ss_Phlogopite'][4] + Sconf Vbiot = (XAn*dbaccessdic['ss_Annite'][5] + XPh*dbaccessdic['ss_Phlogopite'][5]) Cpbiot = [a + b for a, b in zip([XAn*x for x in dbaccessdic['ss_Annite'][6:11] ], [XPh*y for y in dbaccessdic['ss_Phlogopite'][6:11] ])] WG = WH - TK*WS Gex = WG*XAn*XPh dGbiot = dGbiot + Gex - 298.15*Sconf Rxn['min'] = [dGbiot, np.nan, Sbiot, Vbiot] + Cpbiot Rxn['min'].insert(0, Rxn['formula']) Rxn['min'].insert(1,' R&H95, P&H99') biot = Rxn['min'] dGbiotTP = heatcap( T = TC, P = P, method = 'HF76', Species_ppt = biot).dG coeff = [-nH, nAl, nK, nMg, nFe, nSi, nH2O] spec = ['H+', 'Al+++', 'K+', 'Mg++', 'Fe++', 'SiO2(aq)', 'H2O'] Rxn['spec'] = [x for x, y in zip(spec, coeff) if y!=0] Rxn['coeff'] = [y for y in coeff if y!=0] Rxn['nSpec'] = len(Rxn['coeff']) dGrxn = - dGbiotTP - nH*dGH + nAl*dGAl + nMg*dGMg + nFe*dGFe + nK*dGK + nSi*dGSiO2aq + nH2O*dGH2O logKbiot = (-dGrxn/R/(TK)/np.log(10)) # np.log10(np.exp(-dGrxn/R/(TK))) Rxn['min'][2] = dGbiot[(TC == Tref) & (P == Pref)][0] Rxn['V'] = Rxn['min'][5] Rxn['source'] = ' R&H95, P&H99' elements = ['%.4f' % nK, 'K', '%.4f' % nMg, 'Mg', '%.4f' % nFe, 'Fe', '%.4f' % nAl, 'Al', '%.4f' % nSi, 'Si', '12.0000', 'O', '2.0000', 'H'] filters = [y for x, y in enumerate(elements) if x%2 == 0 and float(y) == 0] filters = [[x, x+1] for x, y in enumerate(elements) if y in filters] filters = [num for elem in filters for num in elem] Rxn['elements'] = [y for x, y in enumerate(elements) if x not in filters] if (TC[-1] == Tref) & (P[-1] == Pref): logKbiot = logKbiot[:-1] return logKbiot, Rxn