Simon Matthews (University of Iceland) and Kevin Wong (University of Leeds)
pyMelt is a python package for calculating the melting behaviour of mantle comprising multiple lithologies. The module implements the melting equations developed by Phipps Morgan (2001) to calculate the melting behaviour of mantle comprising any chosen lithology.
Currently supported calculations:
Parameters that can be calculated:
See the github readme or the readthedocs documentation for installation instructions.
once pyMelt is installed, it can be imported:
import pyMelt as m
adding as m
to the end of the instruction allows us to use the shorthand m
when accessing the module.
We will also use the numpy
, pandas
, and matplotlib.pyplot
libraries in this tutorial:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
pyMelt offers a number of different lithologies that can be thermodynamically modelled either separately or in combination. pyMelt includes the new parameterisations for KLB-1, KG1, and silica-saturated pyroxenite of Matthews et al., 2021. The lithologies included in the module are:
m.lithologies.matthews.klb1
: KLB-1 lherzolite (Matthews et al., 2021)m.lithologies.matthews.kg1
: KG1 silica-undersaturated pyroxenite (Matthews et al., 2021)m.lithologies.matthews.eclogite
: silica-saturated pyroxenite (Matthews et al., 2021)m.lithologies.shorttle.kg1
: KG1 silica-undersaturated pyroxenite (Shorttle et al. 2014)m.lithologies.katz.lherzolite
: lherzolite (Katz et al., 2003)m.lithologies.pertermann.g2
: G2 pyroxenite (Pertermann & Hirschmann, 2002)m.lithologies.shorttle.harzburgite
: non-melting harzburgiteEach lithology is treated as a python object, and an instance can be assigned to a variable:
lz = m.lithologies.matthews.klb1()
px = m.lithologies.matthews.kg1()
hz = m.lithologies.shorttle.harzburgite()
Each lithology object contains methods describing its thermodynamic properties. Most of these methods are hidden and they vary from model to model, depending on how it is formulated. However, every lithology has the following methods:
TSolidus(P)
: temperature of the lithology solidus (°C) at a given pressure in GPaTLiquidus(P)
: temperature of the lithology liquidus (°C) at a given pressure in GPaF(P, T)
: melt fraction of the lithology at a given pressure (GPa) and temperature (°C)dTdF(P, T)
: dT/dF of the lithology at a constant pressuredTdP(P, T)
: dT/dP of the lithology at a constant melt fraction
These can be called to get the properties of the lithology at particular temperatures and pressures, for example:lz_solidus = lz.TSolidus(2.0)
lz_liquidus = lz.TLiquidus(2.0)
lz_F = lz.F(2.0, 1500.0)
print("At 2 GPa the solidus temperature is {:.1f}˚C \n and the liquidus temperature is: {:.1f}˚C. \nAt 1500˚C and 2 GPa the melt fraction is {:.2f}".format(lz_solidus, lz_liquidus, lz_F))
Throughout pyMelt the units of temperature are always ˚C, and the units of pressure are always GPa.
We could go one step further and make a plot of the solidus and liquidus temperature for one of the lithologies:
p = np.linspace(0.0,3.0,31)
tliq = np.zeros(np.shape(p))
tsol = np.zeros(np.shape(p))
for i in range(len(p)):
tliq[i] = lz.TLiquidus(p[i])
tsol[i] = lz.TSolidus(p[i])
f,a = plt.subplots(dpi=100)
a.plot(p, tliq, label='Liquidus')
a.plot(p, tsol, label='Solidus')
a.set_xlabel('Pressure (GPa)')
a.set_ylabel('Temperature (˚C)')
a.legend()
plt.show()
To see a list of the methods available for any python object, the following can be used within a Jupyter notebook to get access to its documentation.
lz?
A mantle
object is constructed from one or multiple lithologies in specified proportions, and comprises three arguments.
lithologies
: a list of the defined lithology objects to be considered in the melting calculation.proportions
: a list of floats (of equivalent length to the list of Argument 1) comprising the relative proportions of the lithologies listed in Argument 1. The floats do not have to be normalised.names
: a list of strings (of equivalent length to the other lists) comprising the names by which the lithologies will be labelled. These strings will be used in data outputs. This can be ommitted, but the results will be unlabelled.As a demonstration, we can define a three-component mantle. Note that the code is not limited to three lithologies, and can (in principle) have any number of lithologies:
mantle = m.mantle([lz, px, hz], [6, 2, 2], ['Lz', 'Px', 'Hz'])
Here are some of the methods that can be called from the Mantle
class:
The bulk thermodynamic properties of the solid mantle can be called:
mantle.bulkProperties()
Does this change once the mantle is molten?
mantle.bulkProperties(T=1400.0, P=2.0)
To find out when one of the lithologies will start melting, we can call either the solid_intersection_isobaric
method if we are interested in a particular pressure, or we can find out at what pressure the mantle will beginning melting during decompression for a given $T_p$ (potential temperature):
mantle.solidusIntersectionIsobaric(2.0)
mantle.solidusIntersection(1450.0)
Notice that three values were returned, one for each lithology. Since the third lithology is the non-melting harzburgite from Shorttle et al. (2014), it doesn't have a solidus intersection. If we just wanted to know at what temperature the mantle would start melting, but we didn't care which lithology, we could write:
np.nanmin(mantle.solidusIntersectionIsobaric(2.0))
We often talk about mantle temperatures in terms of $T_p$, but sometimes we want to know what temperature this equates to in the solid mantle. The adiabat
method will do this. To see the required inputs we can use:
mantle.adiabat?
So we must specify the pressure and temperature. Note that the documentation specifies this will be the temperature of solid mantle. The adiabatic path taken by semi-molten mantle must be calculated by running a full decompression melting calculation (see below).
mantle.adiabat(10.0, 1300.0)
Stepping back into the world of partially-molten mantle, perhaps we want to know the melt fractions of each lithology at a given temperature and pressure. Just like the pure lithology objects, the F
method will perform this calculation:
mantle.F(2.0, 1400.0)
To calculate the consequences of adiabatic decompression melting for this Mantle
object, the method AdiabaticMelt
can be called which will return a new MeltingColumn
object:
column = mantle.adiabaticMelt(1400.0)
This performed the calculation for a $T_p$ of 1400˚C. The way the calculation is performed can be modified, look up the documentation for the other (optional) arguments.
What is going on behind the scenes? This method performs a simultaneous integration of $\frac{dF}{dP}$ and $\frac{dT}{dP}$ to obtain the thermal gradient through the melting region. The melt fraction $F$ of each lithology is then calculated at the same time. Integration is performed using a fourth-order Runge-Kutta algorithm.
pyMelt provides a built in method plot
to quickly visualise the results of the calculation.
f,a = column.plot()
Often we want to use these results further, however. We can access the results, like temperature:
column.T
This returned a pandas.Series
object, with 100 values. To keep the notebook tidy, the printout is automatically truncated. But what pressures do these correspond to?
column.P
What about melt fraction?
column.F
This returns the total melt fraction, but what about the melt fractions for each lithology?
column.lithologies['Lz'].F
column.lithologies['Px'].F
Here we can see the pyroxenite is almost completely molten, but the lherzolite has only achieved 21.5% melting. The pyroxenite has a sufficiently low abundance that it only contributes a small amount to the aggregate melt fraction.
To save this for further work outside of python, we could construct a pandas.DataFrame
.
results = pd.DataFrame()
results['T'] = column.T
results['P'] = column.P
results['F_total'] = column.F
results['F_lz'] = column.lithologies['Lz'].F
results['F_px'] = column.lithologies['Px'].F
results
Calling results.to_csv('my_results.csv')
will save this as a csv file in your working directory.
Crustal thickness can be calculated assuming passive decompression melting in a triangular spreading centre melting region similar to that of a mid-ocean ridge. As part of the pyMelt.geosettings
module, there is a spreadingCentre
object. When a spreadingCentre
object is created from the meltingColumn
object, the melt fractions are integrated to obtain the crustal thickness, and more shallow melts are removed.
morb = m.geosettings.spreadingCentre(column)
The new object we created contains the attributes:
tc
: integrated crustal thickness at the point where the pressure it exerts is equal to the calculation pressure.P_base_of_crust
: pressure at the base of the crust, at the point where the pressure the generated crust exerts is equal to the calculation pressure.lithology_contributions
integrated proportion of generated crust derived from each lithology where the pressure the generated crust exerts is equal to the calculation pressure.
As well as many of the same attributes as the column.To see the crustal thickness:
morb.tc
If we want to know how much each lithology contributes to the crust, we can call the tc_lithology_contributions
attribute:
morb.lithology_contributions
In this case the pyroxenite dominates the accumulated melt.
pyMelt can be used to estimate the liquidus of mantle-derived melts (crystallisation temperature). This is achieved using the MeltCrystallisationT()
method. Triangular integration must have been performed beforehand to achieve a liquidus temperature, else an error will be returned.
morb.meltCrystallisationT()
This function returns two crystallisation temperature estimates, the first for the melts at the top of the melting column (the shallowest melts), and the second for the melts at the bottom of the melting column (the deepest melts). Both cases assume thermal (and chemical) isolation of the melts during ascent.
See Matthews et al. (2016) or Matthews et al. (2021) for more information about this calculation. To recreate the results from Matthews et al. (2021), the lower crystallisation temperature estimate should be used. Check the documentation to see how the parameters of the calculation may be changed.
Another class in the pyMelt.geosetting
module is intraPlate
, a very simplistic implementation of a plume upwelling beneath lithosphere. This requires a few more arguments than when creating a spreadingCentre
, given by the documentation:
m.geosettings.intraPlate?
If you don't wish to calculate a melt flux, the relative_density
argument may be ignored. You might want to do this if you are calculating melt chemistry (see tutorial 3), or if you want an easy way of cutting off the top of the melting column. The relative density of the plume compared with ambient mantle determines the upwelling velocity, and therefore the melt production over time. pyMelt does not directly estimate lithology density. The approach used by Shorttle et al. (2014) and Matthews et al. (2021) was to calculate lithology densities as a a function of temperature (at fixed pressure for simplicity) using THERMOCALC. A lookup table was then used to extract an appropriate $\Delta \rho$. To illustrate the calculation we will use an abitrary value of $\Delta \rho = 0.2$ kg m$^{-3}$:
oib = m.geosettings.intraPlate(column, P_lithosphere=1.2, relative_density=0.2)
To print the calculated melt flux (in m$^3$ s$^{-1}$):
oib.melt_flux