ENKI

Feldspar Solid Solution - Berman Compatible

Required Python code to load the phase library.

from thermoengine import core, phases, model

Listing of solution phases compatible with Berman inherited from the MELTS model

get_phase_info returns an ordered list of pure and solution phases

phase_info,info_files = phases.get_phase_info()
phase_info['solution']
Abbrev Name Members
0 AkiS Akimotoite Solution MgAki/FeAki/AlAki
1 Bt Biotite Ann/Phl
2 CfS Ca-Ferrite Solution MgCf/FeCf/NaCf
3 CalS Calcite Solution Cal/Mgs
4 Cam Clinoamphibole Cum/Gru/Tr
5 Fp Ferropericlase Per/Wus
6 Mws Magnesiowustite Per/Wus
7 Fsp Feldspar Ab/An/Kfs/Or/Mc/Sa
8 Grt Garnet Alm/Grs/Prp/Maj/NaMaj
9 Hbl Hornblende Fprg/Mhs/Prg
10 KlsS Kalsilite Solution Nph/Kls
11 LctS Leucite Solution Anl/Lct/nLct
12 Liq Liquid NaN
13 Mll Melilite Ak/Gh/fAk/nMll
14 NphS Nepheline Solution Nph/Kls
15 Ol Olivine CoOl/Fa/Fo/Mtc/NiOl/Tep
16 OrOx OrthoOxide Fpsb/Krt/Psb
17 Oam Orthoamphibole Cum/Gru/Tr
18 Opx Orthopyroxene Abfn/Bfn/CaTs/En/Fs/cEn/Di/Ess/Hd/Jd/oDi
19 Cpx Clinopyroxene Abfn/Bfn/CaTs/En/Fs/cEn/Di/Ess/Hd/Jd
20 hpCpx High-Pressure Clinopyroxene hpcEn/hpcFs
21 PrvS Perovskite Prv/FePrv/AlPrv
22 PpvS Post-Perovskite MgPpv/FePpv/AlPpv
23 Rhom Rhombohedral Crn/Gk/Hem/Ilm/Pph
24 Rwd Ringwoodite MgRwd/FeRwd
25 SplS Spinel Chr/Hc/Mag/Spl/Usp
26 Wds Wadsleyite MgWds/FeWds
27 Psu PseudoPhase NaN

Get access to a thermodynamic database (by default, the Berman (1988)/MELTS database).

modelDB = model.Database()

Create a Python reference to the feldspar solution phase class.

The required phase abbreviation method argument is listed above.

Feldspar = modelDB.get_phase('Fsp')

Obtain some properties of the selected feldspar solution

Name, formulas of endmembers, molecular weights of endmembers, abbreviation, number of endmember components, names of endmembers

print (Feldspar.props['phase_name'])
print (Feldspar.props['formula'])
print (Feldspar.props['molwt'])
print (Feldspar.props['abbrev'])
print (Feldspar.props['endmember_num'])
print (Feldspar.props['endmember_name'])
Feldspar
['NaAlSi3O8' 'CaAl2Si2O8' 'KAlSi3O8']
[262.22301 278.20928 278.33524]
Fsp
3
['albite' 'anorthite' 'sanidine']

Specify the composition of the solution

Composition input in wt% oxides (treated as grams of oxides)

Sandine composition in wt% oxides, taken from Deer, Howie and Zussman, Table 30, entry 9: Sanidine from a rhyolite in Mitchell Mesa, Texas.

mol_oxides = core.chem.format_mol_oxide_comp({'SiO2':67.27,'Al2O3':18.35, 'FeO':0.92, 'CaO':0.15,
                                              'Na2O':6.45, 'K2O':7.05, 'H2O':0.16}, convert_grams_to_moles=True)

Convert analytical composition to moles of endmember components

Note that a method is called - test_endmember_comp() - to test the validity of the projected composition #### (1st method) A default projection method based on generic least sqaures …

moles_end,oxide_res = Feldspar.calc_endmember_comp(mol_oxide_comp=mol_oxides, output_residual=True)
for i in range(0,Feldspar.props['endmember_num']):
    print ("mole number of {0:10.10s} = {1:13.6e}".format(Feldspar.props['endmember_name'][i], moles_end[i]))
if not Feldspar.test_endmember_comp(moles_end):
    print ("Calculated composition is infeasible!")
mole number of albite     =  2.156163e-01
mole number of anorthite  = -2.161623e-04
mole number of sanidine   =  1.571699e-01
Calculated composition is infeasible!
moles_end,oxide_res = Feldspar.calc_endmember_comp(mol_oxide_comp=mol_oxides, method='intrinsic', output_residual=True)
for i in range(0,Feldspar.props['endmember_num']):
    print ("mole number of {0:10.10s} = {1:13.6e}".format(Feldspar.props['endmember_name'][i], moles_end[i]))
if not Feldspar.test_endmember_comp(moles_end):
    print ("Calculated composition is infeasible!")
mole number of albite     =  2.081352e-01
mole number of anorthite  =  2.674779e-03
mole number of sanidine   =  1.496888e-01

Some convenience methods to manipulate composition information

➜ Moles of endmember components => Molar sum
➜ Moles of endmember components => Moles of elements (standard order)
➜ Moles of endmember components => Mole fractions of endmember components
➜ Moles of elements (standard order) => Moles of endmember components of the phase
➜ Moles of elements (standard order) => Total moles of endmember components of the phase
➜ Moles of elements (standard order) => Total mass of the phase (g)
print ('Total moles of endmembers: ', Feldspar.covert_endmember_comp(moles_end,output='total_moles'))
mol_elm = Feldspar.covert_endmember_comp(moles_end,output='moles_elements')
print ('Mole fractions of endmembers: ', Feldspar.covert_endmember_comp(moles_end,output='mole_fraction'))
print ('Moles of endmembers: ', Feldspar.convert_elements(mol_elm, output='moles_end'))
print ('Total moles of endmembers: ', Feldspar.convert_elements(mol_elm, output='total_moles'))
print ('Total grams of phase: ', Feldspar.convert_elements(mol_elm, output='total_grams'))
Total moles of endmembers:  0.36049883224427715
Mole fractions of endmembers:  [0.57735337 0.00741966 0.41522697]
Moles of endmembers:  [0.20813521 0.00267478 0.14968884]
Total moles of endmembers:  0.36049883224427715
Total grams of phase:  96.98566962270826

Compute activities and chemical potentials of endmember components:

➜ Moles of components, T (K), P (bars) => activities of endmember components
➜ Moles of components, T (K), P (bars) => chemical potentials of endmember components (J)
t = 1000
p = 1000
names = Feldspar.props['endmember_name']
forms = Feldspar.props['formula']
for i in range(0,Feldspar.props['endmember_num']):
    print ("chemical potential of {0:15.15s} ({1:15.15s}) = {2:15.2f} J, activity = {3:10.6f}".format(
        names[i], forms[i], Feldspar.chem_potential(t, p, mol=moles_end, endmember=i),
        Feldspar.activity(t, p, mol=moles_end, endmember=i)))
chemical potential of albite          (NaAlSi3O8      ) =     -4265565.12 J, activity =   0.842485
chemical potential of anorthite       (CaAl2Si2O8     ) =     -4575045.33 J, activity =   0.053515
chemical potential of sanidine        (KAlSi3O8       ) =     -4305773.90 J, activity =   0.646776

Molar derivatives of activities:

➜ Moles of components, T (K), P (bars) => d(activities of endmember components)/d(Moles of components)

for i in range(0,Feldspar.props['endmember_num']):
    dadm = Feldspar.activity(t,p,mol=moles_end,deriv={'dmol':1},endmember=i)[0]
    print ("Derivative of a[{0:s}] with respect to:".format(names[i]))
    for j in range(0,Feldspar.props['endmember_num']):
        print ("    {0:15.15s} = {1:13.6e}".format(names[j], dadm[j]))
Derivative of a[albite] with respect to:
    albite          =  2.787764e-01
    anorthite       = -7.297958e+00
    sanidine        = -2.572186e-01
Derivative of a[anorthite] with respect to:
    albite          = -4.635673e-01
    anorthite       =  1.932289e+01
    sanidine        =  2.992890e-01
Derivative of a[sanidine] with respect to:
    albite          = -1.974669e-01
    anorthite       =  3.617189e+00
    sanidine        =  2.099331e-01

Gibbs free energy and its compositional derivatives:

➜ Moles of components, T (K), P (bars) => Gibbs free energy (J)
➜ Moles of components, T (K), P (bars) => d(Gibbs free energy)/d(Moles of components) (J)
➜ Moles of components, T (K), P (bars) => d^2(Gibbs free energy)/d(Moles of components)^2 (J)
➜ Moles of components, T (K), P (bars) => d^3(Gibbs free energy)/d(Moles of components)^3 (J)
print ('Gibbs free energy = {0:12.2f} J'.format(Feldspar.gibbs_energy(t, p, mol=moles_end)))
print ("")
dgdm = Feldspar.gibbs_energy(t, p, mol=moles_end, deriv={'dmol':1})[0]
d2gdm2 = Feldspar.gibbs_energy(t, p, mol=moles_end, deriv={'dmol':2})[0]
for i in range (0, Feldspar.props['endmember_num']):
    print ('dg/dm[{0:>2d}] = {1:13.6e}     d2gdm2[{0:>2d}][*]:  '.format(i, dgdm[i], i), end=' ')
    for j in range (0, Feldspar.props['endmember_num']):
        print ('{0:13.6e}'.format(d2gdm2[i, j]), end=' ')
    print()
print ("")
d3gdm3 = Feldspar.gibbs_energy(t, p, mol=moles_end, deriv={'dmol':3})[0]
for i in range (0, Feldspar.props['endmember_num']):
    print('d3gdm3[{0:>2d}][*][*]: '.format(i), end=' ')
    for j in range (0, Feldspar.props['endmember_num']):
        for k in range (0, Feldspar.props['endmember_num']):
            print ('{0:13.6e}'.format(d3gdm3[i, j, k]), end=' ')
        print ('  ', end=' ')
    print ()
Gibbs free energy =  -1544577.84 J

dg/dm[ 0] = -4.265565e+06     d2gdm2[ 0][*]:    2.751184e+03 -7.202198e+04 -2.538435e+03
dg/dm[ 1] = -4.575045e+06     d2gdm2[ 1][*]:   -7.202198e+04  3.002095e+06  4.649893e+04
dg/dm[ 2] = -4.305774e+06     d2gdm2[ 2][*]:   -2.538435e+03  4.649893e+04  2.698688e+03

d3gdm3[ 0][*][*]:  -3.714742e+03  1.866043e+05 -1.613352e+03     5.612260e+04  4.781953e+05  5.992005e+04     7.182150e+02 -1.215085e+05  3.343639e+03
d3gdm3[ 1][*][*]:   3.042045e+05  8.211941e+05  2.308674e+04     6.907124e+05 -1.160760e+09  1.827003e+05     2.541830e+04  1.271789e+03 -3.258032e+05
d3gdm3[ 2][*][*]:  -3.714742e+03  1.866043e+05 -1.613352e+03     5.612260e+04  4.781953e+05  5.992005e+04     7.182150e+02 -1.215085e+05  3.343639e+03

Enthalpy, Entropy, and molar derivatives:

➜ Moles of components, T (K), P (bars) => enthalpy (J)
➜ Moles of components, T (K), P (bars) => entropy (J/K)
➜ Moles of components, T (K), P (bars) => d(entropy)/d(Moles of components) (J/K)
➜ Moles of components, T (K), P (bars) => d^2(entropy)/d(Moles of components)^2 (J/K)
print ('Entropy = {0:12.2f} J/K'.format(Feldspar.entropy(t, p, mol=moles_end)))
print ("")
dsdm = Feldspar.entropy(t, p, mol=moles_end, deriv={'dmol':1})[0]
d2sdm2 = Feldspar.entropy(t, p, mol=moles_end, deriv={'dmol':2})[0]
for i in range (0, Feldspar.props['endmember_num']):
    print ('ds/dm[{0:>2d}] = {1:13.6e}     d2sdm2[{0:>2d}][*]:  '.format(i, dsdm[i], i), end=' ')
    for j in range (0, Feldspar.props['endmember_num']):
        print ('{0:13.6e}'.format(d2sdm2[i, j]), end=' ')
    print()
Entropy =       199.56 J/K

ds/dm[ 0] =  5.506230e+02     d2sdm2[ 0][*]:   -2.691158e+01  2.489871e+01  3.697437e+01
ds/dm[ 1] =  5.697965e+02     d2sdm2[ 1][*]:    2.489871e+01 -3.071644e+03  2.026651e+01
ds/dm[ 2] =  5.573604e+02     d2sdm2[ 2][*]:    3.697437e+01  2.026651e+01 -5.177324e+01

Heat capacity and its derivatives:

➜ Moles of components, T (K), P (bars) => isobaric heat capacity (J/K)
➜ Moles of components, T (K), P (bars) => d(isobaric heat capacity)/dT (J/K^2)
➜ Moles of components, T (K), P (bars) => d(isobaric heat capacity)/d(Moles of components) (J/K)
print ('{0:<10s}{1:13.6f} {2:<15s}'.format('Cp', Feldspar.heat_capacity(t, p, mol=moles_end), 'J/K'))
print ('{0:<10s}{1:13.6f} {2:<15s}'.format('dCp/dT', Feldspar.heat_capacity(t, p, mol=moles_end, deriv={'dT':1}), 'J/K^2'))
print ("")
dcpdm = Feldspar.heat_capacity(t, p, mol=moles_end, deriv={'dmol':1})[0]
for i in range (0, Feldspar.props['endmember_num']):
    print ('dCp/dm[{0:>2d}]    = {1:13.6e} J/K-m'.format(i, dcpdm[i]))
Cp           117.869612 J/K
dCp/dT        -0.069768 J/K^2

dCp/dm[ 0]    =  3.313154e+02 J/K-m
dCp/dm[ 1]    =  3.208859e+02 J/K-m
dCp/dm[ 2]    =  3.210186e+02 J/K-m

Volume and its derivatives:

➜ Moles of components, T (K), P (bars) => volume (J/bar)
➜ Moles of components, T (K), P (bars) => d(volume)/d(Moles of components) (J/bar)
➜ Moles of components, T (K), P (bars) => d^2(volume)/d(Moles of components)^2 (J/bar)
➜ Moles of components, T (K), P (bars) => d(volume)/dT (J/bar-K)
➜ Moles of components, T (K), P (bars) => d(volume)/dP (J/bar^2)
➜ Moles of components, T (K), P (bars) => d2(volume)/dT^2 (J/bar-K^2)
➜ Moles of components, T (K), P (bars) => d2(volume)/dTdP (J/bar^2-K)
➜ Moles of components, T (K), P (bars) => d2(volume)/dP^2 (J/bar^3)
➜ Moles of components, T (K), P (bars) => d2(volume)/d(Moles of components)dT (J/bar-K)
➜ Moles of components, T (K), P (bars) => d2(volume)/d(Moles of components)dP (J/bar^2)
print ('{0:<10s}{1:13.6f} {2:<15s}'.format('Volume', Feldspar.volume(t, p, mol=moles_end), 'J/bar'))
print ('{0:<10s}{1:13.6e} {2:<15s}'.format('dvdt', Feldspar.volume(t, p, mol=moles_end, deriv={'dT':1}), 'J/bar-K'))
print ('{0:<10s}{1:13.6e} {2:<15s}'.format('dvdp', Feldspar.volume(t, p, mol=moles_end, deriv={'dP':1}), 'J/bar^2'))
print ('{0:<10s}{1:13.6e} {2:<15s}'.format('d2vdt2', Feldspar.volume(t, p, mol=moles_end, deriv={'dT':2}), 'J/bar-K^2'))
print ('{0:<10s}{1:13.6e} {2:<15s}'.format('d2vdtdp', Feldspar.volume(t, p, mol=moles_end, deriv={'dT':1,'dP':1}),  'J/bar^2-K'))
print ('{0:<10s}{1:13.6e} {2:<15s}'.format('d2vdp2', Feldspar.volume(t, p, mol=moles_end, deriv={'dP':2}), 'J/bar^3'))
print ("")
dvdm = Feldspar.volume(t, p, mol=moles_end, deriv={'dmol':1})[0]
d2vdm2 = Feldspar.volume(t, p, mol=moles_end, deriv={'dmol':2})[0]
for i in range (0, Feldspar.props['endmember_num']):
    print ('dv/dm[{0:>2d}] = {1:13.6e} J/bar-m     d2vdm2[{0:>2d}][*]:  '.format(i, dvdm[i], i), end=' ')
    for j in range (0, Feldspar.props['endmember_num']):
        print ('{0:13.6e}'.format(d2vdm2[i, j]), end=' ')
    print('J/bar-m^2')
Volume         3.843501 J/bar
dvdt       1.194789e-04 J/bar-K
dvdp      -7.056455e-06 J/bar^2
d2vdt2    -2.283345e-07 J/bar-K^2
d2vdtdp    7.335540e-10 J/bar^2-K
d2vdp2     3.499319e-11 J/bar^3

dv/dm[ 0] =  1.031408e+01 J/bar-m     d2vdm2[ 0][*]:   -3.493432e-01  4.277645e-01  4.781015e-01 J/bar-m^2
dv/dm[ 1] =  9.782711e+00 J/bar-m     d2vdm2[ 1][*]:    4.277645e-01  3.692671e+00 -6.607703e-01 J/bar-m^2
dv/dm[ 2] =  1.116056e+01 J/bar-m     d2vdm2[ 2][*]:    4.781015e-01 -6.607703e-01 -6.529701e-01 J/bar-m^2

Accessing properties of solution species:

➜ Moles of components, T (K), P (bars) => formulae as an NSString object
➜ Retrieves the name of the solution species at the specified index
➜ Moles of solution species => moles of endmember components
➜ Retrieves an elemental stoichiometry vector for the species at the specified index
➜ Moles of components, T (K), P (bars) => chemical potentials of solution species (J)
import numpy as np
print ('Composition as a formula = ', Feldspar.compute_formula(t, p, moles_end))
mSpecies = np.zeros(Feldspar.props['species_num'])
for i in range (0, Feldspar.props['species_num']):
    print ('Species = {0:<15.15s}  elms:'.format(Feldspar.props['species_name'][i]), end=' ')
    elm = Feldspar.props['species_elms'][i]
    for j in range (0, 107):
        if elm[j] > 0.0:
            print ('[{0:>2.2s}] {1:5.2f}'.format(core.chem.PERIODIC_ORDER[j], elm[j]), end=' ')
    print ('   chemical potential = {0:12.2f} J/m'.format(Feldspar.chem_potential(t, p, mol=moles_end, endmember=i, species=True)))
    mSpecies[i] = float(i+1)
for i in range (0, Feldspar.props['species_num']):
    print ('moles of species   [{0:>2d}] = {1:5.2f}'.format(i, mSpecies[i]))
mSpToComp = Feldspar.convert_species_to_comp(mSpecies)
for i in range (0, Feldspar.props['endmember_num']):
    print ('moles of component [{0:>2d}] = {1:5.2f}'.format(i, mSpToComp[i]))
Composition as a formula =  K0.42Na0.58Ca0.01Al1.01Si2.99O8
Species = albite           elms: [ O]  8.00 [Na]  1.00 [Al]  1.00 [Si]  3.00    chemical potential =  -4265565.12 J/m
Species = anorthite        elms: [ O]  8.00 [Al]  2.00 [Si]  2.00 [Ca]  1.00    chemical potential =  -4575045.33 J/m
Species = sanidine         elms: [ O]  8.00 [Al]  1.00 [Si]  3.00 [ K]  1.00    chemical potential =  -4305773.90 J/m
moles of species   [ 0] =  1.00
moles of species   [ 1] =  2.00
moles of species   [ 2] =  3.00
moles of component [ 0] =  1.00
moles of component [ 1] =  2.00
moles of component [ 2] =  3.00

Solution model parameters

Accessing, modifying, functional derivatives

if Feldspar.param_props['supports_calib']:
    print ('This phase supports the Calibration protocol', end=' ')
    np = Feldspar.param_props['param_num']
    print ('and there are', np, 'parameters')
    for i in range (0, np):
        name = Feldspar.param_names[i]
        value = Feldspar.get_param_values(param_names=[name])[0]
        units = Feldspar.param_units(param_names=[name])[0]
        dgdw = Feldspar.gibbs_energy(t, p, mol=moles_end, deriv_param=[name])
        print ('Parameter {0:<10.10s} = {1:13.6e} {2:<10.10s} dggw = {3:13.6e}'.format(
            name, value, units, Feldspar.gibbs_energy(t, p, mol=moles_end, deriv_param=[name])))
        dmudw = Feldspar.gibbs_energy(t, p, mol=moles_end, deriv={'dmol':1}, deriv_param=[name])[0]
        print ('   dmu[*]dw:', end=' ')
        for j in range (0, Feldspar.props['endmember_num']):
            print ('{0:13.6e}'.format(dmudw[j]), end=' ')
        print ()
    Feldspar.set_param_values(param_names=['whabor','whorab'], param_values=['1.0', '2.0'])
    print ()
    print('Reset values of whabor and whorab: ', Feldspar.get_param_values(param_names=['whabor','whorab']))
else:
    print ('This phase does not implement the parameter calibration protocol')
This phase supports the Calibration protocol and there are 13 parameters
Parameter whabor     =  1.881000e+04 joules     dggw =  3.620592e-02
   dmu[*]dw: -2.691388e-02 -8.100080e-02  2.807416e-01
Parameter wsabor     =  1.030000e+01 joules     dggw = -3.620592e+01
   dmu[*]dw:  2.691141e+01  8.100121e+01 -2.807403e+02
Parameter wvabor     =  4.602000e-01 joules     dggw =  3.616972e+01
   dmu[*]dw: -2.689048e+01 -8.094307e+01  2.804487e+02
Parameter whorab     =  2.732000e+04 joules     dggw =  5.021743e-02
   dmu[*]dw:  2.024044e-01 -1.587322e-01  5.687912e-02
Parameter wsorab     =  1.030000e+01 joules     dggw = -5.021743e+01
   dmu[*]dw: -2.024090e+02  1.587318e+02 -5.688107e+01
Parameter wvorab     =  3.264000e-01 joules     dggw =  5.016721e+01
   dmu[*]dw:  2.022059e+02 -1.585478e+02  5.687040e+01
Parameter whaban     =  7.924000e+03 joules     dggw =  3.320741e-04
   dmu[*]dw: -2.523978e-04  1.265933e-01  2.997224e-04
Parameter whanab     =  0.000000e+00 joules     dggw =  1.212219e-03
   dmu[*]dw:  0.000000e+00  4.375000e-01  0.000000e+00
Parameter whoran     =  4.031700e+04 joules     dggw =  3.288566e-04
   dmu[*]dw: -2.852395e-04  1.242032e-01  3.720515e-04
Parameter whanor     =  3.897400e+04 joules     dggw =  7.817838e-04
   dmu[*]dw: -2.796736e-03  2.879420e-01  3.965785e-03
Parameter wvanor     = -1.037000e-01 joules     dggw =  7.810020e-01
   dmu[*]dw: -2.410800e+00  2.874879e+02  4.218901e+00
Parameter whabanor   =  1.254500e+04 joules     dggw =  6.412320e-04
   dmu[*]dw: -4.782782e-04  2.361748e-01  7.273814e-04
Parameter wvabanor   = -1.095000e+00 joules     dggw =  1.776956e+00
   dmu[*]dw: -4.566210e-01  2.359589e+02  7.420091e-01

Reset values of whabor and whorab:  [1. 2.]