Integration of pygcc with EQ3/6

Import pygcc, EQ3/6 environment and other modules

All files used in this tutorial can be downloaded from https://bitbucket.org/Tutolo-RTG/pygcc/src/master/docs/

Download output_reader into your working directory

import os, subprocess, re, numpy as np, pandas as pd, sys
import shutil
from pygcc.pygcc_utils import *
import pygcc
print(pygcc.__version__)
from output_reader import read_eq36output
1.2.0

Set current working directory and EQ3/6 working directory. This is because EQ3/6 can only run from its working directory

eq_dir = "C:\Eq3_6v8.0a\work_db"
if os.path.exists(eq_dir) == False:
    os.makedirs(os.path.join(eq_dir))
    
current_dir = os.getcwd()
output_folder = 'Vent_spec'
if os.path.exists(os.path.join(os.getcwd(), output_folder)) == False:
    os.makedirs(os.path.join(os.getcwd(), output_folder))
# function to copy file between source and destination directory
def copyfile(source_dir, destination_dir):
    # Source path
    source = source_dir

    # Destination path
    destination = destination_dir
  
    try:
        shutil.copy(source, destination)
        print("File copied successfully.")

    # If source and destination are same
    except shutil.SameFileError:
        print("Same files are in the Source and destination path.")

    # If there is any permission issue
    except PermissionError:
        print("Permission denied.")

    # For other errors
    except:
        print("Error occurred while copying file.")

Load filtered vent fluid data

filename = './MARHYS_DB_2_0_filter.csv'
df = pd.read_csv(filename)
i = 2
df.loc[i, :]
Sample-ID                       EPR 21°N-OBS-1985
Geol_setting         Mid-oceanic spreading center
Rock_type_primary                          Basalt
Depth                                        2500
Temp                                          340
pH                                            3.4
XH2S                                          7.6
Cl                                          500.0
Na                                          439.0
Fe                                           1530
Mn                                           1024
H2                                           0.01
K                                            23.5
Ca                                           15.6
Si                                           17.6
Cu                                            0.0
Press                                  251.945325
Name: 2, dtype: object

Here we will work with EPR 21 N sample to run speciation and warm up the fluid from 25 °C to 340 °C

Generate the database for the vent fluid with pygcc and run the database through EQ3/6 preprocessing

We initialize the inputs and generate the database

P = np.round(df.loc[i,:].Press)
Temp = df.loc[i,:].Temp;       npH = df.loc[i,:].pH
# multiplication was done to convert the concentrations from mmol/kg and umol/kg to mol/kg
nCl = df.loc[i,:].Cl*1e-3;     nNa = df.loc[i,:].Na*1e-3
nH2S = df.loc[i,:].XH2S*1e-3;  nFe = df.loc[i,:].Fe*1e-6
nMn = df.loc[i,:].Mn*1e-6;     nH2 = df.loc[i,:].H2*1e-6
nK = df.loc[i,:].K*1e-3;       nCu = df.loc[i,:].Cu*1e-6;    
nCa = df.loc[i,:].Ca*1e-3;     nSi = df.loc[i,:].Si*1e-3

write_database(T = [20, Temp], P = P, solid_solution = True, objdb = './data0', 
               clay_thermo = 'Yes', dataset = 'eq36',  sourcedb =  './database/data0.geo') #
Success, your new EQ3/6 database is ready for download
<pygcc.pygcc_utils.write_database at 0x7fc783f2efd0>

Copy the generated database to the EQ3/6 work environment specified above and run through eqpt for preprocessing

copyfile(current_dir + '/output/EQ36/data0.geo', eq_dir + '/data0.pym')
File copied successfully.
# change the working directory to EQ3/6 work environment
os.chdir(eq_dir)
# load EQ3/6 environment
os.environ["EQ36CO"] = "C:\Eq3_6v8.0a\Bin"
os.environ["EQ36DA"] = eq_dir
# check the "eqptout.txt" for the output of the database preprocessing
f = open(current_dir + "/%s/eqptout.txt" % output_folder, "w")
subprocess.run([r'C:/Eq3_6v8.0a/bin/runeqpt', 'pym'], stdout = f)
f.close()
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
/tmp/ipykernel_268/326673575.py in <module>
      1 # check the "eqptout.txt" for the output of the database preprocessing
      2 f = open(current_dir + "/%s/eqptout.txt" % output_folder, "w")
----> 3 subprocess.run([r'C:/Eq3_6v8.0a/bin/runeqpt', 'pym'], stdout = f)
      4 f.close()

~/.pyenv/versions/3.7.9/lib/python3.7/subprocess.py in run(input, capture_output, timeout, check, *popenargs, **kwargs)
    486         kwargs['stderr'] = PIPE
    487 
--> 488     with Popen(*popenargs, **kwargs) as process:
    489         try:
    490             stdout, stderr = process.communicate(input, timeout=timeout)

~/.pyenv/versions/3.7.9/lib/python3.7/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors, text)
    798                                 c2pread, c2pwrite,
    799                                 errread, errwrite,
--> 800                                 restore_signals, start_new_session)
    801         except:
    802             # Cleanup if the child failed starting.

~/.pyenv/versions/3.7.9/lib/python3.7/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1549                         if errno_num == errno.ENOENT:
   1550                             err_msg += ': ' + repr(err_filename)
-> 1551                     raise child_exception_type(errno_num, err_msg, err_filename)
   1552                 raise child_exception_type(err_msg)
   1553 

FileNotFoundError: [Errno 2] No such file or directory: 'C:/Eq3_6v8.0a/bin/runeqpt': 'C:/Eq3_6v8.0a/bin/runeqpt'

Replace species and concentration in an existing eq3 file

# change the working directory back to current work environment to do some calculations and build input files
os.chdir(current_dir)
# Switch basis between HS- and SO4--
lst = ['|Special Basis Switches (for model definition only)       | (nsbswt)           |\n',  
       '|------------------------------------------------------------------------------|\n',
       '|Replace |SO4--                                           | (usbsw(1,n))       |\n',
       '|   with |HS-                                             | (usbsw(2,n))       |\n',
       '|------------------------------------------------------------------------------|\n']
lst
['|Special Basis Switches (for model definition only)       | (nsbswt)           |\n',
 '|------------------------------------------------------------------------------|\n',
 '|Replace |SO4--                                           | (usbsw(1,n))       |\n',
 '|   with |HS-                                             | (usbsw(2,n))       |\n',
 '|------------------------------------------------------------------------------|\n']
# Set H2(aq) as the redox constraint
lst2 = ['|Default redox constraint (irdxc3):                                            |\n',
        '|  [ ] (-3) Use O2(g) line in the aqueous basis species block                  |\n',
        '|  [ ] (-2) pe (pe units)      | 0.00000E+00| (pei)                            |\n',
        '|  [ ] (-1) Eh (volts)         | 5.00000E-01| (ehi)                            |\n',
        '|  [ ] ( 0) Log fO2 (log bars) |-7.00000E-01| (fo2lgi)                         |\n',
        '|  [x] ( 1) Couple (aux. sp.)  |H2(aq)                  | (uredox)             |\n',
        '|------------------------------------------------------------------------------|\n']
lst2
['|Default redox constraint (irdxc3):                                            |\n',
 '|  [ ] (-3) Use O2(g) line in the aqueous basis species block                  |\n',
 '|  [ ] (-2) pe (pe units)      | 0.00000E+00| (pei)                            |\n',
 '|  [ ] (-1) Eh (volts)         | 5.00000E-01| (ehi)                            |\n',
 '|  [ ] ( 0) Log fO2 (log bars) |-7.00000E-01| (fo2lgi)                         |\n',
 '|  [x] ( 1) Couple (aux. sp.)  |H2(aq)                  | (uredox)             |\n',
 '|------------------------------------------------------------------------------|\n']
ion_lst = ['Fe++', 'Na+', 'H+', 'Cl-', 'HS-', 'Mn++', 'K+', 'Ca++', 'SiO2(aq)', 'Cu++', 'H2(aq)'] # , 'H2S(aq)'
conc_lst = [nFe, nNa, npH, nCl, nH2S, nMn, nK, nCa, nSi, nCu, nH2]

lst3 = ['|Aqueous Basis Species/Constraint Species        |Conc., etc. |Units/Constraint|\n',
        '| (uspeci(n)/ucospi(n))                          | (covali(n))|(ujf3(jflgi(n)))|\n',
        '|------------------------------------------------------------------------------|\n']
for i, j in zip(ion_lst, conc_lst):
    units = 'Molality        ' if i != 'H+' else 'pH              '
    lst3.append('|%-10s                                      | %.5e|%s|\n' % (i, j, units))
lst3.append('|------------------------------------------------------------------------------|\n')
lst3
['|Aqueous Basis Species/Constraint Species        |Conc., etc. |Units/Constraint|\n',
 '| (uspeci(n)/ucospi(n))                          | (covali(n))|(ujf3(jflgi(n)))|\n',
 '|------------------------------------------------------------------------------|\n',
 '|Fe++                                            | 1.53000e-03|Molality        |\n',
 '|Na+                                             | 4.39000e-01|Molality        |\n',
 '|H+                                              | 3.40000e+00|pH              |\n',
 '|Cl-                                             | 5.00000e-01|Molality        |\n',
 '|HS-                                             | 7.60000e-03|Molality        |\n',
 '|Mn++                                            | 1.02400e-03|Molality        |\n',
 '|K+                                              | 2.35000e-02|Molality        |\n',
 '|Ca++                                            | 1.56000e-02|Molality        |\n',
 '|SiO2(aq)                                        | 1.76000e-02|Molality        |\n',
 '|Cu++                                            | 1.00000e-13|Molality        |\n',
 '|H2(aq)                                          | 1.00000e-08|Molality        |\n',
 '|------------------------------------------------------------------------------|\n']
# open an existing sample file and a new in put file
fin = open("swmaj_ex.3i", "r")
fout = open("./%s/swmaj.3i" % output_folder, "w")

# read and paste line by line until the beginning of the "Special Basis Switches" block
for i in range(1000):#
    s = fin.readline()
    if s.strip('|').startswith("Special Basis Switches"):
        break
    fout.writelines(s)
# paste the new "Special Basis Switches" block
fout.writelines(lst)
# read and skip pasting until the end of the "Special Basis Switches" block
for i in range(1000):#
    s = fin.readline()
    if s.strip('|').startswith("Temperature (C)"):
        break
fout.writelines(s)

# read and paste line by line until the beginning of the "Default redox constraint" block
for i in range(1000):#
    s = fin.readline()
    if s.strip('|').startswith("Default redox constraint"):
        break
    fout.writelines(s)
# paste the new "Special Basis Switches" block
fout.writelines(lst2)
# read and skip pasting until the end of the "Default redox constraint" block and 
# the beginning of the "Aqueous Basis Species" block
for i in range(1000):#
    s = fin.readline()
    if s.strip('|').startswith("Aqueous Basis Species/Constraint Species"):
        break

# paste the new "Aqueous Basis Species" block
fout.writelines(lst3)
# read and skip pasting until the end of the "Aqueous Basis Species" block
for i in range(1000):#
    s = fin.readline()
    if s.strip('* ').startswith("Valid jflag strings"):
        break
fout.writelines(s)

# read and paste the rest of the files
for i in range(1000):#
    s = fin.readline()
    fout.writelines(s)

fout.close()
fin.close()

Copy the new eq3 input file to the EQ3/6 work environment and run eq3

copyfile(current_dir + '/%s/swmaj.3i' % output_folder, eq_dir + '/swmaj.3i')
File copied successfully.
# change the working directory to EQ3/6 work environment
os.chdir(eq_dir)

# check the "eq3out.txt" for the eq3 run details
f = open(current_dir + "/%s/eq3out.txt" % output_folder, "w")
subprocess.run([r'C:\Eq3_6v8.0a/bin/runeq3', 'pym',  'swmaj.3i'], stdout = f)
f.close()

Generate a new eq6 file to run speciation and warm up temperature

# change the working directory back to current work environment to build input files
os.chdir(current_dir)
Descript = 'Heat ventfluid from 25C to %sC' % Temp
# open an existing sample file, a new input file and the copied eq3 output file
fin = open("swmaj_ex.6i", "r")
fout = open("./%s/swmaj.6i" % output_folder, "w")
f3p = open(eq_dir + "/swmaj.3p", "r")
# read and paste line by line until the beginning of the first "Linear tracking in Xi" in the "Temperature option" block
for i in range(1000):#
    s = fin.readline()
    if s.strip('|  ').startswith("[x] ( 1) Linear tracking in Xi"):
        break
    if s.strip('|').startswith('EQ6 input file name'):
        line = '%s= %-11s                                             |\n' % (s.split('=')[0], 
                                                                              'swmaj.6i')
        fout.writelines(line)
    elif s.strip('|').startswith('Description'):
        line = '%s= %-45s                    |\n' % (s.split('=')[0], Descript)
        fout.writelines(line)
    else:
        fout.writelines(s)

#create the new "Linear tracking in Xi" subblock and paste the new subblock
derivative = (Temp - 25)/1
lst = ['|  [x] ( 1) Linear tracking in Xi:                                             |\n',
       '|             Base Value (C)    | 2.50000E+01| (tempcb)                        |\n',
       '|             Derivative        | %.5e| (ttk(1))                        |\n' % derivative]
fout.writelines(lst)

# read and skip pasting until the end of the "Linear tracking in Xi" subblock
for i in range(1000):#
    s = fin.readline()
    if s.strip('|').lstrip().startswith("Derivative"):
        break

# read and paste line by line until the beginning of the "eq3 output" input
for i in range(10000):#
    s = fin.readline()
    if s.strip('* ').startswith("Start of the bottom half of the INPUT file"):
        break
    fout.writelines(s)

# paste the new eq3 output
for i in range(1000):#
    s = f3p.readline()
    fout.writelines(s)
fout.close()
fin.close()
f3p.close()

Copy the new eq6 input file to the EQ3/6 work environment and run eq6

copyfile(current_dir + '/%s/swmaj.6i' % output_folder, eq_dir + '/swmaj.6i')
File copied successfully.
# change the working directory to EQ3/6 work environment
os.chdir(eq_dir)

# check the "eq6out.txt" for the eq6 run details
f = open(current_dir + "/%s/eq6out.txt" % output_folder, "w")
subprocess.run([r'C:\Eq3_6v8.0a/bin/runeq6', 'pym', 'swmaj.6i'], stdout = f)
f.close()

Results processing

data = {}
rerun = []
path = eq_dir + '/'
files = os.listdir(path)
for i, f in enumerate(files):
    if f.endswith('.6o'): 
        size = os.path.getsize(path + f)/(1024) #size in KB
        if size > 2:
            data[f[:-3]] = read_eq36output(path + f)
        else:
            rerun.append(f)
print(rerun)
[]
data_df = data['swmaj']
# data_df
data_df.columns
Index(['Xi', 'pH', 'Temperature_C', 'Pressure_bars', 'Water_mass_g',
       'Rock_mass_g', 'M_Ca++', 'M_CaCl+', 'M_CaCl2(aq)', 'M_CaHSiO3+',
       'M_CaOH+', 'M_CaSO4(aq)', 'M_Cl-', 'M_Cu+', 'M_CuCl(aq)', 'M_CuCl2-',
       'M_CuCl3--', 'M_Fe++', 'M_Fe+++', 'M_FeCl+', 'M_FeCl++', 'M_FeCl2(aq)',
       'M_FeO(aq)', 'M_FeO+', 'M_FeO2-', 'M_FeOH+', 'M_FeOH++', 'M_H+',
       'M_H2(aq)', 'M_H2S(aq)', 'M_H2S2O3(aq)', 'M_HFeO2(aq)', 'M_HFeO2-',
       'M_HS-', 'M_HS2O3-', 'M_HSO3-', 'M_HSO4-', 'M_HSiO3-', 'M_K+',
       'M_KCl(aq)', 'M_KHSO4(aq)', 'M_KOH(aq)', 'M_KSO4-', 'M_Mn++', 'M_MnCl+',
       'M_MnO(aq)', 'M_MnOH+', 'M_MnSO4(aq)', 'M_Na+', 'M_NaCl(aq)',
       'M_NaHSiO3(aq)', 'M_NaOH(aq)', 'M_NaSO4-', 'M_OH-', 'M_S2--',
       'M_S2O3--', 'M_S3--', 'M_S4--', 'M_S5--', 'M_SO2(aq)', 'M_SO4--',
       'M_SiO2(aq)', 'TotM_Ca++', 'TotM_Cl-', 'TotM_Cu++', 'TotM_Fe++',
       'TotM_H+', 'TotM_H2O', 'TotM_HS-', 'TotM_K+', 'TotM_Mn++', 'TotM_Na+',
       'TotM_O2(g)', 'TotM_SiO2(aq)', 'SI_Alabandite', 'SI_Anhydrite',
       'SI_Bornite', 'SI_Chalcedony', 'SI_Chalcocite', 'SI_Chalcopyrite',
       'SI_Coesite', 'SI_Covellite', 'SI_Cristobalite(beta)', 'SI_Fayalite',
       'SI_FeO', 'SI_Ferrosilite', 'SI_Halite', 'SI_Hematite', 'SI_Magnetite',
       'SI_Minnesotaite', 'SI_Pyrite', 'SI_Pyrrhotite', 'SI_Quartz',
       'SI_SiO2(am)', 'SI_Sylvite'],
      dtype='object')

Plot pyrite saturation with Temperature

import matplotlib.pyplot as plt
y = data_df.SI_Pyrite
x = data_df.Temperature_C
plt.figure()
plt.scatter(x, y)
plt.plot([0, 350], [0, 0], 'k--')
plt.xlim([0, 350])
plt.xlabel('Temperature [C]')
plt.ylabel('$Log_{10}$(Q/K)$_{Pyrite}$')
Text(0, 0.5, '$Log_{10}$(Q/K)$_{Pyrite}$')
_images/77f5c11b3534ba0bba812e55ad0bf3de38cb82fbe447c0f4571930508bd1d959.png

plot pH with Temperature

y = data_df.pH
x = data_df.Temperature_C
plt.figure()
sc = plt.scatter(x, y)
plt.xlim([0, 350])
plt.xlabel('Temperature [C]')
plt.ylabel('pH')
Text(0, 0.5, 'pH')
_images/70eb527ddb97f59ab0b23864f2ceddc03efc98fc24ac7a6e9e308b2802da6bfb.png