Mineral Endmember Decomposition

A common task when working with mineral chemistry data is to take measured compositions and decompose these into relative proportions of mineral endmember compositions. pyrolite includes some utilities to achieve this and a limited mineral database for looking up endmember compositions.

import numpy as np
import pandas as pd

from pyrolite.mineral.mindb import get_mineral
from pyrolite.mineral.normative import endmember_decompose

First we’ll start with a composition of an unknown olivine:

comp = pd.Series({"MgO": 42.06, "SiO2": 39.19, "FeO": 18.75})

We can break this down into olivine endmebmers using the endmember_decompose() function:

ed = endmember_decompose(
    pd.DataFrame(comp).T, endmembers="olivine", order=1, molecular=True
)
ed
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.57294068        nan        nan 0.10480888]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.42705932 0.29485884 0.29750712 0.31249035]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[       nan 0.70514116        nan        nan]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
name forsterite fayalite
0 79.994 20.006


Equally, if you knew the likely endmembers beforehand, you could specify a list of endmembers:

ed = endmember_decompose(
    pd.DataFrame(comp).T, endmembers=["forsterite", "fayalite"], order=1, molecular=True
)
ed
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.57294068        nan]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[0.42705932 0.29485884]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[       nan 0.70514116]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
forsterite fayalite
0 79.994 20.006


We can check this by recombining the components with these proportions. We can first lookup the compositions for our endmembers:

em = pd.DataFrame([get_mineral("forsterite"), get_mineral("fayalite")])
em.loc[:, ~(em == 0).all(axis=0)]  # columns not full of zeros
name group formula Mg Si O Fe
0 forsterite olivine Mg2SiO4 0.346 0.200 0.455 0.000
1 fayalite olivine Fe2SiO4 0.000 0.138 0.314 0.548


First we have to convert these element-based compositions to oxide-based compositions:

emvalues = (
    em.loc[:, ["Mg", "Si", "Fe"]]
    .pyrochem.to_molecular()
    .fillna(0)
    .pyrochem.convert_chemistry(to=["MgO", "SiO2", "FeO"], molecular=True)
    .fillna(0)
    .pyrocomp.renormalise(scale=1)
)
emvalues
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[66.66666667         nan]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[33.33333333 33.33333333]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/geochem/transform.py:351: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value '[        nan 66.66666667]' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.
  _df.loc[:, targetnames] = subsum.values[:, np.newaxis] @ coeff[np.newaxis, :]
MgO SiO2 FeO
0 0.667 0.333 0.000
1 0.000 0.333 0.667


These can now be used with our endmember proportions to regenerate a composition:

MgO SiO2 FeO
0 42.059 39.191 18.75


To make sure these compositions are within 0.01 percent:

Total running time of the script: (0 minutes 0.214 seconds)