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:
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/comp/codata.py:88: UserWarning: Non-positive entries found in specified components. Negative values have been replaced with NaN. Renormalisation assumes all positive entries.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/comp/codata.py:88: UserWarning: Non-positive entries found in specified components. Negative values have been replaced with NaN. Renormalisation assumes all positive entries.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/comp/codata.py:43: UserWarning: Non-positive entries found. Closure operation assumes all positive entries.
warnings.warn(
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/comp/codata.py:88: UserWarning: Non-positive entries found in specified components. Negative values have been replaced with NaN. Renormalisation assumes all positive entries.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/comp/codata.py:88: UserWarning: Non-positive entries found in specified components. Negative values have been replaced with NaN. Renormalisation assumes all positive entries.
warnings.warn(
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
First we have to convert these element-based compositions to oxide-based compositions:
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/comp/codata.py:88: UserWarning: Non-positive entries found in specified components. Negative values have been replaced with NaN. Renormalisation assumes all positive entries.
warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/pyrolite/checkouts/main/pyrolite/comp/codata.py:88: UserWarning: Non-positive entries found in specified components. Negative values have been replaced with NaN. Renormalisation assumes all positive entries.
warnings.warn(
These can now be used with our endmember proportions to regenerate a composition:
recombined = pd.DataFrame(ed.values.flatten() @ emvalues).T.pyrochem.to_weight()
recombined
To make sure these compositions are within 0.01 percent:
assert np.allclose(recombined.values, comp.values, rtol=10**-4)
Total running time of the script: (0 minutes 0.180 seconds)