Charles Le Losq Friday, 22 May 2015 Modified the 16 June 2015.
Geophysical Laboratory, Carnegie Institution for Science
Example of use of pysolex, the library using the software SolEx developped by Fred Witham, University of Bristol.
From the header of the solex.cpp:
"An object that calculates the solubility relationships for basalt given basalt compositional parameters and PT conditions."
Difference with the SolEx GUI software:
Let's start and call the useful libraries:
In [1]:
import numpy as np #for handling the numbers/arrays
import scipy #for a lot of scientific stuffs, optimisation, interpolation functions, etc.
import pysolex #we import the pysolex library!
import ctypes #for reading the SWIG array output
import matplotlib #for doing nice graphs
import matplotlib.pyplot as plt # For doing the plots
from pylab import * #for doing nice graphs
# We need this in this case because we use IPython notebooks, but not needed in a .py code
%matplotlib inline
Ok, now we start by defining a starting chemical composition of interest (warning to put dots so that Python interpretes the numbers as float and not int!):
In [2]:
CO2 = 4890.
H2O = 3.67
PPMS0 = 3560.
PPMCl0 = 1572.
Si = 52.12
Al = 16.38
Fe = 5.82
Ca = 10.72
Mg = 6.71
Na = 2.47
K = 1.89
Now we define the value of pi to use, which is the parameterisation described in Dixon(1997). This value is not used unless piswitch is set to 1.
In [3]:
pi = -0.05341
Below is the wt% of SiO2 that was used for the SiO2 only parameterisation. From Fred: "I think (I should check but dont have the code available now) that this is no-longer used as the SiO2 wt% is calculated from Si mol%."
In [4]:
SiO2 = 52.12 #should match the good value
Now the pisol switch: if 1 solubility is based on the value of pi, if 0 it is used on SiO2 wt% only.
In [5]:
pisol = 0
Now a second switch to determine if pisol is given or should be calculated: if 1 then the value pi is used for solubility calculations, and if 0 pi is calculated from the composition of the melt.
In [6]:
piswitch = 0
Now let's fix the other parameters of our system: temperature, pressure, and oxygen fugacity! We will start with a fixed oxygen fugacity, pressure and temperature:
In [7]:
T = 1153.; #in K
P = 100.; #in bars
NNO = 1.8;
Ok, for this single calculation, SolEx has a flag for terminal output, but it is not working in the Notebook. So let's put the flag to 0:
In [8]:
flagout = bool(0) #has to be a bool value
And the function to call is pysolex.pyex:
In [9]:
output = pysolex.pyex(H2O,CO2,PPMS0,PPMCl0,Si,Al,Fe,Ca,Mg,Na,K,pi,SiO2,pisol,piswitch,flagout,T,P,NNO)
output
Out[9]:
Oh... Here is the result contained in output => a SWIG Object containing double number... To read it, I only found one way online: reading directly the memory block allocated to this Ojbect using ctypes:
In [10]:
rawPointer = output.__long__() # we're going to read the "address"
pC = ctypes.cast(rawPointer, ctypes.POINTER( ctypes.c_double )) # and we read the array stored at this address
print(("wt% H2O = "+str(pC[0])))
print(("PPM CO2 = "+str(pC[1])))
print(("PPM S = "+str(pC[2])))
print(("PPM Cl = "+str(pC[3])))
print(("Vol% Exsolve = "+str(pC[4])))
print(("XV H2O (mass) = "+str(pC[5])))
print(("XV CO2 (mass) = "+str(pC[6])))
print(("XV S (mass) = "+str(pC[7])))
print(("XV Cl (mass) = "+str(pC[8])))
print(("molV H2O (mass) = "+str(pC[9])))
print(("molV CO2 (mass) = "+str(pC[10])))
print(("molV S (mass) = "+str(pC[11])))
print(("molV Cl (mass) = "+str(pC[12])))
Ok, it's working. Now let's complicate the case. Let's imagine that we have a closed-system degassing, going from P = 4000 to 100 bar, as you can do in SolEx. You will write something like that to reproduce the calculation in Python:
In [11]:
Pint = np.arange(100,4000,100) #start, stop, step
rev_Pint = Pint[::-1] # To have the first values being the highest ones
results = np.zeros((len(Pint),13)) # For storing the results
We create a loop in which we will call pysolex for doing the calculation:
In [12]:
for i in range(len(rev_Pint)):
output = pysolex.pyex(H2O,CO2,PPMS0,PPMCl0,Si,Al,Fe,Ca,Mg,Na,K,pi,SiO2,pisol,piswitch,flagout,T,rev_Pint[i],NNO)
rawPointer = output.__long__()
pC = ctypes.cast(rawPointer, ctypes.POINTER( ctypes.c_double ))
results[i,0] = pC[0] #wt% water
results[i,1] = pC[1] #co2 ppm
results[i,2] = pC[2] #S ppm
results[i,3] = pC[3] #Cl ppm
results[i,4] = pC[4] #EXSOLVE
results[i,5] = pC[5] #XV H2O
results[i,6] = pC[6] #XV CO2
results[i,7] = pC[7] #XV S
results[i,8] = pC[8] #XV Cl
results[i,9] = pC[9] #molV H2O
results[i,10] = pC[10] #molV CO2
results[i,11] = pC[11] #molV S
results[i,12] = pC[12] #molV Cl
Done! Let's do a nice graph for those results:
In [13]:
plt.plot(rev_Pint[:],results[:,0])
plt.xlabel("Pressure, bars", fontsize = 14)
plt.ylabel("Water content in melt, wt%", fontsize = 14)
plt.title("Fig. 1: Water concentration vs pressure, closed system",fontsize = 14,fontweight = "bold")
plt.text(2000,2,("T ="+str(T)+"\nNNO ="+str(NNO)),fontsize = 14)
Out[13]:
Let's do the same thing for the CO2 now:
In [14]:
plt.plot(rev_Pint[:],results[:,1])
plt.xlabel("Pressure, bars", fontsize = 14)
plt.ylabel("CO$_2$ content in melt, ppm", fontsize = 14)
plt.title("Fig. 2: CO$_2$ concentration vs pressure, closed system",fontsize = 14,fontweight = "bold")
plt.text(1000,800,("T ="+str(T)+"\nNNO ="+str(NNO)),fontsize = 14)
Out[14]:
Now let's make the case of an open system. Easy, we will just take the H2O, CO2, S and Cl values from the past output to input them in the next...
In [15]:
for i in range(len(rev_Pint)):
if i == 0:
output = pysolex.pyex(H2O,CO2,PPMS0,PPMCl0,Si,Al,Fe,Ca,Mg,Na,K,pi,SiO2,pisol,piswitch,flagout,T,rev_Pint[i],NNO)
else:
H2O = results[i-1,0]
CO2 = results[i-1,1]
PPMS = results[i-1,2]
PPMCl = results[i-1,3]
output = pysolex.pyex(H2O,CO2,PPMS,PPMCl,Si,Al,Fe,Ca,Mg,Na,K,pi,SiO2,pisol,piswitch,flagout,T,rev_Pint[i],NNO)
rawPointer = output.__long__()
pC = ctypes.cast(rawPointer, ctypes.POINTER( ctypes.c_double ))
results[i,0] = pC[0] #wt% water
results[i,1] = pC[1] #co2 ppm
results[i,2] = pC[2] #S ppm
results[i,3] = pC[3] #Cl ppm
results[i,4] = pC[4] #EXSOLVE
results[i,5] = pC[5] #XV H2O
results[i,6] = pC[6] #XV CO2
results[i,7] = pC[7] #XV S
results[i,8] = pC[8] #XV Cl
results[i,9] = pC[9] #molV H2O
results[i,10] = pC[10] #molV CO2
results[i,11] = pC[11] #molV S
results[i,12] = pC[12] #molV Cl
We can now plot the results as we did for the closed system case:
In [16]:
plt.plot(rev_Pint[:],results[:,0])
plt.xlabel("Pressure, bars", fontsize = 14)
plt.ylabel("Water content in melt, wt%", fontsize = 14)
plt.title("Fig. 3: Open system ",fontsize = 14,fontweight = "bold")
plt.text(2000,2,("T ="+str(T)+"\nNNO ="+str(NNO)))
Out[16]:
In [17]:
plt.plot(rev_Pint[:],results[:,1])
plt.xlabel("Pressure, bars", fontsize = 14)
plt.ylabel("CO$_2$ content in melt, ppm", fontsize = 14)
plt.title("Fig. 4: Open system ",fontsize = 14,fontweight = "bold")
#plt.text(1000,800,("T ="+str(T)+"\nNNO ="+str(NNO)))
Out[17]:
In [18]:
results[0,0]
Out[18]:
In [19]:
rev_Pint[0]
Out[19]:
In [ ]:
In [ ]: