VolFe 0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1534 @@
1
+ # model_dependent_variables.py
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ import gmpy2 as gp
6
+ import math
7
+ import densityx as dx
8
+ import PySulfSat as ss
9
+
10
+ #import VolFe.melt_gas as mg
11
+
12
+ #from VolFe.melt_gas import *
13
+ import VolFe.melt_gas as mg
14
+
15
+ ################
16
+ ### Contents ###
17
+ ################
18
+ # Solubility constants
19
+ # Solid/liquid saturation
20
+ # Equilibrium constants
21
+ # Fugacity coefficients
22
+ # Speciation
23
+ # Isotope fractionation factors
24
+
25
+ #################################################################################################################################
26
+ ##################################################### SOLUBILITY CONSTANTS ######################################################
27
+ #################################################################################################################################
28
+
29
+ ###################################
30
+ ### Solubility constant for H2O ###
31
+ ###################################
32
+ def C_H2O(PT,melt_wf,species,models):
33
+
34
+ """
35
+ Solubility constant for disolving H2O in the melt.
36
+
37
+
38
+ Parameters
39
+ ----------
40
+ PT: pandas.DataFrame
41
+ Dataframe of pressure-temperature conditions:
42
+ pressure (bars) as "P"
43
+ temperature ('C) as "T"
44
+
45
+ melt_wf: pandas.DataFrame
46
+ Dataframe of melt composition, not used but kept in case dependence on melt composition is required.
47
+
48
+ species: pandas.DataFrame
49
+ Dataframe of species.csv file.
50
+
51
+ models: pandas.DataFrame
52
+ Typically dataframe of models.csv file
53
+ Minimum requirement is dataframe with row labels of "Hspeciation" and "water" and column label of "option"
54
+ See models.csv for available options
55
+
56
+ Returns
57
+ -------
58
+ Solubility constant for H2O as <class 'mpfr'>
59
+
60
+ """
61
+
62
+ if models.loc["Hspeciation","option"] == "none": ### C_H2O = (xmH2O)^2/fH2O ### (mole fraction)
63
+ model = models.loc["water","option"]
64
+ if model == "ETN-1" or model == "PST-9": # Fitted to ETN-1 and PST-9 from Lesne et al. (2011) 162:133-151
65
+ C = 4.77591e-6
66
+ elif model == "VES-9": # Fitted to VES-9 from Lesne et al. (2011) 162:133-151
67
+ C = 5.46061e-6
68
+ elif model == "rhyolite": # Fitted to Blank et al. (1993) and Silver et al. (1990) datasets
69
+ C = 5.13488743E-06
70
+ elif model == "evo": # Fitted to match EVo
71
+ C = 2.782e-6
72
+ elif model == "Lesne11mod": # modified general model from Lesne et al. (2011) 162:133-151
73
+ C = 5.62316e-6
74
+ elif model == "AllisonDataComp": # fitted to experimental data compilation from Allison et al. (2022) for H2O < 6 wt%
75
+ C = 4.6114e-6
76
+ elif model == "test": #test
77
+ R_ = 83.144621 # cm3 bar K−1 mol−1
78
+ DV = 12 # cm3/mol
79
+ P0 = 1.0 # bar
80
+ A = 4.6114e-6
81
+ B = -((DV/(R_*T_K))*(P-P0))
82
+ C = A*gp.exp(B)
83
+ elif model == "test2": # for Ptot paper
84
+ C = gp.exp(-12.29)
85
+ elif model == "carbon":
86
+ C = 1.5e-9
87
+ elif model == "test3":
88
+ C = 6.22885E-09 # like 1000 ppm H2O at 730 bar
89
+
90
+ elif models.loc["Hspeciation","option"] == "linear": ### C_H2O = xmH2O/fH2O ### (mole fraction)
91
+ C = 0.00007925494 # like AllisonDataComp... I think.
92
+
93
+ else: ### C_H2O = xmH2Omol/fH2O ### (mole fraction)
94
+ P = PT['P']
95
+ model = models.loc["water","option"]
96
+ P0 = 1.0 # bar
97
+ R = 83.15 # cm3 etc.
98
+ T0 = 1473.15 # K
99
+ if model == "Dixon95": # Dixon et al. (1995) - no compositional dependence
100
+ DV = 12.
101
+ A = 3.28e-5
102
+ C = A*gp.exp((-DV*(P-P0))/(R*T0))
103
+ elif model == "alkali basalt": # Lesne et al. (2011) 162:133-151 eqn 31 with added RT term otherwise it will not work
104
+ A = 5.71e-5 # XmH2Om0
105
+ DV = 26.9 # VH2Om0 in cm3/mol
106
+ C = A*gp.exp((-DV*(P-P0))/(R*T0))
107
+ elif model == "ETN-1": # Fitted to ETN-1 and VES-9 Xm_H2Omol calculated at 1200 'C data from Lesne et al. (2011) 162:133-151
108
+ C = 3.3989655e-6
109
+ elif model == "VES-9": # Fitted to ETN-1 and VES-9 Xm_H2Omol calculated at 1200 'C data from Lesne et al. (2011) 162:133-151
110
+ C = 3.3989655e-6
111
+ elif model == "PST-9": # Fitted to PST-9 Xm_H2Omol calculated at 1200 'C data from Lesne et al. (2011) 162:133-151
112
+ C = 1.7022269e-6
113
+
114
+ return C
115
+
116
+
117
+
118
+ ##############################################
119
+ ### Solubility constant for carbon dioxide ###
120
+ ##############################################
121
+ def C_CO3(PT,melt_wf,species,models): ### C_CO2,T = xmCO2,T/fCO2 ### (mole fraction) ***except Shishkina14 - wmCO2 ppm***
122
+
123
+ """
124
+ Solubility constant for disolving CO2 in the melt: C_CO2,T = xmCO2,T/fCO2 [all oxidised carbon - i.e., CO2mol and CO32- - as CO2,T]
125
+
126
+
127
+ Parameters
128
+ ----------
129
+ PT: pandas.DataFrame
130
+ Dataframe of pressure-temperature conditions
131
+ pressure (bars) as "P"
132
+ temperature ('C) as "T"
133
+
134
+ melt_wf: pandas.DataFrame
135
+ Dataframe of melt composition (SiO2, TiO2, etc.)
136
+ Not normally required unless "carbon dioxide" option requires melt composition.
137
+
138
+ species: pandas.DataFrame
139
+ Dataframe of species.csv file.
140
+
141
+ models: pandas.DataFrame
142
+ Typically dataframe of models.csv file
143
+ Minimum requirement is dataframe with row label of "carbon dioxide" and column label of "option"
144
+ See models.csv for available options
145
+
146
+ Returns
147
+ -------
148
+ Solubility constant for CO2 as <class 'mpfr'>
149
+
150
+ """
151
+
152
+ model = models.loc["carbon dioxide","option"]
153
+
154
+ P = PT['P']
155
+ T_K = PT['T']+273.15
156
+ # Calculate cation proportions with no volatiles but correct Fe speciation if available (a la Dixon 1997)
157
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"no","no")
158
+
159
+ R = 83.15
160
+ T0 = 1473.15 # K
161
+ PI = -6.5*(melt_comp["Si"]+melt_comp["Al"]) + 20.17*(melt_comp["Ca"] + 0.8*melt_comp["K"] + 0.7*melt_comp["Na"] + 0.4*melt_comp["Mg"] + 0.4*melt_comp["FeT"]) # Dixon (1997) Am. Min. 82:368-378
162
+ PI_ = (melt_comp["Ca"] + 0.8*melt_comp["K"] + 0.7*melt_comp["Na"] + 0.4*melt_comp["Mg"] + 0.4*melt_comp["FeT"])/(melt_comp["Si"] + melt_comp["Al"]) # Shishkina et al. (2014) Chem. Geol. 388:112-129
163
+ DH = -13.1 # kJ/mol # Lesne et al. (2011) CMP 162:153-168 from basanite of Holloway & Blank (1994)
164
+
165
+ if model == "Dixon95": # Dixon et al. (1995)
166
+ DV = 23. # cm3/mol
167
+ P0 = 1.0 # bar
168
+ A = 3.8e-7
169
+ B = (-DV*(P-P0))/(R*T0)
170
+ C = A*gp.exp(B)
171
+ elif model == "Dixon97": # Compositional dependence from Dixon (1997) Am. Min. 82:368-378 as shown by Witham et al. (2012) [assumes PI-SiO2 relationship in caption of figre 2 is 10.19 instead of 10.9 - if 10.9 is assumed you get negative C_CO3]
172
+ DV = 23 # cm3/mol
173
+ P0 = 1.0 # bar
174
+ A = (7.94e-7)*(PI+0.762)
175
+ B = (-DV*(P-P0))/(R*T0)
176
+ C = A*gp.exp(B)
177
+ elif model == "Lesne11": # Lesne et al. (2011)
178
+ DV = 23 # cm3/mol
179
+ P0 = 1.0 # bar
180
+ A = 7.94e-7*((((871*PI)+93.0)/1000.0)+0.762)
181
+ B = (-DV*(P-P0))/(R*T0)
182
+ C = A*gp.exp(B)
183
+ elif model == "VES-9": # Lesne et al. (2011) CMP 162:153-168
184
+ DV = 31.0 # cm3/mol
185
+ P0 = 1000.0 # bar
186
+ A = gp.exp(-14.10)
187
+ B = -((DV/(R*T_K))*(P-P0)) + (DH/R)*((1.0/T0) - (1.0/T_K))
188
+ C = A*gp.exp(B)
189
+ elif model == "ETN-1": # Lesne et al. (2011) CMP 162:153-168
190
+ DV = 23.0 # cm3/mol
191
+ P0 = 1000.0 # bar
192
+ A = gp.exp(-14.55)
193
+ B = -((DV/(R*T_K))*(P-P0)) + (DH/R)*((1.0/T0) - (1.0/T_K))
194
+ C = A*gp.exp(B)
195
+ elif model == "PST-9": # Lesne et al. (2011) CMP 162:153-168
196
+ DV = 6.0 # cm3/mol
197
+ P0 = 1000.0 # bar
198
+ A = gp.exp(-14.74)
199
+ B = -((DV/(R*T_K))*(P-P0)) + (DH/R)*((1.0/T0) - (1.0/T_K))
200
+ C = A*gp.exp(B)
201
+ elif model == "Shishkina14": # modified from Shishkina et al. (2014) Chem. Geol. 388:112-129
202
+ A = 1.164 # modified by converting P^A to APyCO2 but only including data up to and including 400 MPa
203
+ B = 6.71*PI_-1.345
204
+ C = A*gp.exp(B)
205
+ elif model == "Sunset Crater": # Sunset Crater from Allison et al. (2022) CMP 177:40
206
+ R_ = 83.144621 # cm3 bar K−1 mol−1
207
+ DV = 16.40 # cm3/mol
208
+ P0 = 1000.0 # bar
209
+ A = gp.exp(-14.67)
210
+ B = -((DV/(R_*T_K))*(P-P0))
211
+ C = A*gp.exp(B)
212
+ elif model == "SFVF": # SFVF from Allison et al. (2022) CMP 177:40
213
+ R_ = 83.144621 # cm3 bar K−1 mol−1
214
+ DV = 15.02 # cm3/mol
215
+ P0 = 1000.0 # bar
216
+ A = gp.exp(-14.87)
217
+ B = -((DV/(R_*T_K))*(P-P0))
218
+ C = A*gp.exp(B)
219
+ elif model == "Erebus": # Erebus from Allison et al. (2022) CMP 177:40
220
+ R_ = 83.144621 # cm3 bar K−1 mol−1
221
+ DV = -14.65 # cm3/mol
222
+ P0 = 1000.0 # bar
223
+ A = gp.exp(-14.65)
224
+ B = -((DV/(R_*T_K))*(P-P0))
225
+ C = A*gp.exp(B)
226
+ elif model == "Vesuvius": # Vesuvius from Allison et al. (2022) CMP 177:40
227
+ R_ = 83.144621 # cm3 bar K−1 mol−1
228
+ DV = 24.42 # cm3/mol
229
+ P0 = 1000.0 # bar
230
+ A = gp.exp(-14.04)
231
+ B = -((DV/(R_*T_K))*(P-P0))
232
+ C = A*gp.exp(B)
233
+ elif model == "Etna": # Etna from Allison et al. (2022) CMP 177:40
234
+ R_ = 83.144621 # cm3 bar K−1 mol−1
235
+ DV = 21.59 # cm3/mol
236
+ P0 = 1000.0 # bar
237
+ A = gp.exp(-14.28)
238
+ B = -((DV/(R_*T_K))*(P-P0))
239
+ C = A*gp.exp(B)
240
+ elif model == "Stromboli": # Stromboli from Allison et al. (2022) CMP 177:40
241
+ R_ = 83.144621 # cm3 bar K−1 mol−1
242
+ DV = 14.93 # cm3/mol
243
+ P0 = 1000.0 # bar
244
+ A = gp.exp(-14.68)
245
+ B = -((DV/(R_*T_K))*(P-P0))
246
+ C = A*gp.exp(B)
247
+ elif model == "Basanite": # Basanite composition from Holloway and Blank (1994), data from Allison et al. (2022) CMP 177:40
248
+ R_ = 83.144621 # cm3 bar K−1 mol−1
249
+ DV = 21.72 # cm3/mol
250
+ P0 = 1000.0 # bar
251
+ A = gp.exp(-14.32)
252
+ B = -((DV/(R_*T_K))*(P-P0))
253
+ C = A*gp.exp(B)
254
+ elif model == "Leucite": # Leucite composition from Thibault and Holloway (1994), data from Allison et al. (2022) CMP 177:40
255
+ R_ = 83.144621 # cm3 bar K−1 mol−1
256
+ DV = 21.53 # cm3/mol
257
+ P0 = 1000.0 # bar
258
+ A = gp.exp(-13.36)
259
+ B = -((DV/(R_*T_K))*(P-P0))
260
+ C = A*gp.exp(B)
261
+ elif model == "AH3 phonotephrite": # AH3 Phonotephrite composition from Vetere et al. (2014), data from Allison et al. (2022) CMP 177:40
262
+ R_ = 83.144621 # cm3 bar K−1 mol−1
263
+ DV = 30.45 # cm3/mol
264
+ P0 = 1000.0 # bar
265
+ A = gp.exp(-13.26)
266
+ B = -((DV/(R_*T_K))*(P-P0))
267
+ C = A*gp.exp(B)
268
+ elif model == "N72 Basalt": # N72 basalt composition from Shishkina et al. (2010), data from Allison et al. (2022) CMP 177:40
269
+ R_ = 83.144621 # cm3 bar K−1 mol−1
270
+ DV = 19.05 # cm3/mol
271
+ P0 = 1000.0 # bar
272
+ A = gp.exp(-14.86)
273
+ B = -((DV/(R_*T_K))*(P-P0))
274
+ C = A*gp.exp(B)
275
+ elif model == "Allison22mod": # modified from Allison et al. (2022) CMP 177:40
276
+ P0 = 1000. # bars
277
+ R_ = 83.144621 # cm3 bar K−1 mol−1
278
+ DV = -3350.650 + 3375.552*(Si+Na) + 2625.385*Ti + 3105.426*Al + 3628.018*Fe2 + 3323.320*(Mg+Ca) + 3795.115*K + 47.004*(Na/(Na+K)) # cm/mol
279
+ lnK0 = -128.365 + 114.098*Si + 92.263*(Ti+Al) + 122.644*(Fe2+Ca+Na) + 111.549*Mg + 138.855*K + 2.239*(Na/(Na+K))
280
+ A = gp.exp(lnK0)
281
+ B = ((-1.*DV)*(P-P0))/(R_*T_K)
282
+ C = A*gp.exp(B)
283
+ elif model == "scaledCsulfate": # O'Neill & Mavrogenes (2022) GCA 334:368-382 eq[12a]
284
+ # Mole fractions in the melt on cationic lattice (all Fe as FeO) no volatiles
285
+ lnC = -8.02 + ((21100. + 44000.*Na + 18700.*Mg + 4300.*Al + 44200.*K + 35600.*Ca + 12600.*Mn + 16500.*FeT)/T_K) #CS6+ = [S6+, ppm]/fSO3
286
+ Csulfate = gp.exp(lnC)*KOSg2(PT,models) # ppm S
287
+ lnCsulfate = math.log(Csulfate)
288
+ lnC=-0.46*lnCsulfate
289
+ A = gp.exp(lnC)
290
+ DV = 23.
291
+ P0 = 1000. # bars
292
+ R_ = 83.144621 # cm3 bar K−1 mol−1
293
+ B = ((-1.*DV)*(P-P0))/(R_*T_K)
294
+ C = A*gp.exp(B)
295
+ elif model == "Blank93": # Blank et al. (1993) - rhyolite - tried for workshop
296
+ DV = 28 # cm3/mol
297
+ P0 = 1.0 # bar
298
+ A = gp.exp(-14.45)
299
+ B = (-DV*(P-P0))/(R*(850.+273.15))
300
+ C = A*gp.exp(B)
301
+ elif model == "Behrens04fit": # Fit to Behrens et al. (2004) - tried for workshop
302
+ DV = 41.8 # cm3/mol
303
+ P0 = 1.0 # bar
304
+ A = gp.exp(-14.2)
305
+ B = (-DV*(P-P0))/(R*(1250.+273.15))
306
+ C = A*gp.exp(B)
307
+ elif model == "dacite": # Fit to Behrens et al. (2004) using Ptot
308
+ DV = 36.5 # cm3/mol
309
+ P0 = 1.0 # bar
310
+ A = gp.exp(-14.3)
311
+ B = (-DV*(P-P0))/(R*(1250.+273.15))
312
+ C = A*gp.exp(B)
313
+ elif model == "test": # for Ptot paper!!!
314
+ P0 = 1.0 # bar
315
+ # "CO2mol"
316
+ #DV1 = 24.2340838328 # cm3/mol
317
+ #A1 = gp.exp(-14.92978383)
318
+ #B1 = (-DV1*(P-P0))/(R*T_K)
319
+ # "CO32-"
320
+ #DV2 = 8.912862511 # cm3/mol
321
+ #A2 =gp.exp(])
322
+ #B2 = (-DV2*(P-P0))/(R*T_K)
323
+ #C = A1*gp.exp(B1) + A2*gp.exp(B2)
324
+ # "average"
325
+ DV2 = 16.57 # cm3/mol
326
+ A2 =gp.exp(-15.275)
327
+ B2 = (-DV2*(P-P0))/(R*T_K)
328
+ C = A2*gp.exp(B2)
329
+ elif model == "water":
330
+ C = 27.e-6
331
+ return C
332
+
333
+
334
+ ########################################
335
+ ### solubility constant for sulfide ###
336
+ ########################################
337
+ def C_S(PT,melt_wf,species,models): ### C_S = wmS2-*(fO2/fS2)^0.5 ### (weight ppm)
338
+
339
+ """
340
+ Solubility constant for disolving S in the melt as S2- in ppmw: C_S = wmS2-*(fO2/fS2)^0.5
341
+
342
+
343
+ Parameters
344
+ ----------
345
+ PT: pandas.DataFrame
346
+ Dataframe of pressure-temperature conditions
347
+ pressure (bars) as "P"
348
+ temperature ('C) as "T"
349
+
350
+ melt_wf: pandas.DataFrame
351
+ Dataframe of melt composition (SiO2, TiO2, etc.)
352
+
353
+ species: pandas.DataFrame
354
+ Dataframe of species.csv file.
355
+
356
+ models: pandas.DataFrame
357
+ Typically dataframe of models.csv file
358
+ Minimum requirement is dataframe with row label of "sulfide" and column label of "option"
359
+ See models.csv for available options
360
+
361
+ Returns
362
+ -------
363
+ Solubility constant for CO2 as <class 'mpfr'>
364
+
365
+ """
366
+
367
+ model = models.loc["sulfide","option"]
368
+
369
+ T = PT['T'] + 273.15 # T in K
370
+
371
+ def ONeill21(T,melt_comp):
372
+ lnC = (8.77 - (23590.0/T) + (1673.0/T)*(6.7*(melt_comp["Na"]+melt_comp["K"]) + 4.9*melt_comp["Mg"] + 8.1*melt_comp["Ca"] + 8.9*(melt_comp["FeT"]+melt_comp["Mn"]) + 5.0*melt_comp["Ti"] + 1.8*melt_comp["Al"] - 22.2*melt_comp["Ti"]*(melt_comp["FeT"]+melt_comp["Mn"]) + 7.2*melt_comp["FeT"]*melt_comp["Si"]) - 2.06*math.erf(-7.2*(melt_comp["FeT"]+melt_comp["Mn"])))
373
+ return lnC
374
+
375
+ if model == "ONeill21":
376
+ # Mole fractions in the melt on cationic lattice (all Fe as FeO) no volatiles
377
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"no","no")
378
+ lnC = ONeill21(T,melt_comp)
379
+
380
+ if model == "ONeill21dil":
381
+ # Mole fractions in the melt on cationic lattice (all Fe as FeO) no volatiles
382
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"water","no")
383
+ lnC = ONeill21(T,melt_comp)
384
+
385
+ if model == "ONeill21hyd":
386
+ # Mole fractions in the melt on cationic lattice (all Fe as FeO) no volatiles
387
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"no","no")
388
+ lnC_nondil = ONeill21(T,melt_comp)
389
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"water","no")
390
+ lnCdil = ONeill21(T,melt_comp)
391
+ lnCH = (melt_comp["H"]*(6.4 + 12.4*melt_comp["H"] - 20.3*melt_comp["Si"] + 73.0*(melt_comp["Na"]+melt_comp["K"])))
392
+ lnC = lnCdil+lnCH
393
+
394
+ elif model == "FR54-S1":
395
+ lnC = math.log(((1.3e-4)*10000.))
396
+
397
+ C = math.exp(lnC)
398
+ return C
399
+
400
+
401
+
402
+ ########################################
403
+ ### solubility constant for sulfate ###
404
+ ########################################
405
+ def C_SO4(PT,melt_wf,species,models): ### C_SO4 = wmS6+*(fS2*fO2^3)^-0.5 ### (weight ppm)
406
+ model = models.loc["sulfate","option"]
407
+ T = PT['T'] + 273.15 # T in Kelvin
408
+ P = PT['P'] # P in bars
409
+ slope = 115619.707 # slope for T-dependence for melt inclusion fits
410
+
411
+ if model == "Nash19": # Nash et al. (2019) EPSL 507:187-198
412
+ S = 1. # S6+/S2- ratio of S6+/S2- of 0.5
413
+ Csulfide = C_S(PT,melt_wf,species,models)
414
+ A = PT_KCterm(PT,melt_wf,species,models) # P, T, compositional term from Kress & Carmicheal (1991)
415
+ B = (8743600/T**2) - (27703/T) + 20.273 # temperature dependence from Nash et al. (2019)
416
+ a = 0.196 # alnfO2 from Kress & Carmicheal (1991)
417
+ F = 10**(((math.log10(S))-B)/8.)
418
+ fO2 = math.exp(((math.log(0.5*F))-A)/a)
419
+ Csulfate = (S*Csulfide)/(fO2**2)
420
+ elif model == "S6ST":
421
+ Csulfide = C_S(PT,melt_wf,species,models)
422
+ fO2 = f_O2(PT,melt_wf,species,models)
423
+ S6ST_ = melt_wf["S6ST"]
424
+ S = overtotal2ratio(S6ST_)
425
+ Csulfate = (S*Csulfide)/(fO2**2)
426
+ elif model == "Hawaii":
427
+ #Csulfate = gp.exp(30.4) # Using Brounce et al. (2017) dataset at 1200 'C
428
+ Csulfate = math.exp(slope*(1./T) -48.)
429
+ elif model == "Etna":
430
+ Csulfate = math.exp(slope*(1./T) -50.15)
431
+ elif model == "Fuego":
432
+ Csulfate = math.exp(slope*(1./T) -48.5)
433
+ elif model == "Erta Ale":
434
+ Csulfate = math.exp(slope*(1./T) -45.5)
435
+ elif model == "FR54-S1":
436
+ Csulfate = ((67.e6)*10000.)
437
+ elif model == "JdF": # 1100 'C ONLY
438
+ Csulfate = 10.**17.
439
+ elif model == "Boulliung22nP": # Boullioung & Wood (2022) GCA 336:150-164 [eq5] - corrected!
440
+ # Mole fractions in the melt on cationic lattice (all Fe as FeO) no volatiles
441
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"no","no")
442
+ logCS6 = -12.948 + ((15602.*melt_comp["Ca"] + 28649.*melt_comp["Na"] - 9596.*melt_comp["Mg"] + 4194.*melt_comp["Al"] +16016.*melt_comp["Mn"] + 29244.)/T) # wt% S
443
+ Csulfate = (10.**logCS6)*10000. # ppm S
444
+ elif model == "Boulliung22wP": # Boullioung & Wood (2022) GCA 336:150-164 [eq5] - corrected!
445
+ # Mole fractions in the melt on cationic lattice (all Fe as FeO) no volatiles
446
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"no","no")
447
+ logCS6 = -12.659 + ((3692.*melt_comp["Ca"] - 7592.*melt_comp["Si"] - 13736.*melt_comp["Ti"] + 3762.*melt_comp["Al"] + 34483)/T) - (0.1*P*1.5237)/T # wt% S
448
+ Csulfate = (10.**logCS6)*10000. # ppm S
449
+ elif model in ["ONeill22","ONeill22dil"]:
450
+ if model == "ONeill22": # O'Neill & Mavrogenes (2022) GCA 334:368-382 eq[12a]
451
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"no","yes") # Mole fractions in the melt on cationic lattice (Fe as Fe2 and Fe3) no volatiles
452
+ elif model == "ONeill22dil": # O'Neill & Mavrogenes (2022) GCA 334:368-382 eq[12a]
453
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"water","yes") # Mole fractions in the melt on cationic lattice (Fe as Fe2 and Fe3) includes water
454
+ lnC = -8.02 + ((21100. + 44000.*melt_comp["Na"] + 18700.*melt_comp["Mg"] + 4300.*melt_comp["Al"] + 44200.*melt_comp["K"] + 35600.*melt_comp["Ca"] + 12600.*melt_comp["Mn"] + 16500.*melt_comp["Fe2"])/T) #CS6+ = [S6+, ppm]/fSO3
455
+ Csulfate = gp.exp(lnC)*KOSg2(PT,models) # ppm S
456
+
457
+ return Csulfate
458
+
459
+
460
+
461
+ ###################################
462
+ ### solubility constant for H2S ###
463
+ ###################################
464
+ def C_H2S(PT,melt_wf,species,models): # C_H2S = wmH2S/fH2S (ppm H2S, fH2S bar)
465
+ model = models.loc["hydrogen sulfide","option"]
466
+ if model == "basalt":
467
+ K = 10.23 # fitted to basalt data from Moune+ 2009 CMP 157:691–707 and Lesne+ 2015 ChemGeol 418:104–116
468
+ elif model == "basaltic andesite":
469
+ K = 6.82 # fitted to basaltic andesite data from Moune+ 2009 CMP 157:691–707 and Lesne+ 2015 ChemGeol 418:104–116
470
+ return K
471
+
472
+
473
+
474
+ ########################################
475
+ ### solubility constant for hydrogen ###
476
+ ########################################
477
+ def C_H2(PT,melt_wf,species,models): # C_H2 = wmH2/fH2 (wtppm)
478
+ # Hirchmann et al. (2012) EPSL 345-348:38-48
479
+ model = models.loc["hydrogen","option"]
480
+ R = 83.144598 # bar cm3 /mol /K
481
+ P = PT['P'] # pressure in bars
482
+ T = PT['T'] + 273.15 # T in Kelvin SHOULD BE T0
483
+ P0 = 100.*0.01 # kPa to bars
484
+ if model == "basalt":
485
+ #lnK0 = -11.4 # T0 = 1400 'C, P0 = 100 kPa for mole fraction H2
486
+ lnK0 = -0.9624 # for ppm H2 (fitted in excel)
487
+ DV = 10.6 # cm3/mol
488
+ elif model == "andesite":
489
+ #lnK0 = -10.6 # T0 = 1400 'C, P0 = 100 kPa for mole fraction H2
490
+ lnK0 = -0.1296 # for ppm H2 (fitted in excel)
491
+ DV = 11.3 # cm3/mol
492
+ lnK = lnK0 - (DV*(P-P0))/(R*T) # = ln(XH2/fH2) in ppm/bar
493
+ C = gp.exp(lnK)
494
+ return C
495
+
496
+
497
+
498
+ ######################################
499
+ ### solubility constant for methane ##
500
+ ######################################
501
+ def C_CH4(PT,melt_wf,species,models): # C_CH4 = wmCH4/fCH4 (ppm)
502
+ model = models.loc["methane","option"]
503
+ if model == "Ardia13": # Ardia et al. (2013) GCA 114:52-71
504
+ R = 83.144598 # bar cm3 /mol /K
505
+ P = PT['P'] # pressure in bars
506
+ T = PT['T'] + 273.15 # T in Kelvin SHOULD BE T0
507
+ P0 = 100.*0.01 # kPa to bars
508
+ lnK0 = 4.93 # ppm CH4
509
+ #lnK0 = -7.63 # mole fraction CH4
510
+ DV = 26.85 # cm3/mol
511
+ lnK = lnK0 - (DV*(P-P0))/(R*T)
512
+ K_ = gp.exp(lnK) # for fCH4 in GPa
513
+ K = 0.0001*K_ # for fCH4 in bars
514
+ return K
515
+
516
+
517
+
518
+ #################################
519
+ ### solubility constant for CO ##
520
+ #################################
521
+ def C_CO(PT,melt_wf,species,models): # C_CO = wmCO/fCO (ppm)
522
+ model = models.loc["carbon monoxide","option"]
523
+ if model == "basalt": # from fitting Armstrong et al. (2015) GCA 171:283-302; Stanley+2014, and Wetzel+13 thermodynamically
524
+ R = 83.144598 # bar cm3 /mol /K
525
+ P = PT['P'] # pressure in bars
526
+ T = PT['T'] +273.15 # T in Kelvin
527
+ P0 = 1. # in bars
528
+ lnK0 = -2.11 # ppm CO
529
+ DV = 15.20 # cm3/mol
530
+ lnK = lnK0 - (DV*(P-P0))/(R*T)
531
+ K = gp.exp(lnK) # CO(ppm)/fCO(bars)
532
+ return K
533
+
534
+
535
+ #################################
536
+ ### solubility constant for X ###
537
+ #################################
538
+ def C_X(PT,melt_wf,species,models): # C_X = wmX/fX (ppm)
539
+ species = models.loc["species X","option"]
540
+ model = models.loc["species X solubility","option"]
541
+ if species == "Ar":
542
+ if model == "Iacono-Marziano10_Ar_basalt": # Iacono-Marziano et al. (2010) Chemical Geology 279(3–4):145-157
543
+ K = 0.0799 # fitted assuming Ar is an ideal gas... i.e. yAr = 1.
544
+ elif model == "Iacono-Marziano10_Ar_rhyolite": # Iacono-Marziano et al. (2010) Chemical Geology 279(3–4):145-157
545
+ K = 0.4400 # fitted assuming Ar is an ideal gas... i.e. yAr = 1.
546
+ elif model == "test":
547
+ #K = 40. # similar to H2O
548
+ #K = 6. # similar to S @ DFMQ+1.25
549
+ #K = 21. # similar to S @ DFMQ+3
550
+ #K = 155 # similar to S @ DFMQ0
551
+ #K = 918005 # similar to S @DFMQ-3
552
+ #K = 10.23 # similar to H2S
553
+ #K = 0.51 # similar to CO32-
554
+ #K = 1.37 # degassed at a similar depth to H2OT at 3wt%
555
+ #K = 100.
556
+ K = 35.
557
+ if species == "Ne":
558
+ if model == "Iacono-Marziano10_Ne_basalt": # Iacono-Marziano et al. (2010) Chemical Geology 279(3–4):145-157
559
+ K = 0.1504 # fitted assuming Ne is an ideal gas... i.e. yNe = 1.
560
+ elif model == "Iacono-Marziano10_Ne_rhyolite": # Iacono-Marziano et al. (2010) Chemical Geology 279(3–4):145-157
561
+ K = 0.8464 # fitted assuming Ne is an ideal gas... i.e. yNe = 1.
562
+ return K
563
+
564
+
565
+ #################################################################################################################################
566
+ ################################################ solid/liquid volatile saturation ###############################################
567
+ #################################################################################################################################
568
+
569
+ ################################################
570
+ ### sulfate content at anhydrite saturation ###
571
+ ################################################
572
+ def SCAS(PT,melt_wf,species,models):
573
+ model = models.loc["SCAS","option"]
574
+ T = PT['T'] +273.15
575
+
576
+ comp = mg.melt_pysulfsat(melt_wf,species)
577
+
578
+
579
+ if model == "Chowdhury18": # sulfate content (ppm) at anhydrite saturation from Chowdhury & Dasgupta (2018) [T in K]
580
+ # mole fraction melt composition including water but all Fe as FeOT
581
+ melt_comp = mg.melt_mole_fraction(melt_wf,species,models,"water","no")
582
+ tot = 100.*melt_comp["mol_tot"]
583
+ a = -13.23
584
+ b = -0.50
585
+ dSi = 3.02
586
+ dCa = 36.70
587
+ dMg = 2.84
588
+ dFe = 10.14
589
+ dAl = 44.28
590
+ dNa = 26.27
591
+ dK = -25.77
592
+ e = 0.09
593
+ f = 0.54
594
+ wm_H2OT = 100.*melt_wf['H2OT']
595
+ dX = dSi*melt_comp["SiO2"] + dCa*melt_comp["CaO"] + dMg*melt_comp["MgO"] + dFe*melt_comp["FeOT"] + dAl*melt_comp["Al2O3"] + dNa*melt_comp["Na2O"] + dK*melt_comp["K2O"]
596
+ lnxm_SO4 = a + b*((10.0**4.0)/T) + dX + e*wm_H2OT - f*gp.log(melt_comp["CaO"])
597
+ xm_SO4 = gp.exp(lnxm_SO4)
598
+ Xm_SO4 = xm_SO4*(xm_SO4 + tot)
599
+ S6CAS = Xm_SO4*species.loc["S","M"]*10000.0
600
+
601
+ elif model == "Zajacz19":
602
+ # mole fraction melt composition including water but all Fe as FeOT
603
+ melt_comp = mg.melt_mole_fraction(melt_wf,species,models,"water","no")
604
+ tot = 100.*melt_comp["mol_tot"]
605
+ if melt_comp["Na2O"] + melt_comp["K2O"] + melt_comp["CaO"] >= melt_comp["Al2O3"]:
606
+ P_Rhyo = 3.11*(melt_comp["Na2O"]+melt_comp["K2O"]+melt_comp["CaO"]-melt_comp["Al2O3"])
607
+ else:
608
+ P_Rhyo = 1.54*(melt_comp["Al2O3"]-(melt_comp["Na2O"]+melt_comp["K2O"]+melt_comp["CaO"]))
609
+ NBOT = (2.*melt_comp["Na2O"]+2.*melt_comp["K2O"]+2.*(melt_comp["CaO"]+melt_comp["MgO"]+melt_comp["FeOT"])-melt_comp["Al2O3"]*2.)/(melt_comp["SiO2"]+2.*melt_comp["Al2O3"]) # according to spreadsheet not paper
610
+ P_T = gp.exp(-7890./T)
611
+ P_H2O = melt_comp["H2O"]*(2.09 - 1.65*NBOT) + 0.42*NBOT + 0.23
612
+ P_C = ((P_Rhyo + 251.*melt_comp["CaO"]**2. + 57.*melt_comp["MgO"]**2. + 154.*melt_comp["FeOT"]**2.)/(2.*melt_comp["Al2O3"] + melt_comp["SiO2"]))/(1. + 4.8*NBOT)
613
+ Ksm_SPAnh = gp.exp(1.226*gp.log(P_C*P_T*P_H2O) + 0.079)
614
+ Xsm_S = Ksm_SPAnh/melt_comp["CaO"]
615
+ S6CAS = Xsm_S*tot*32.07*10000.
616
+
617
+ elif model == "Liu23": # Liu et al. (2023) GCA 349:135-145 eq. 4
618
+ melt_comp = mg.melt_mole_fraction(melt_wf,species,models,"no","no")
619
+ NBOT = (2.*melt_comp["Na2O"]+2.*melt_comp["K2O"]+2.*(melt_comp["CaO"]+melt_comp["MgO"]+melt_comp["FeOT"])-melt_comp["Al2O3"]*2.)/(melt_comp["SiO2"]+2.*melt_comp["Al2O3"])
620
+ melt_comp = mg.melt_mole_fraction(melt_wf,species,models,"water","no")
621
+ lnSCAS = 12.185 - (8541./T) + (1.409*NBOT) + 9.984*melt_comp["CaO"] + melt_wf["H2OT"]*100.
622
+ S6CAS = gp.exp(lnSCAS)
623
+
624
+ elif model == "pss_CD2019": # Chowdhury & Dasgupta (2019) using PySulfSat by Wieser and Gleeson (2023)
625
+ output = ss.calculate_CD2019_SCAS(df=comp, T_K=T, H2O_Liq=float(100.*melt_wf['H2OT']), Fe3Fet_Liq=None, P_kbar=None)
626
+ S6CAS = float(output["SCAS6_ppm"])
627
+ elif model == "pss_ZT2022": # Zajacz and Tsay (2022) using PySulfSat by Wieser and Gleeson (2023)
628
+ output = ss.calculate_ZT2022_SCAS(df=comp, T_K=T, H2O_Liq=float(100.*melt_wf['H2OT']), Fe3Fet_Liq=None, P_kbar=None)
629
+ S6CAS = float(output["SCAS6_ppm"])
630
+ #elif model == "pss_MK2015": # NOT WORKING: Masotta and Kepler (2015) using PySulfSat by Wieser and Gleeson (2023)
631
+ # output = ss.calculate_MK2015_SCAS(df=comp, T_K=T, H2O_Liq=float(100.*melt_wf['H2OT']), Fe3Fet_Liq=None, P_kbar=None)
632
+ # S6CAS = float(output["SCAS6_ppm"])
633
+
634
+ return S6CAS
635
+
636
+ ###############################################
637
+ ### sulfide content at sulfide saturation ###
638
+ ###############################################
639
+ def SCSS(PT,melt_wf,species,models): # sulfide content (ppm) at sulfide saturation from O'Neill (2020) [P in bar,T in K]
640
+ model = models.loc["SCSS","option"]
641
+ P_bar = PT['P']
642
+ T = PT['T'] + 273.15
643
+ Fe3FeT = melt_wf["Fe3FeT"]
644
+ comp = mg.melt_pysulfsat(melt_wf,species)
645
+
646
+ if model in ["ONeill21","ONeill21dil","ONeill21hyd"]:
647
+ R = 8.31441
648
+ P = (1.0e-4)*P_bar # pressure in GPa
649
+ D = (137778.0 - 91.66*T + 8.474*T*gp.log(T)) # J/mol
650
+ sulfide_comp = 1.0 # assumes the sulfide is pure FeS (no Ni, Cu, etc.)
651
+ if model == "ONeill21":
652
+ # Mole fractions in the melt on cationic lattice (Fe2 and Fe3) no volatiles
653
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"no","yes")
654
+ elif model == "ONeill21dil":
655
+ # Mole fractions in the melt on cationic lattice (Fe2 and Fe3) and water
656
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"water","yes")
657
+ elif model == "ONeill21hyd":
658
+ # Mole fractions in the melt on cationic lattice (Fe2 and Fe3) and water
659
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"water","yes")
660
+ lnaFeS = gp.log((1.0 - melt_comp["Fe2"])*sulfide_comp)
661
+ lnyFe2 = (((1.0-melt_comp["Fe2"])**2.0)*(28870.0 - 14710.0*melt_comp["Mg"] + 1960.0*melt_comp["Ca"] + 43300.0*melt_comp["Na"] + 95380.0*melt_comp["K"] - 76880.0*melt_comp["Ti"]) + (1.0-melt_comp["Fe2"])*(-62190.0*melt_comp["Si"] + 31520.0*melt_comp["Si"]**2.0))/(R*T)
662
+ lnS = D/(R*T) + gp.log(C_S(PT,melt_wf,species,models)) - gp.log(melt_comp["Fe2"]) - lnyFe2 + lnaFeS + (-291.0*P + 351.0*gp.erf(P))/T
663
+ SCSS = gp.exp(lnS)
664
+
665
+ elif model == "Liu07":
666
+ # Mole fractions in the melt on cationic lattice (Fe2 and Fe3) and water
667
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"water","yes")
668
+ MFM = (melt_comp["Na"]+melt_comp["K"]+2.*(melt_comp["Ca"]+melt_comp["Mg"]+melt_comp["Fe2"]))/(melt_comp["Si"]*(melt_comp["Al"]+melt_comp["Fe3"]))
669
+ lnS = 11.35251 - (4454.6/T) - 0.03190*(PT["P"]/T) + 0.71006*gp.log(MFM) - 1.98063*(MFM*melt_comp["H"]) + 0.21867*gp.log(melt_comp["H"]) + 0.36192*gp.log(melt_comp["Fe2"])
670
+ SCSS = gp.exp(lnS)
671
+
672
+ elif model == "Fortin15":
673
+ # Mole fractions in the melt on cationic lattice (all Fe as FeOT) and water
674
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"water","no")
675
+ lnS = 34.784 - (5772.3/T) - 346.54*((0.0001*PT["P"])/T) - 20.393*melt_comp["H"] - 25.499*melt_comp["Si"] - 18.344*melt_comp["Ti"] - 27.381*melt_comp["Al"] - 17.275*melt_comp["Fe"] - 22.398*melt_comp["Mg"] - 20.378*melt_comp["Ca"] - 18.954*melt_comp["Na"] - 32.195*melt_comp["K"]
676
+ SCSS = gp.exp(lnS)
677
+
678
+ elif model == "Liu21":
679
+ XFeS = 1.
680
+ H2O = melt_wf["H2OT"]*100.
681
+ SCSS = (XFeS*gp.exp(13.88 - (9744./T) - (328.*(0.0001*PT["P"])/T))) + 104.*H2O
682
+
683
+ elif model == "pss_LiZhang2022": # NOT WORKING Li and Zhang (2022) using PySulfSat by Wieser and Gleeson (2023) - assuming pure FeS
684
+ output = ss.calculate_LiZhang2022_SCSS(df=comp, T_K=T, P_kbar=(P_bar/1000.), H2O_Liq=float(100.*melt_wf['H2OT']), Fe_FeNiCu_Sulf=1., Cu_FeNiCu_Sulf=0., Ni_FeNiCu_Sulf=0., Fe3Fet_Liq=Fe3FeT, logfo2=None,Ni_Liq=None, Cu_Liq=None, Ni_Sulf_init=5, Cu_Sulf_init=5, Fe_Sulf=None, Cu_Sulf=None, Ni_Sulf=None, T_K_transition=True,highT=False, lowT=False)
685
+ SCSS = float(output["SCSS_Tot"])
686
+ elif model == "pss_F2015": # Fortin et al. (2015) using PySulfSat by Wieser and Gleeson (2023)
687
+ output = ss.calculate_F2015_SCSS(df=comp, T_K=T, P_kbar=(P_bar/1000.), H2O_Liq=float(100.*melt_wf['H2OT']), Fe3Fet_Liq=None,Fe_FeNiCu_Sulf=None)
688
+ SCSS = float(output["SCSS2_ppm"])
689
+ elif model == "pss_Liu2021": # Liu et al. (2021) using PySulfSat by Wieser and Gleeson (2023) - assuming pure FeS
690
+ output = ss.calculate_Liu2021_SCSS(df=comp, T_K=T, P_kbar=(P_bar/1000.), Fe_FeNiCu_Sulf=1., H2O_Liq=float(100.*melt_wf['H2OT']), Cu_FeNiCu_Sulf=0., Ni_FeNiCu_Sulf=0.,
691
+ Ni_Liq=None, Cu_Liq=None, Fe_Sulf=None, Cu_Sulf=None, Ni_Sulf=None, Ni_Sulf_init=5, Cu_Sulf_init=5, Fe3Fet_Liq=Fe3FeT)
692
+ SCSS = float(output["SCSS2_ppm"])
693
+ elif model == "pss_OM2022": # O'Neill & Mavrogenes (2022) using PySulfSat by Wieser and Gleeson (2023) - assuming pure FeS
694
+ output = ss.calculate_OM2022_SCSS(df=comp, T_K=T, P_kbar=(P_bar/1000.), Fe3Fet_Liq=Fe3FeT, Fe_FeNiCu_Sulf=1., Cu_FeNiCu_Sulf=0., Ni_FeNiCu_Sulf=0., Fe_Sulf=None, Ni_Sulf=None, Cu_Sulf=None, Ni_Liq=None, Cu_Liq=None, Ni_Sulf_init=5, Cu_Sulf_init=5)
695
+ SCSS = float(output["SCSS2_ppm"])
696
+ elif model == "pss_O2021": # O'Neill (2021) using PySulfSat by Wieser and Gleeson (2023) - assuming pure FeS
697
+ output = ss.calculate_O2021_SCSS(df=comp, T_K=T, P_kbar=(P_bar/1000.), Fe3Fet_Liq=Fe3FeT, Fe_FeNiCu_Sulf=1., Cu_FeNiCu_Sulf=0., Ni_FeNiCu_Sulf=0.,Fe_Sulf=None, Ni_Sulf=None, Cu_Sulf=None,Ni_Liq=None, Cu_Liq=None, Ni_Sulf_init=5, Cu_Sulf_init=5)
698
+ SCSS = float(output["SCSS2_ppm"])
699
+ elif model == "pss_S2017": # Smythe et al. (2017) using PySulfSat by Wieser and Gleeson (2023) - assuming pure FeS
700
+ output = ss.calculate_S2017_SCSS(df=comp, T_K=T, P_kbar=(P_bar/1000.), Fe3Fet_Liq=Fe3FeT, Fe_FeNiCu_Sulf=0.999999, Cu_FeNiCu_Sulf=0.000001, Ni_FeNiCu_Sulf=0.,Fe_Sulf=None, Ni_Sulf=None, Cu_Sulf=None, Ni_Liq=None, Cu_Liq=None, H2O_Liq=float(100.*melt_wf['H2OT']),Ni_Sulf_init=5, Cu_Sulf_init=5)
701
+ SCSS = float(output["SCSS2_ppm_ideal_Smythe2017"])
702
+ return SCSS
703
+
704
+ #################################################################################################################################
705
+ #################################### EQUILIBRIUM CONSTANTS FOR HOMOGENEOUS VAPOR EQUILIBRIA #####################################
706
+ #################################################################################################################################
707
+
708
+ # H2 + 0.5O2 = H2O
709
+ # K = fH2O/(fH2*(fO2)^0.5)
710
+ def KHOg(PT,models):
711
+ T_K = PT['T']+273.15
712
+ if models.loc["KHOg","option"] == "KO97":
713
+ K = 10.**((12510.0/T_K)-0.979*(gp.log10(T_K))+0.483)
714
+ return K
715
+
716
+ # H2O + 0.5S2 = H2S + 0.5O2
717
+ # K = (fH2S*(fO2)^0.5)/((fS2^0.5)*fH2O)
718
+ def KHOSg(PT,models):
719
+ T_K = PT['T']+273.15
720
+ if models.loc["KHOSg","option"] == "KO97": # Kerrick & Ohmoto (1997)
721
+ K = 10.**((-8117.0/T_K)+0.188*gp.log10(T_K)-0.352)
722
+ elif models.loc["KHOSg","option"] == "noH2S": # H2S doesn't form in the gas...
723
+ K = 0.
724
+ return K
725
+
726
+ # 0.5S2 + O2 = SO2
727
+ # K = fSO2/((fS2^0.5)*fO2)
728
+ def KOSg(PT,models):
729
+ T_K = PT['T']+273.15
730
+ if models.loc["KOSg","option"] == "KO97": # Kerrick & Ohmoto (1997)
731
+ K = 10.**((18929.0/T_K)-3.783)
732
+ return K
733
+
734
+ # 0.5S2 + 1.5O2 = SO3
735
+ # K = fSO3/((fS2^0.5)*(fO2^1.5)
736
+ def KOSg2(PT,models):
737
+ T_K = PT['T']+273.15
738
+ if models.loc["KOSg2","option"] == "OM22": # O'Neill+Mavrogenes2022 from JANF
739
+ lnK = (55921./T_K) - 25.07 + 0.6465*gp.log(T_K)
740
+ K = gp.exp(lnK)
741
+ return K
742
+
743
+ # CO + 0.5O = CO2
744
+ # K = fCO2/(fCO*(fO2^0.5))
745
+ def KCOg(PT,models):
746
+ T_K = PT['T']+273.15
747
+ if models.loc["KCOg","option"] == "KO97": # Kerrick & Ohmoto (1997)
748
+ K = 10.**((14751.0/T_K)-4.535)
749
+ return K
750
+
751
+ # CH4 + 2O2 = CO2 + 2H2O
752
+ # K = (fCO2*(fH2O^2))/(fCH4*(fO2^2))
753
+ def KCOHg(PT,models):
754
+ T_K = PT['T']+273.15
755
+ if models.loc["KCOHg","option"] == "KO97": # Kerrick & Ohmoto (1997)
756
+ K = 10.**((41997.0/T_K)+0.719*gp.log10(T_K)-2.404)
757
+ return K
758
+
759
+ def KOCSg(PT,models): # OCS - depends on system
760
+ T = PT['T']+273.15
761
+ if models.loc["carbonylsulfide","option"] == "COHS":
762
+ # OCS + H2O = CO2 + H2S
763
+ # K = (fCO2*fH2S)/(fOCS*fH2O)
764
+ if models.loc["KOCSg","option"] == "EVo":
765
+ K = gp.exp(0.482 + (16.166e-2/T) + 0.081e-3*T - (5.715e-3/T**2) - 2.224e-1*gp.log(T))
766
+ return K
767
+ if models.loc["carbonylsulfide","option"] == "COS":
768
+ # 2CO2 + OCS = 3CO + SO2 -
769
+ # K = (fCO^3*fSO2)/(fCO2^2*fOCS)
770
+ if models.loc["KOCSg","option"] == "Moussallam19": # Moussallam et al. (2019) EPSL 520:260-267
771
+ K = 10.**(9.24403 - (15386.45/T)) # P and f in bars, T in K
772
+ return K
773
+
774
+ # Cgraphite + O2 = CO2
775
+ def KCOs(PT,models):
776
+ T_K = PT['T']+273.15
777
+ P = PT['P']
778
+ if models.loc["KCOs","option"] == "Holloway92": # Holloway et al. (1992) Eur J. Mineral. 4:105-114 equation (3) KI
779
+ a = 40.07639
780
+ b = -2.5392e-2
781
+ c = 5.27096e-6
782
+ d = 0.0267
783
+ log10K = a + (b*T_K) + (c*T_K**2) + ((P-1)/T_K)
784
+ K = 10.**log10K
785
+ return K
786
+
787
+ #################################################################################################################################
788
+ ##################################### EQUILIBRIUM CONSTANTS FOR HOMOGENEOUS MELT EQUILIBRIA #####################################
789
+ #################################################################################################################################
790
+
791
+ # H2Omol + O = 2OH
792
+ # K = xOH*2/(xH2Omol*xO)
793
+ def KHOm(PT,melt_wf,species,models):
794
+ Hspeccomp = models.loc["Hspeccomp","option"]
795
+ T_K = PT['T']+273.15
796
+
797
+ if Hspeccomp == "rhyolite": # Zhang (1999) Reviews in Geophysics 37(4):493-516
798
+ a = -3120.0
799
+ b = 1.89
800
+ K = gp.exp((a/T_K) + b)
801
+ elif Hspeccomp == "alkali basalt": # average of eqn-15-17 from Lesne et al. (2010) CMP 162:133-151
802
+ a = -8348.0 # VES-9 = -8033.0, ETN-1 = -8300.0, and PST-9 = -8710.0
803
+ b = 7.8108 # VES-9 = 7.4222, ETN-1 = 7.4859, and PEST-9 = 8.5244
804
+ K = gp.exp((a/T_K) + b)
805
+ elif Hspeccomp == "PST-9": # Lesne et al. (2010) CMP 162:133-151 eqn 15
806
+ a = -8710.0
807
+ b = 8.5244
808
+ K = gp.exp((a/T_K) + b)
809
+ elif Hspeccomp == "VES-9": # Lesne et al. (2010) CMP 162:133-151 eqn 16
810
+ a = -8033.0
811
+ b = 7.4222
812
+ K = gp.exp((a/T_K) + b)
813
+ elif Hspeccomp == "ETN-1": # Lesne et al. (2010) CMP 162:133-151 eqn 17
814
+ a = -8300.0
815
+ b = 7.4859
816
+ K = gp.exp((a/T_K) + b)
817
+ elif Hspeccomp == "andesite": # eqn-7 from Botcharnikov et al. (2006) Chem. Geol. 229(1-3)125-143
818
+ a = -3650.0
819
+ b = 2.99
820
+ K = gp.exp((a/T_K) + b)
821
+ elif Hspeccomp == "MORB": # fit to Dixon et al. (1995) data digitised from Lesne et al. (2010) CMP 162:133-151
822
+ a = -2204.99
823
+ b = 1.2600
824
+ K = gp.exp((a/T_K) + b)
825
+ elif Hspeccomp == "albite": # Silver & Stolper (1989) J.Pet 30(3)667-709
826
+ K = 0.17
827
+ return K
828
+
829
+ def KregH2O(PT,melt_wf,species,models):
830
+ if Hspeccomp == "MORB": # Dixon et al. (1995)
831
+ A = 0.403
832
+ B = 15.333
833
+ C = 10.894
834
+ elif Hspeccomp == "alkali basalt XT": # No T-dependence, hence its the speciation frozen in the glass. Eqn 7-10 from Lesne et al. (2010) CMP 162:133-151 (eqn 7 is wrong)
835
+ # wt% normalised including H2O, all Fe as FeOT
836
+ melt_comp = melt_normalise_wf(melt_wf,species,"volatiles","Fe speciation")
837
+ Na = melt_comp["Na2O"]*100.
838
+ K = melt_comp["K2O"]*100.
839
+ A = 0.5761*(Na+K) - 0.2884 # eqn-8
840
+ B = -8.9589*(Na+K) + 24.65 # eqn-9
841
+ C = 1.7013*(Na+K) + 9.6481 # eqn-1
842
+ elif Hspeccomp == "alkali basalt": # Includes T-dependence, hence speciation in the melt. Eqn 24-27 from Lesne et al. (2010) CMP 162:133-151
843
+ lnK = ((-2704.4/T_K) + 0.641)
844
+ A = lnK + 49016.0/(R*T_K)
845
+ B = -2153326.51/(R*T_K)
846
+ C = 1.965495217/(R*T_K)
847
+ elif Hspeccomp == "VES-9": # Lesne et al. (2011) 162:133-151 Table 5
848
+ A = 3.139
849
+ B = -29.555
850
+ C = 20.535
851
+ elif Hspeccomp == "ETN-1": # Lesne et al. (2011) 162:133-151 Table 5
852
+ A = 4.128
853
+ B = -45.905
854
+ C = 21.311
855
+ elif Hspeccomp == "PST-9": # Lesne et al. (2011) 162:133-151 Table 5
856
+ A = 2.6
857
+ B = -22.476
858
+ C = 22.295
859
+ elif Hspeccomp == "albite": # Silver & Stolper (1989) J.Pet 30(3)667-709
860
+ A = 0.403
861
+ B = 15.333
862
+ C = 10.894
863
+
864
+ # CO2 + O = CO3
865
+ def KCOm(PT,melt_wf,species,models): # K =
866
+ T_K = PT['T']+273.15
867
+ Cspeccomp = models.loc["Cspeccomp","option"]
868
+ if Cspeccomp == "andesite": # eqn-8 from Botcharnikov et al. (2006) Chem. Geol. 229(1-3)125-143
869
+ a = 8665.0
870
+ b = -5.11
871
+ value = gp.exp((a/T_K) + b)
872
+ elif Cspeccomp == "dacite": # from Botcharnikov et al. (2006) Chem. Geol. 229(1-3)125-143
873
+ a = 9787.0
874
+ b = -7.69
875
+ value = gp.exp((a/T_K) + b)
876
+ elif Cspeccomp == "basalt": # all oxidised carbon is CO32-
877
+ value = "infinite"
878
+ elif Cspeccomp == "rhyolite": # all oxidised carbon is CO2,mol
879
+ value = 0.
880
+ return value
881
+
882
+
883
+ #################################################################################################################################
884
+ ##################################################### FUGACITY COEFFICIENTS #####################################################
885
+ #################################################################################################################################
886
+
887
+ # all fugacity coefficients are assumed to equal 1 below 1 bar.
888
+
889
+ ##########################################
890
+ ### CORK using Holland & Powell (1991) ###
891
+ ##########################################
892
+
893
+ def CORK(PT,p0,a,b,c,d):
894
+ P = PT['P']
895
+ T_K = PT['T']+273.15
896
+ def MRK(P_kb,VMRK,R,T_K,a,b): # MRK volume equation rearranged to equal 0
897
+ return P_kb*pow(VMRK,3.0) - R*T_K*pow(VMRK,2.0) - (b*R*T_K + pow(b,2.0)*P_kb - a*pow(T_K,-0.5))*VMRK - (a*b)*pow(T_K,-0.5)
898
+
899
+ def dMRK(P_kb,VMRK,R,T_K,a,b): # derivative of above
900
+ return 3.0*P_kb*pow(VMRK,2.0) - 2.0*R*T_K*VMRK - (b*R*T_K + pow(b,2.0)*P_kb - a*pow(T_K,-0.5))
901
+
902
+ def dVMRK(MRK,P_kb,VMRK,R,T_K,a,b):
903
+ return abs(0-MRK(P_kb,VMRK,R,T_K,a,b))
904
+
905
+ def NR_VMRK(MRK, dMRK, VMRK0, e1, P_kb,R,T_K,a,b):
906
+ delta1 = dVMRK(MRK,P_kb,VMRK0,R,T_K,a,b)
907
+ while delta1 > e1:
908
+ VMRK0 = VMRK0 - MRK(P_kb,VMRK0,R,T_K,a,b)/dMRK(P_kb,VMRK0,R,T_K,a,b)
909
+ delta1 = dVMRK(MRK,P_kb,VMRK0,R,T_K,a,b)
910
+ return VMRK0
911
+
912
+ R = 8.314e-3 # in kJ/mol/K
913
+ P_kb = P/1000.0
914
+
915
+ Vi = ((R*T_K)/P_kb) + b
916
+
917
+ VMRK = NR_VMRK(MRK, dMRK, Vi, 1E20, P_kb,R,T_K,a,b)
918
+
919
+ if P_kb > p0:
920
+ V = VMRK + c*pow((P_kb-p0),0.5) + d*(P_kb-p0)
921
+ ln_y_virial = (1/(R*T_K))*((2./3.)*c*pow((P_kb-p0),1.5) + (d/2.0)*pow((P_kb-p0),2.0))
922
+ else:
923
+ V = VMRK
924
+ ln_y_virial = 0.0
925
+
926
+ z = (P_kb*V)/(R*T_K)
927
+ A = a/(b*R*pow(T_K,1.5))
928
+ B = (b*P_kb)/(R*T_K)
929
+
930
+ ln_y = z - 1.0 - gp.log(z-B) - A*gp.log(1.0 + (B/z)) + ln_y_virial
931
+ return gp.exp(ln_y)
932
+
933
+ ###########################
934
+ ### Shi & Saxena (1992) ###
935
+ ###########################
936
+
937
+ def lny_SS(PT,Pcr,Tcr):
938
+ P = PT['P']
939
+ T_K = PT['T']+273.15
940
+ Tr = T_K/Tcr
941
+ A, B, C, D, P0, integral0 = Q_SS(PT,Tr,Pcr)
942
+ Pr = P/Pcr
943
+ P0r = P0/Pcr
944
+ integral = A*gp.log(Pr/P0r) + B*(Pr - P0r) + (C/2.0)*(pow(Pr,2.0) - pow(P0r,2.0)) + (D/3.0)*(pow(Pr,3.0) - pow(P0r,3.0))
945
+ integral_total = integral + integral0
946
+ return integral_total
947
+
948
+ def Q_SS(PT,Tr,Pcr):
949
+ P = PT['P']
950
+ def Q1000(Pcr):
951
+ Pr_ = 1000.0/Pcr
952
+ P0r_ = 1.0/Pcr
953
+ A0 = 1.0
954
+ B0 = 0.9827e-1*pow(Tr,-1.0) + -0.2709*pow(Tr,-3.0)
955
+ C0 = -0.1030e-2*pow(Tr,-1.5) + 0.1427e-1*pow(Tr,-4.0)
956
+ D0 = 0.0
957
+ return A0*gp.log(Pr_/P0r_) + B0*(Pr_ - P0r_) + (C0/2.0)*(pow(Pr_,2.0) - pow(P0r_,2.0)) + (D0/3.0)*(pow(Pr_,3.0) - pow(P0r_,3.0))
958
+ def Q5000(Pcr):
959
+ Pr_ = 5000.0/Pcr
960
+ P0r_ = 1000.0/Pcr
961
+ A0 = 1.0 + -5.917e-1*pow(Tr,-2.0)
962
+ B0 = 9.122e-2*pow(Tr,-1.0)
963
+ C0 = -1.416e-4*pow(Tr,-2.0) + -2.835e-6*gp.log(Tr)
964
+ D0 = 0.0
965
+ return A0*gp.log(Pr_/P0r_) + B0*(Pr_ - P0r_) + (C0/2.0)*(pow(Pr_,2.0) - pow(P0r_,2.0)) + (D0/3.0)*(pow(Pr_,3.0) - pow(P0r_,3.0))
966
+ if P > 5000.0:
967
+ A = 2.0614 + -2.235*pow(Tr,-2.0) + -3.941e-1*gp.log(Tr)
968
+ B = 5.513e-2*pow(Tr,-1.0) + 3.934e-2*pow(Tr,-2.0)
969
+ C = -1.894e-6*pow(Tr,-1.0) + -1.109e-5*pow(Tr,-2.0) + -2.189e-5*pow(Tr,-3.0)
970
+ D = 5.053e-11*pow(Tr,-1.0) + -6.303e-21*pow(Tr,3.0)
971
+ P0 = 5000.0
972
+ integral0 = Q1000(Pcr) + Q5000(Pcr)
973
+ return A, B, C, D, P0, integral0
974
+ elif P == 5000.0:
975
+ A = 0
976
+ B = 0
977
+ C = 0
978
+ D = 0
979
+ P0 = 5000.0
980
+ integral0 = Q1000(Pcr) + Q5000(Pcr)
981
+ return A, B, C, D, P0, integral0
982
+ elif P > 1000.0 and P < 5000.0:
983
+ A = 1.0 + -5.917e-1*pow(Tr,-2.0)
984
+ B = 9.122e-2*pow(Tr,-1.0)
985
+ C = -1.416e-4*pow(Tr,-2.0) + -2.835e-6*gp.log(Tr)
986
+ D = 0.0
987
+ P0 = 1000.0
988
+ integral0 = Q1000(Pcr)
989
+ return A, B, C, D, P0, integral0
990
+ elif P == 1000.0:
991
+ A = 0
992
+ B = 0
993
+ C = 0
994
+ D = 0.0
995
+ P0 = 1000.0
996
+ integral0 = Q1000(Pcr)
997
+ return A, B, C, D, P0, integral0
998
+ else:
999
+ A = 1.0
1000
+ B = 0.9827e-1*pow(Tr,-1.0) + -0.2709*pow(Tr,-3.0)
1001
+ C = -0.1030e-2*pow(Tr,-1.5) + 0.1427e-1*pow(Tr,-4.0)
1002
+ D = 0.0
1003
+ P0 = 1.0
1004
+ integral0 = 0.0
1005
+ return A, B, C, D, P0, integral0
1006
+
1007
+ def y_SS(gas_species,PT,species,models):
1008
+ P = PT['P']
1009
+ T_K = PT['T']+273.15
1010
+ ideal_gas = models.loc["ideal_gas","option"]
1011
+ if ideal_gas == "yes":
1012
+ return 1.0
1013
+ elif P < 1.: # ideal gas below 1 bar
1014
+ return 1.
1015
+ else:
1016
+ Tcr = species.loc[gas_species,"Tcr"]
1017
+ Pcr = species.loc[gas_species,"Pcr"]
1018
+ return gp.exp(lny_SS(PT,Pcr,Tcr))/P
1019
+
1020
+ ##############################
1021
+ ### for each vapor species ###
1022
+ ##############################
1023
+
1024
+ def y_H2(PT,species,models):
1025
+ P = PT['P']
1026
+ T_K = PT['T']+273.15
1027
+ ideal_gas = models.loc["ideal_gas","option"]
1028
+ if ideal_gas == "yes":
1029
+ return 1.0
1030
+ elif P < 1.: # ideal gas below 1 bar
1031
+ return 1.
1032
+ elif models.loc["y_H2","option"] == "SW64": # Shaw & Wones (1964)
1033
+ SW1 = gp.exp(-3.8402*pow(T_K,0.125)+0.5410)
1034
+ SW2 = gp.exp(-0.1263*pow(T_K,0.5)-15.980)
1035
+ SW3 = 300*gp.exp((-0.011901*T_K)-5.941) # NB used a value of -0.011901 instead of -0.11901 as reported to match data in Table 2
1036
+ P_atm = 0.986923*P
1037
+ ln_y = SW1*P_atm - SW2*pow(P_atm,2.0) + SW3*gp.exp((-P_atm/300.0)-1.0)
1038
+ return gp.exp(ln_y)
1039
+ elif models.loc["y_H2","option"] == "SS92": # Shi & Saxena (1992) NOT WORKING
1040
+ Tcr = 33.25 # critical temperature in K
1041
+ Pcr = 12.9696 # critical temperature in bar
1042
+ Tr = T_K/Tcr
1043
+ # Q for 1-1000 bar
1044
+ Q1_A_LP, Q2_A_LP, Q3_A_LP, Q4_A_LP, Q5_A_LP = 1., 0., 0., 0., 0.
1045
+ Q1_B_LP, Q2_B_LP, Q3_B_LP, Q4_B_LP, Q5_B_LP = 0., 0.9827e-1, 0., -0.2709, 0.
1046
+ Q1_C_LP, Q2_C_LP, Q3_C_LP, Q4_C_LP, Q5_C_LP = 0., 0., -0.1030e-2, 0., 0.1427e-1
1047
+ # Q for 1000-10000 bar
1048
+ Q1_A_HP, Q2_A_HP, Q3_A_HP, Q4_A_HP, Q5_A_HP, Q6_A_HP, Q7_A_HP, Q8_A_HP = 2.2615, 0., -6.8712e1, 0., -1.0573e4, 0., 0., -1.6936e-1
1049
+ Q1_B_HP, Q2_B_HP, Q3_B_HP, Q4_B_HP, Q5_B_HP, Q6_B_HP, Q7_B_HP, Q8_B_HP = -2.6707e-4, 0., 2.0173e-1, 0., 4.5759, 0., 0., 3.1452e-5
1050
+ Q1_C_HP, Q2_C_HP, Q3_C_HP, Q4_C_HP, Q5_C_HP, Q6_C_HP, Q7_C_HP, Q8_C_HP = -2.3376e-9, 0., 3.4091e-7, 0., -1.4188e-3, 0., 0., 3.0117e-10
1051
+ Q1_D_HP, Q2_D_HP, Q3_D_HP, Q4_D_HP, Q5_D_HP, Q6_D_HP, Q7_D_HP, Q8_D_HP = -3.2606e-15, 0., 2.4402e-12, 0., -2.4027e-9, 0., 0., 0.
1052
+ if P < 1000.:
1053
+ A = Q1_A_LP + Q2_A_LP*Tr**(-1.) + Q3_A_LP*Tr**(-1.5) + Q4_A_LP*Tr**(-3.) + Q5_A_LP*Tr**(-4.)
1054
+ B = Q1_B_LP + Q2_B_LP*Tr**(-1.) + Q3_B_LP*Tr**(-1.5) + Q4_B_LP*Tr**(-3.) + Q5_B_LP*Tr**(-4.)
1055
+ C = Q1_C_LP + Q2_C_LP*Tr**(-1.) + Q3_C_LP*Tr**(-1.5) + Q4_C_LP*Tr**(-3.) + Q5_C_LP*Tr**(-4.)
1056
+ D = 0.0
1057
+ P0 = 1.0
1058
+ integral0 = 0.
1059
+ elif P == 1000.:
1060
+ A = 0.0
1061
+ B = 0.0
1062
+ C = 0.0
1063
+ D = 0.0
1064
+ P0 = 1000.0
1065
+ Pr_ = 1000.0/Pcr
1066
+ P0r_ = 1.0/Pcr
1067
+ A0 = Q1_A_LP + Q2_A_LP*Tr + Q3_A_LP*Tr**(-1.) + Q4_A_LP*Tr**2. + Q5_A_LP*Tr**(-2.)
1068
+ B0 = Q1_B_LP + Q2_B_LP*Tr + Q3_B_LP*Tr**(-1.) + Q4_B_LP*Tr**2. + Q5_B_LP*Tr**(-2.)
1069
+ C0 = Q1_C_LP + Q2_C_LP*Tr + Q3_C_LP*Tr**(-1.) + Q4_C_LP*Tr**2. + Q5_C_LP*Tr**(-2.)
1070
+ D0 = 0.0
1071
+ integral0 = A0*gp.log(Pr_/P0r_) + B0*(Pr_ - P0r_) + (C0/2.0)*(pow(Pr_,2.0) - pow(P0r_,2.0)) + (D0/3.0)*(pow(Pr_,3.0) - pow(P0r_,3.0))
1072
+ elif P > 1000.:
1073
+ A = Q1_A_HP + Q2_A_HP*Tr + Q3_A_HP*Tr**(-1.) + Q4_A_HP*Tr**2. + Q5_A_HP*Tr**(-2.) + Q6_A_HP*Tr**3. + Q7_A_HP*Tr**(-3.0) + Q8_A_HP*gp.log(Tr)
1074
+ B = Q1_B_HP + Q2_B_HP*Tr + Q3_B_HP*Tr**(-1.) + Q4_B_HP*Tr**2. + Q5_B_HP*Tr**(-2.) + Q6_B_HP*Tr**3. + Q7_B_HP*Tr**(-3.0) + Q8_B_HP*gp.log(Tr)
1075
+ C = Q1_C_HP + Q2_C_HP*Tr + Q3_C_HP*Tr**(-1.) + Q4_C_HP*Tr**2. + Q5_C_HP*Tr**(-2.) + Q6_C_HP*Tr**3. + Q7_C_HP*Tr**(-3.0) + Q8_C_HP*gp.log(Tr)
1076
+ D = Q1_D_HP + Q2_D_HP*Tr + Q3_D_HP*Tr**(-1.) + Q4_D_HP*Tr**2. + Q5_D_HP*Tr**(-2.) + Q6_D_HP*Tr**3. + Q7_D_HP*Tr**(-3.0) + Q8_D_HP*gp.log(Tr)
1077
+ P0 = 1000.0
1078
+ Pr_ = 1000.0/Pcr
1079
+ P0r_ = 1.0/Pcr
1080
+ A0 = Q1_A_LP + Q2_A_LP*Tr + Q3_A_LP*Tr**(-1.) + Q4_A_LP*Tr**2. + Q5_A_LP*Tr**(-2.)
1081
+ B0 = Q1_B_LP + Q2_B_LP*Tr + Q3_B_LP*Tr**(-1.) + Q4_B_LP*Tr**2. + Q5_B_LP*Tr**(-2.)
1082
+ C0 = Q1_C_LP + Q2_C_LP*Tr + Q3_C_LP*Tr**(-1.) + Q4_C_LP*Tr**2. + Q5_C_LP*Tr**(-2.)
1083
+ D0 = 0.0
1084
+ integral0 = A0*gp.log(Pr_/P0r_) + B0*(Pr_ - P0r_) + (C0/2.0)*(pow(Pr_,2.0) - pow(P0r_,2.0)) + (D0/3.0)*(pow(Pr_,3.0) - pow(P0r_,3.0))
1085
+ P0r = P0/Pcr
1086
+ Pr = P/Pcr
1087
+ integral = A*gp.log(Pr/P0r) + B*(Pr - P0r) + (C/2.0)*(pow(Pr,2.0) - pow(P0r,2.0)) + (D/3.0)*(pow(Pr,3.0) - pow(P0r,3.0))
1088
+ return gp.exp(integral + integral0)/P
1089
+
1090
+ def y_H2O(PT,species,models):
1091
+ P = PT['P']
1092
+ T_K = PT['T']+273.15
1093
+ ideal_gas = models.loc["ideal_gas","option"]
1094
+ if ideal_gas == "yes":
1095
+ return 1.
1096
+ elif P < 1.: # ideal gas below 1 bar
1097
+ return 1.
1098
+ if models.loc["y_H2O","option"] == "HP91":
1099
+ # (T > 673 K only) - using Holland & Powell (1991) CORK
1100
+ p0 = 2.00 # in kb
1101
+ a = 1113.4 + -0.22291*(T_K - 673.0) + -3.8022e-4*pow((T_K-673.0),2.0) + 1.7791e-7*pow((T_K-673.0),3.0)
1102
+ b = 1.465
1103
+ c = -3.025650e-2 + -5.343144e-6*T_K
1104
+ d = -3.2297554e-3 + 2.2215221e-6*T_K
1105
+ y = CORK(PT,p0,a,b,c,d)
1106
+ return y
1107
+
1108
+ def y_CO2(PT,species,models):
1109
+ P = PT['P']
1110
+ T_K = PT['T']+273.15
1111
+ ideal_gas = models.loc["ideal_gas","option"]
1112
+ if ideal_gas == "yes":
1113
+ return 1.0
1114
+ elif P < 1.: # ideal gas below 1 bar
1115
+ return 1.
1116
+ else:
1117
+ if models.loc["y_CO2","option"] == "HP91": # use Holland & Powell (1991)
1118
+ p0 = 5.00 # in kb
1119
+ a = 741.2 + -0.10891*(T_K) + -3.4203e-4*pow(T_K,2.0)
1120
+ b = 3.057
1121
+ c = -2.26924e-1 + -7.73793e-5*T_K
1122
+ d = 1.33790e-2 + -1.1740e-5*T_K
1123
+ y = CORK(PT,p0,a,b,c,d)
1124
+ elif models.loc["y_CO2","option"] == "SS92": # use Shi & Saxena (1992)
1125
+ gas_species = "CO2"
1126
+ y = y_SS(gas_species,PT,species,models)
1127
+ return y
1128
+
1129
+ def y_O2(PT,species,models):
1130
+ if models.loc["y_O2","option"] == "SS92":
1131
+ gas_species = "O2"
1132
+ y = y_SS(gas_species,PT,species,models)
1133
+ return y
1134
+
1135
+ def y_S2(PT,species,models):
1136
+ if models.loc["y_S2","option"] == "SS92":
1137
+ gas_species = "S2"
1138
+ y = y_SS(gas_species,PT,species,models)
1139
+ return y
1140
+
1141
+ def y_CO(PT,species,models):
1142
+ if models.loc["y_CO","option"] == "SS92":
1143
+ gas_species = "CO"
1144
+ y = y_SS(gas_species,PT,species,models)
1145
+ return y
1146
+
1147
+ def y_CH4(PT,species,models):
1148
+ if models.loc["y_CH4","option"] == "SS92":
1149
+ gas_species = "CH4"
1150
+ y = y_SS(gas_species,PT,species,models)
1151
+ return y
1152
+
1153
+ def y_OCS(PT,species,models):
1154
+ if models.loc["y_OCS","option"] == "SS92":
1155
+ gas_species = "OCS"
1156
+ y = y_SS(gas_species,PT,species,models)
1157
+ return y
1158
+
1159
+ def y_X(PT,species,models): # species X fugacity coefficient
1160
+ if models.loc["y_X","option"] == "ideal": # ideal gas
1161
+ y = 1.
1162
+ return y
1163
+
1164
+ #################################################################################
1165
+ ### SO2 and H2S from Shi & Saxena (1992) with option to modify below 500 bars ###
1166
+ #################################################################################
1167
+
1168
+ def y_SO2(PT,species,models):
1169
+ P = PT['P']
1170
+ T_K = PT['T']+273.15
1171
+ ideal_gas = models.loc["ideal_gas","option"]
1172
+ gas_species = "SO2"
1173
+ if ideal_gas == "yes":
1174
+ return 1.
1175
+ elif P < 1.: # ideal gas below 1 bar
1176
+ return 1.
1177
+ else: # 1-10000 bar
1178
+ Tcr = species.loc[gas_species,"Tcr"] # critical temperature in K
1179
+ Pcr = species.loc[gas_species,"Pcr"] # critical temperature in bar
1180
+ P0 = 1.0
1181
+ P0r = P0/Pcr
1182
+ Tr = T_K/Tcr
1183
+ Q1_A, Q2_A, Q3_A, Q4_A, Q5_A, Q6_A, Q7_A, Q8_A = 0.92854, 0.43269e-1, -0.24671, 0., 0.24999, 0., -0.53182, -0.16461e-1
1184
+ Q1_B, Q2_B, Q3_B, Q4_B, Q5_B, Q6_B, Q7_B, Q8_B = 0.84866e-3, -0.18379e-2, 0.66787e-1, 0., -0.29427e-1, 0., 0.29003e-1, 0.54808e-2
1185
+ Q1_C, Q2_C, Q3_C, Q4_C, Q5_C, Q6_C, Q7_C, Q8_C = -0.35456e-3, 0.23316e-4, 0.94159e-3, 0., -0.81653e-3, 0., 0.23154e-3, 0.55542e-4
1186
+ A = Q1_A + Q2_A*Tr + Q3_A*Tr**(-1.) + Q4_A*Tr**2. + Q5_A*Tr**(-2.) + Q6_A*Tr**3. + Q7_A*Tr**(-3.0) + Q8_A*gp.log(Tr)
1187
+ B = Q1_B + Q2_B*Tr + Q3_B*Tr**(-1.) + Q4_B*Tr**2. + Q5_B*Tr**(-2.) + Q6_B*Tr**3. + Q7_B*Tr**(-3.0) + Q8_B*gp.log(Tr)
1188
+ C = Q1_C + Q2_C*Tr + Q3_C*Tr**(-1.) + Q4_C*Tr**2. + Q5_C*Tr**(-2.) + Q6_C*Tr**3. + Q7_C*Tr**(-3.0) + Q8_C*gp.log(Tr)
1189
+ D = 0.0
1190
+ if P >= 500.: # above 500 bar using Shi and Saxena (1992) as is
1191
+ Pr = P/Pcr
1192
+ integral = A*gp.log(Pr/P0r) + B*(Pr - P0r) + (C/2.0)*(pow(Pr,2.0) - pow(P0r,2.0)) + (D/3.0)*(pow(Pr,3.0) - pow(P0r,3.0))
1193
+ y = (gp.exp(integral))/P
1194
+ elif models.loc["y_SO2","option"] == "SS92": # as is Shi and Saxena (1992)
1195
+ Pr = P/Pcr
1196
+ integral = A*gp.log(Pr/P0r) + B*(Pr - P0r) + (C/2.0)*(pow(Pr,2.0) - pow(P0r,2.0)) + (D/3.0)*(pow(Pr,3.0) - pow(P0r,3.0))
1197
+ y = (gp.exp(integral))/P
1198
+ elif models.loc["y_SO2","option"] == "SS92_modified": # below 500 bar linear fit between the value at 500 bar and y = 1 at 1 bar to avoid weird behaviour...
1199
+ Pr = 500./Pcr # calculate y at 500 bar
1200
+ integral = A*gp.log(Pr/P0r) + B*(Pr - P0r) + (C/2.0)*(pow(Pr,2.0) - pow(P0r,2.0)) + (D/3.0)*(pow(Pr,3.0) - pow(P0r,3.0))
1201
+ y_500 = (gp.exp(integral))/500.
1202
+ y = ((y_500 - 1.)*(P/500.)) + 1. # linear extrapolation to P of interest
1203
+ return y
1204
+
1205
+ def y_H2S(PT,species,models):
1206
+ P = PT['P']
1207
+ T_K = PT['T']+273.15
1208
+ ideal_gas = models.loc["ideal_gas","option"]
1209
+ gas_species = "H2S"
1210
+ if ideal_gas == "yes":
1211
+ return 1.0
1212
+ elif ideal_gas == "no":
1213
+ Tcr = species.loc[gas_species,"Tcr"] # critical temperature in K
1214
+ Pcr = species.loc[gas_species,"Pcr"] # critical temperature in bar
1215
+ Tr = T_K/Tcr
1216
+ # Q for 1-500 bar
1217
+ Q1_A_LP, Q2_A_LP, Q3_A_LP, Q4_A_LP, Q5_A_LP, Q6_A_LP, Q7_A_LP, Q8_A_LP = 0.14721e1, 0.11177e1, 0.39657e1, 0., -0.10028e2, 0., 0.45484e1, -0.382e1
1218
+ Q1_B_LP, Q2_B_LP, Q3_B_LP, Q4_B_LP, Q5_B_LP, Q6_B_LP, Q7_B_LP, Q8_B_LP = 0.16066, 0.10887, 0.29014, 0., -0.99593, 0., -0.18627, -0.45515
1219
+ Q1_C_LP, Q2_C_LP, Q3_C_LP, Q4_C_LP, Q5_C_LP, Q6_C_LP, Q7_C_LP, Q8_C_LP = -0.28933, -0.70522e-1, 0.39828, 0., -0.50533e-1, 0., 0.1176, 0.33972
1220
+ # Q for 500-10000 bar
1221
+ Q1_A_HP, Q2_A_HP, Q3_A_HP, Q4_A_HP, Q5_A_HP, Q6_A_HP, Q7_A_HP, Q8_A_HP = 0.59941, -0.1557e-2, 0.4525e-1, 0., 0.36687, 0., -0.79248, 0.26058
1222
+ Q1_B_HP, Q2_B_HP, Q3_B_HP, Q4_B_HP, Q5_B_HP, Q6_B_HP, Q7_B_HP, Q8_B_HP = 0.22545e-1, 0.17473e-2, 0.48253e-1, 0., -0.1989e-1, 0., 0.32794e-1, -0.10985e-1
1223
+ Q1_C_HP, Q2_C_HP, Q3_C_HP, Q4_C_HP, Q5_C_HP, Q6_C_HP, Q7_C_HP, Q8_C_HP = 0.57375e-3, -0.20944e-5, -0.11894e-2, 0., 0.14661e-2, 0., -0.75605e-3, -0.27985e-3
1224
+ if P < 1.:
1225
+ return 1. # ideal gas below 1 bar
1226
+ elif P < 500.:
1227
+ if models.loc["y_H2S","option"] == "SS92": # as is Shi and Saxena (1992)
1228
+ A = Q1_A_LP + Q2_A_LP*Tr + Q3_A_LP*Tr**(-1.) + Q4_A_LP*Tr**2. + Q5_A_LP*Tr**(-2.) + Q6_A_LP*Tr**3. + Q7_A_LP*Tr**(-3.0) + Q8_A_LP*gp.log(Tr)
1229
+ B = Q1_B_LP + Q2_B_LP*Tr + Q3_B_LP*Tr**(-1.) + Q4_B_LP*Tr**2. + Q5_B_LP*Tr**(-2.) + Q6_B_LP*Tr**3. + Q7_B_LP*Tr**(-3.0) + Q8_B_LP*gp.log(Tr)
1230
+ C = Q1_C_LP + Q2_C_LP*Tr + Q3_C_LP*Tr**(-1.) + Q4_C_LP*Tr**2. + Q5_C_LP*Tr**(-2.) + Q6_C_LP*Tr**3. + Q7_C_LP*Tr**(-3.0) + Q8_C_LP*gp.log(Tr)
1231
+ D = 0.0
1232
+ P0 = 1.0
1233
+ integral0 = 0.
1234
+ elif models.loc["y_SO2","option"] == "SS92_modified": # below 500 bar linear fit between the value at 500 bar and y = 1 at 1 bar to avoid weird behaviour...
1235
+ P0 = 500.0 # calculate y at 500 bars
1236
+ Pr_ = 500.0/Pcr
1237
+ P0r_ = 1.0/Pcr
1238
+ A0 = Q1_A_LP + Q2_A_LP*Tr + Q3_A_LP*Tr**(-1.) + Q4_A_LP*Tr**2. + Q5_A_LP*Tr**(-2.) + Q6_A_LP*Tr**3. + Q7_A_LP*Tr**(-3.0) + Q8_A_LP*gp.log(Tr)
1239
+ B0 = Q1_B_LP + Q2_B_LP*Tr + Q3_B_LP*Tr**(-1.) + Q4_B_LP*Tr**2. + Q5_B_LP*Tr**(-2.) + Q6_B_LP*Tr**3. + Q7_B_LP*Tr**(-3.0) + Q8_B_LP*gp.log(Tr)
1240
+ C0 = Q1_C_LP + Q2_C_LP*Tr + Q3_C_LP*Tr**(-1.) + Q4_C_LP*Tr**2. + Q5_C_LP*Tr**(-2.) + Q6_C_LP*Tr**3. + Q7_C_LP*Tr**(-3.0) + Q8_C_LP*gp.log(Tr)
1241
+ D0 = 0.0
1242
+ integral0 = A0*gp.log(Pr_/P0r_) + B0*(Pr_ - P0r_) + (C0/2.0)*(pow(Pr_,2.0) - pow(P0r_,2.0)) + (D0/3.0)*(pow(Pr_,3.0) - pow(P0r_,3.0))
1243
+ y_500 = gp.exp(integral0)/500.
1244
+ y = ((y_500 - 1.)*(P/500.)) + 1. # linear extrapolation to P of interest
1245
+ return y
1246
+ elif P == 500.:
1247
+ A = 0.0
1248
+ B = 0.0
1249
+ C = 0.0
1250
+ D = 0.0
1251
+ P0 = 500.0
1252
+ Pr_ = 500.0/Pcr
1253
+ P0r_ = 1.0/Pcr
1254
+ A0 = Q1_A_LP + Q2_A_LP*Tr + Q3_A_LP*Tr**(-1.) + Q4_A_LP*Tr**2. + Q5_A_LP*Tr**(-2.) + Q6_A_LP*Tr**3. + Q7_A_LP*Tr**(-3.0) + Q8_A_LP*gp.log(Tr)
1255
+ B0 = Q1_B_LP + Q2_B_LP*Tr + Q3_B_LP*Tr**(-1.) + Q4_B_LP*Tr**2. + Q5_B_LP*Tr**(-2.) + Q6_B_LP*Tr**3. + Q7_B_LP*Tr**(-3.0) + Q8_B_LP*gp.log(Tr)
1256
+ C0 = Q1_C_LP + Q2_C_LP*Tr + Q3_C_LP*Tr**(-1.) + Q4_C_LP*Tr**2. + Q5_C_LP*Tr**(-2.) + Q6_C_LP*Tr**3. + Q7_C_LP*Tr**(-3.0) + Q8_C_LP*gp.log(Tr)
1257
+ D0 = 0.0
1258
+ integral0 = A0*gp.log(Pr_/P0r_) + B0*(Pr_ - P0r_) + (C0/2.0)*(pow(Pr_,2.0) - pow(P0r_,2.0)) + (D0/3.0)*(pow(Pr_,3.0) - pow(P0r_,3.0))
1259
+ elif P > 500.:
1260
+ A = Q1_A_HP + Q2_A_HP*Tr + Q3_A_HP*Tr**(-1.) + Q4_A_HP*Tr**2. + Q5_A_HP*Tr**(-2.) + Q6_A_HP*Tr**3. + Q7_A_HP*Tr**(-3.0) + Q8_A_HP*gp.log(Tr)
1261
+ B = Q1_B_HP + Q2_B_HP*Tr + Q3_B_HP*Tr**(-1.) + Q4_B_HP*Tr**2. + Q5_B_HP*Tr**(-2.) + Q6_B_HP*Tr**3. + Q7_B_HP*Tr**(-3.0) + Q8_B_HP*gp.log(Tr)
1262
+ C = Q1_C_HP + Q2_C_HP*Tr + Q3_C_HP*Tr**(-1.) + Q4_C_HP*Tr**2. + Q5_C_HP*Tr**(-2.) + Q6_C_HP*Tr**3. + Q7_C_HP*Tr**(-3.0) + Q8_C_HP*gp.log(Tr)
1263
+ D = 0.0
1264
+ P0 = 500.0
1265
+ Pr_ = 500.0/Pcr
1266
+ P0r_ = 1.0/Pcr
1267
+ A0 = Q1_A_LP + Q2_A_LP*Tr + Q3_A_LP*Tr**(-1.) + Q4_A_LP*Tr**2. + Q5_A_LP*Tr**(-2.) + Q6_A_LP*Tr**3. + Q7_A_LP*Tr**(-3.0) + Q8_A_LP*gp.log(Tr)
1268
+ B0 = Q1_B_LP + Q2_B_LP*Tr + Q3_B_LP*Tr**(-1.) + Q4_B_LP*Tr**2. + Q5_B_LP*Tr**(-2.) + Q6_B_LP*Tr**3. + Q7_B_LP*Tr**(-3.0) + Q8_B_LP*gp.log(Tr)
1269
+ C0 = Q1_C_LP + Q2_C_LP*Tr + Q3_C_LP*Tr**(-1.) + Q4_C_LP*Tr**2. + Q5_C_LP*Tr**(-2.) + Q6_C_LP*Tr**3. + Q7_C_LP*Tr**(-3.0) + Q8_C_LP*gp.log(Tr)
1270
+ D0 = 0.0
1271
+ integral0 = A0*gp.log(Pr_/P0r_) + B0*(Pr_ - P0r_) + (C0/2.0)*(pow(Pr_,2.0) - pow(P0r_,2.0)) + (D0/3.0)*(pow(Pr_,3.0) - pow(P0r_,3.0))
1272
+ P0r = P0/Pcr
1273
+ Pr = P/Pcr
1274
+ integral = A*gp.log(Pr/P0r) + B*(Pr - P0r) + (C/2.0)*(pow(Pr,2.0) - pow(P0r,2.0)) + (D/3.0)*(pow(Pr,3.0) - pow(P0r,3.0))
1275
+ return gp.exp(integral + integral0)/P
1276
+
1277
+ def y_SO3(PT,species,models):
1278
+ return 1.
1279
+
1280
+ #######################
1281
+ ### oxygen fugacity ###
1282
+ #######################
1283
+
1284
+ # buffers
1285
+ def NNO(PT,models):
1286
+ P = PT['P']
1287
+ T_K = PT["T"]+273.15
1288
+ if models.loc["NNObuffer","option"] == "Frost91":
1289
+ buffer = (-24930/T_K + 9.36 + 0.046*(P-1.0)/T_K) # Frost (1991)
1290
+ return buffer
1291
+
1292
+ def FMQ(PT,models):
1293
+ P = PT['P']
1294
+ T_K = PT["T"]+273.15
1295
+ if models.loc["FMQbuffer","option"] == "Frost91":
1296
+ buffer = (-25096.3/T_K + 8.735 + 0.11*(P-1.0)/T_K) # Frost (1991)
1297
+ elif models.loc["FMQbuffer","option"] == "ONeill87":
1298
+ buffer = (8.58 - (25050/T_K)) # O'Neill (1987)
1299
+ return buffer
1300
+
1301
+
1302
+ # terms for different equations
1303
+
1304
+ def FefO2_KC91_Eq7_terms(PT,melt_wf,species,models): # terms for Kress & Carmichael (1991) Equation 7
1305
+ # ln(XFe2O3/XFeO) = alnfO2 + (b/T) + c + sum(dX) + e[1 - (T0/T) = ln(T/T0)] + f(P/T) + g(((T-T0)P)/T) + h(P2/T)
1306
+ # ln(XFe2O3/XFeO) = alnfO2 + B
1307
+ # terms
1308
+ a = 0.196
1309
+ # sum(dX)
1310
+ # mole frations in the melt based on oxide components (all Fe as FeO) with no volatiles
1311
+ melt_comp = mg.melt_mole_fraction(melt_wf,species,models,"no","no")
1312
+ DAl = -2.243
1313
+ DFe = -1.828
1314
+ DCa = 3.201
1315
+ DNa = 5.854
1316
+ DK = 6.215
1317
+ D4X = DAl*melt_comp["Al2O3"] + DFe*melt_comp["FeOT"] + DCa*melt_comp["CaO"] + DNa*melt_comp["Na2O"] + DK*melt_comp["K2O"]
1318
+ # PT term
1319
+ P = PT['P']
1320
+ T_K = PT['T']+273.15
1321
+ b = 1.1492e4 # K
1322
+ c = -6.675
1323
+ e = -3.36
1324
+ f = -7.01e-7 # K/Pa
1325
+ g = -1.54e-10 # /Pa
1326
+ h = 3.85e-17 # K/Pa2
1327
+ T0 = 1673.0 # K
1328
+ P_Pa = P*1.0e5 # converts bars to pascals
1329
+ B = (b/T_K) + c + D4X + e*(1.0 - (T0/T_K) - math.log(T_K/T0)) + f*(P_Pa/T_K) + g*(((T_K-T0)*P_Pa)/T_K) + h*((P_Pa**2.0)/T_K)
1330
+ return a, B
1331
+
1332
+ def FefO2_KC91_EqA_terms(PT,melt_wf,species,models): # terms for Kress & Carmichael (1991) Appendix Equations A1-6
1333
+ # XFeO1.5/XFeO = (KD1*fO2**0.25 + 2y*KD2*KD1**2y*fO2**0.5y)/(1 + (1-2y)KD2*KD1**2y*fO2**0.5y)
1334
+ KD2 = 0.4
1335
+ y = 0.3
1336
+ # compositional term
1337
+ # mole frations in the melt based on oxide components (all Fe as FeO) with no volatiles
1338
+ melt_comp = mg.melt_mole_fraction(melt_wf,species,models,"no","no")
1339
+ DWAl = 39.86e3 #J
1340
+ DWCa = -62.52e3 #J
1341
+ DWNa = -102.0e3 #J
1342
+ DWK = -119.0e3 #J
1343
+ D4X = DWAl*melt_comp["Al2O3"]+DWCa*melt_comp["CaO"]+DWNa*melt_comp["Na2O"]+DWK*melt_comp["K2O"]
1344
+ # KD1
1345
+ T_K = PT['T']+273.15
1346
+ P = PT['P']
1347
+ DH = -106.2e3 #J
1348
+ DS = -55.10 #J/K
1349
+ DCp = 31.86 #J/K
1350
+ DV = 7.42e-6 #m3
1351
+ DVdot = 1.63e-9 #m3/K
1352
+ DVdash = -8.16e-16 #m3/Pa
1353
+ T0 = 1673.0 # K
1354
+ P0 = 1.0e5 # Pa
1355
+ R = 8.3144598 # J/K/mol
1356
+ P_Pa = P*1.0e5
1357
+ KD1 = math.exp((-DH/(R*T_K)) + (DS/R) - (DCp/R)*(1.0 - (T0/T_K) - gp.log(T_K/T0)) - (1.0/(R*T_K))*D4X - ((DV*(P_Pa-P0))/(R*T_K)) - ((DVdot*(T_K-T0)*(P_Pa-P0))/(R*T_K)) - (DVdash/(2.0*R*T_K))*pow((P_Pa-P0),2.0))
1358
+ return KD1, KD2, y
1359
+
1360
+ def FefO2_ONeill18_terms(PT,melt_wf,species,models):
1361
+ # O'Neill et al. (2018) EPSL 504:152-162
1362
+ # 1n(Fe3Fe2) = a*DFMQ + B
1363
+ a = 0.125
1364
+ # mole fractions on a single cation basis in the melt based on oxide components (all Fe as FeO) with no volatiles
1365
+ melt_comp = mg.melt_cation_proportion(melt_wf,species,"volatiles","Fe speciation")
1366
+ B = - 1.36 + 2.4*melt_comp["Ca"] + 2.0*melt_comp["Na"] + 3.7*melt_comp["K"] - 2.4*melt_comp["P"]
1367
+ # FMQ
1368
+ T_K = PT['T']+273.15
1369
+ FMQ = 8.58 - (25050/T_K) # O'Neill (1987)
1370
+ return a, B, FMQ
1371
+
1372
+ def FefO2_Borisov18_terms(PT,melt_wf,species,models):
1373
+ T_K = PT['T']+273.15
1374
+ # Borisov et al. (2018) CMP 173
1375
+ a = 0.207
1376
+ # melt mole fraction with no volatiles and all Fe as FeOT
1377
+ melt_comp = mg.melt_mole_fraction(melt_wf,species,models,"no","no")
1378
+ B = (4633.3/T_K -0.445*melt_comp["SiO2"] - 0.900*melt_comp["TiO2"] + 1.532*melt_comp["MgO"] + 0.314*melt_comp["CaO"] + 2.030*melt_comp["Na2O"] + 3.355*melt_comp["K2O"] - 4.851*melt_comp["P2O5"] - 3.081*melt_comp["SiO2"]*melt_comp["Al2O3"] - 4.370*melt_comp["SiO2"]*melt_comp["MgO"] - 1.852)
1379
+ return a, B
1380
+
1381
+ def fO22Fe3FeT(fO2,PT,melt_wf,species,models): # converting fO2 to Fe3/FeT
1382
+ model = models.loc["fO2","option"]
1383
+ T_K = PT['T']+273.15
1384
+
1385
+ if model == "Kress91":
1386
+ a, PTterm = FefO2_KC91_Eq7_terms(PT,melt_wf,species,models)
1387
+ lnXFe2O3XFeO = a*gp.log(fO2) + PTterm
1388
+ XFe2O3XFeO = gp.exp(lnXFe2O3XFeO)
1389
+ return (2.0*XFe2O3XFeO)/((2.0*XFe2O3XFeO)+1.0)
1390
+
1391
+ elif model == "Kress91A":
1392
+ kd1, KD2, y = FefO2_KC91_EqA_terms(PT,melt_wf,species,models)
1393
+ XFeO15XFeO = ((kd1*fO2**0.25)+(2.0*y*KD2*(kd1**(2.0*y))*(fO2**(0.5*y))))/(1.0 + (1.0 - 2.0*y)*KD2*(kd1**(2.0*y))*(fO2**(0.5*y)))
1394
+ return XFeO15XFeO/(XFeO15XFeO+1.0)
1395
+
1396
+ elif model == "ONeill18": # O'Neill et al. (2018) EPSL 504:152-162
1397
+ a,B,FMQ = FefO2_ONeill18_terms(PT,melt_wf,species,models)
1398
+ DQFM = gp.log10(fO2) - FMQ
1399
+ lnFe3Fe2 = a*DQFM + B
1400
+ Fe3Fe2 = gp.exp(lnFe3Fe2)
1401
+ return Fe3Fe2/(Fe3Fe2 + 1.0)
1402
+
1403
+ elif model == "Borisov18": # Borisov et al. (2018) CMP 173:
1404
+ a,B = FefO2_Borisov18_terms(PT,melt_wf,species,models)
1405
+ Fe3Fe2 = 10.**(a*gp.log10(fO2) + B)
1406
+ return Fe3Fe2/(Fe3Fe2 + 1.0)
1407
+
1408
+ def f_O2(PT,melt_wf,species,models):
1409
+
1410
+ model = models.loc["fO2","option"]
1411
+
1412
+ def KC91(PT,melt_wf,species,models):
1413
+ a, PTterm = FefO2_KC91_Eq7_terms(PT,melt_wf,species,models)
1414
+ F = 0.5*mg.Fe3Fe2(melt_wf) # XFe2O3/XFeO
1415
+ alnfO2 = math.log(F) - PTterm
1416
+ fO2 = math.exp(alnfO2/a)
1417
+ return fO2
1418
+
1419
+ #if model == "yes":
1420
+ # return 10.0**(setup.loc[run,"logfO2"])
1421
+
1422
+ if model == "Kress91":
1423
+ fO2 = KC91(PT,melt_wf,species,models)
1424
+ return fO2
1425
+
1426
+ elif model == "Kress91A":
1427
+ F = mg.Fe3Fe2(melt_wf) # XFeO1.5/XFeO
1428
+ kd1, KD2, y = FefO2_KC91_EqA_terms(PT,melt_wf,species,models)
1429
+
1430
+ def f(y,F,KD2,kd1,x): # KC91A rearranged to equal 0
1431
+ f = ((2.0*y - F + 2.0*y*F)*KD2*kd1**(2.0*y)*x**(0.5*y) + kd1*x**0.25 - F)
1432
+ return f
1433
+
1434
+ def df(y,F,KD2,kd1,x): # derivative of above
1435
+ df = (0.5*y)*(2.0*y - F +2.0*y*F)*KD2*kd1**(2.0*y)*x**((0.5*y)-1.0) + 0.25*kd1*x**-0.75
1436
+ return df
1437
+
1438
+ def dx(x):
1439
+ diff = abs(0-f(y,F,KD2,kd1,x))
1440
+ return diff
1441
+
1442
+ def nr(x0, e1):
1443
+ delta1 = dx(x0)
1444
+ while delta1 > e1:
1445
+ x0 = x0 - f(y,F,KD2,kd1,x0)/df(y,F,KD2,kd1,x0)
1446
+ delta1 = dx(x0)
1447
+ return x0
1448
+
1449
+ x0 = KC91(PT,melt_wf,species,models)
1450
+
1451
+ fO2 = nr(x0, 1.e-15)
1452
+ return fO2
1453
+
1454
+ elif model == "ONeill18": # O'Neill et al. (2018) EPSL 504:152-162
1455
+ F = mg.Fe3Fe2(melt_wf) # Fe3+/Fe2+
1456
+ a,B,FMQ = FefO2_ONeill18_terms(PT,melt_wf,species,models)
1457
+ DQFM = (math.log(F) - B)/a
1458
+ logfO2 = DQFM + FMQ
1459
+ return 10.0**logfO2
1460
+
1461
+ #elif model == "S6ST": # remove?!?!
1462
+ # S6T = melt_wf['S6ST']
1463
+ # S62 = mg.overtotal2ratio(S6T)
1464
+ # fO2 = mg.S6S2_2_fO2(S62,melt_wf,run,PT,setup,species,models)
1465
+ # return fO2
1466
+
1467
+ elif model == "Borisov18": # Borisov et al. (2018) CMP 173
1468
+ F = mg.Fe3Fe2(melt_wf)
1469
+ a,B = FefO2_Borisov18_terms(PT,melt_wf,species,models)
1470
+ fO2 = 10.**((gp.log10(F) - B)/a)
1471
+ return fO2
1472
+
1473
+ def S_Nash19_terms(PT): # Nash et al. 2019
1474
+ T_K = PT['T']+273.15
1475
+ A = 8.
1476
+ B = ((8.7436e6)/pow(T_K,2.0)) - (27703.0/T_K) + 20.273
1477
+ return A, B
1478
+
1479
+ # density of the melt in g/cm3 using DensityX (Iacovino & Till 2019 Volcanica 2(1))
1480
+ def melt_density(PT,melt_wf,species,models):
1481
+ if models.loc["density","option"] == "DensityX":
1482
+ melt_comp = mg.melt_normalise_wf(melt_wf,species,"water","yes")
1483
+ P = PT["P"]
1484
+ T = PT["T"]
1485
+ melt_dx = pd.DataFrame([["sample", melt_comp["SiO2"], melt_comp["TiO2"], melt_comp["Al2O3"], melt_comp["FeO"], melt_comp["Fe2O3"], melt_comp["MgO"], melt_comp["CaO"], melt_comp["Na2O"], melt_comp["K2O"], melt_comp["H2O"],P,T]])
1486
+ melt_dx.columns = ["Sample_ID","SiO2","TiO2","Al2O3","FeO","Fe2O3","MgO","CaO","Na2O","K2O","H2O","P","T"]
1487
+ output = dx.Density(melt_dx)
1488
+ density = output.loc[0,"Density_g_per_cm3"]
1489
+ return density
1490
+
1491
+ #################################################################################################################################
1492
+ ################################################# ISOTOPE FRACTIONATION FACTORS #################################################
1493
+ #################################################################################################################################
1494
+
1495
+ # beta factors from Richet et al. (1977) fitted to quadratic equation for 600 < T'C < 1300
1496
+
1497
+ def beta_gas(PT,element,species):
1498
+ t = 1./(PT["T"]+273.15)
1499
+ if element == "S":
1500
+ if species == "SO2":
1501
+ a, b, c = 4872.56428, 0.76400, 0.99975
1502
+ elif species == "S2":
1503
+ a, b, c = 1708.22425, -0.76202, 1.00031
1504
+ elif species == "OCS":
1505
+ a, b, c = 980.75175, 1.74954, 0.99930
1506
+ elif species == "H2S":
1507
+ a, b, c = 935.84901, 1.29355, 0.99969
1508
+ if models.loc["beta_factors","option"] == "Richet77":
1509
+ value = a*t**2 + b*t + c
1510
+ return value
1511
+
1512
+ def alpha_gas(element,A,B,PT):
1513
+ beta_A = beta_gas(PT,element,A)
1514
+ beta_B = beta_gas(PT,element,B)
1515
+ result = beta_A/beta_B
1516
+ return result
1517
+
1518
+ def alpha_H2S_S(PT,models): # Fiege et al. (2015) Chemical Geology equation 8 - H2S fluid and S2- melt
1519
+ if models.loc["alpha_H2S_S","option"] == "Fiege15":
1520
+ T_K = PT["T"] + 273.15
1521
+ lna103 = (10.84*((1000./T_K)**2)) - 2.5
1522
+ a = gp.exp(lna103/1000.)
1523
+ return a
1524
+
1525
+ def alpha_SO2_SO4(PT,models): # Fiege et al. (2015) Chemical Geology equation 9 - SO2 fluid and SO4 melt
1526
+ if models.loc["alpha_SO2_SO4","option"] == "Fiege15":
1527
+ T_K = PT["T"] + 273.15
1528
+ lna103 = (-0.42*((1000./T_K)**3)) - (2.133*((1000./T_K)**3)) - (0.105*(1000./T_K)) - 0.41
1529
+ a = gp.exp(lna103/1000.)
1530
+ return a
1531
+
1532
+ def alpha_A_B(element,A,B,PT,models):
1533
+ if A == "SO2" and B == "H2S":
1534
+ a = alpha_gas(element,A,B,PT)