.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/_4_unorganized/plot_olivine_control_line.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples__4_unorganized_plot_olivine_control_line.py: MELTS: Calculate Olivine Control Line ===================================== Extrapolates a melt composition to a more primitive composition under the assumption that only olivine has crystallised from the melt. This calculation might be used for estimating primary melt compositions, or the effect of post-entrapment crystallisation on olivine-hosted melt inclusions. The calculation uses the rhyoliteMELTS v1.2 model. Open this code in an executable MyBinder instance (MyBinder links may be slow to load-- please be patient!): .. image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gl/swmatthews-research%2FThermoEngineLite/main?urlpath=%2Fdoc%2Ftree%2F.%2Fdoc%2Fsource%2Fauto_examples%2F_4_unorganized%2Fplot_olivine_control_line.ipynb .. GENERATED FROM PYTHON SOURCE LINES 17-21 Initialization -------------- Import the necessary packages .. GENERATED FROM PYTHON SOURCE LINES 21-29 .. code-block:: Python from thermoengine import model, magmaforge, redox from thermoengine.core import chem import pandas as pd import numpy as np from scipy.optimize import root_scalar import matplotlib.pyplot as plt .. GENERATED FROM PYTHON SOURCE LINES 30-31 Retrieve the MELTS v1.2 database and extract the olivine and liquid models. .. GENERATED FROM PYTHON SOURCE LINES 31-36 .. code-block:: Python db = model.Database("MELTS_v1_2") liq = db.get_phase('Liq') olv = db.get_phase('Ol') .. GENERATED FROM PYTHON SOURCE LINES 37-39 Set up the Calculations ----------------------- .. GENERATED FROM PYTHON SOURCE LINES 41-44 This section sets up the algorithm for incrementally finding the equilibrium olivine composition at the liquidus, adding a small amount of it into the melt, and repeating. You must run the cells below, but you do not need to change anything inside them to run a calculation. .. GENERATED FROM PYTHON SOURCE LINES 44-184 .. code-block:: Python def calculate_olivine_control_line( starting_comp : pd.Series, P_bar : float, target_Mgn : float, step_size : float = 0.005 ) -> tuple[pd.DataFrame, pd.DataFrame]: """ Calculate an olivine control line, starting at the start composition and ending when the equilibrium olivine has the target Mgn. The Fe3/tFe ratio must be set in the starting_comp. Inputs ------ starting_comp : pd.Series The starting composition in weight percent (or grams) of oxides P_bar : float The pressure in bars target_Mgn : float The olivine Mgn at which to terminate the calculation step_size : float The moles of olivine to add to the liquid composition at each step Outputs ------- pd.DataFrame The melt compositions at each step on the olivine control line pd.DataFrame The olivine compositions at each step on the olivine control line """ if 'Fe2O3' not in starting_comp or starting_comp['Fe2O3'] == 0.0: print("Warning: Fe2O3 set to 0.0") if 'FeO' not in starting_comp or starting_comp['FeO'] == 0.0: print("Warning: FeO is set to 0.0") if target_Mgn > 1 and target_Mgn < 100: target_Mgn = target_Mgn / 100 elif target_Mgn > 100 or target_Mgn < 0: assert False, "Bad target Mg number." start_comp_arr = np.zeros(len(chem.OXIDE_ORDER)) for i, ox in zip(range(len(chem.OXIDE_ORDER)), chem.OXIDE_ORDER): if ox in starting_comp: start_comp_arr[i] = starting_comp[ox] else: start_comp_arr[i] = 0.0 start_comp_arr = start_comp_arr / np.sum(start_comp_arr) * 100 liq_moles = magmaforge.System.convert_wtoxides_to_liquid_endmems( starting_comp, liq, oxides=starting_comp.index) olv_omni_comps = _calc_omni_comps_for_phases(olv, liq) T, olv_mols = find_liquidus_olivine(P_bar, liq_moles, olv_omni_comps) Mgn = olv_mols[5]/(olv_mols[1]+olv_mols[5]) MAX_ITER = 1000 iternum = 0 check_phase_sat(T, P_bar, starting_comp) # Initialise dataframes for results liq_comps = np.zeros([MAX_ITER, len(chem.OXIDE_ORDER)]) olv_comps = np.zeros([MAX_ITER, olv.endmember_num+1]) liq_comps[0,:] = start_comp_arr olv_comps[0,:-1] = olv_mols olv_comps[0,-1] = Mgn T_seq = [T] while Mgn < target_Mgn and iternum < MAX_ITER: liq_moles += step_size * olv_omni_comps.T.dot(olv_mols) T, olv_mols = find_liquidus_olivine(P_bar, liq_moles, olv_omni_comps, T0=T) Mgn = olv_mols[5]/(olv_mols[1]+olv_mols[5]) liq_mol_ox = liq.endmember_mol_oxide_comps.T.dot(liq_moles) liq_wtpt = chem.mol_to_wt_oxide(liq_mol_ox, chem.OXIDE_ORDER) liq_wtpt = liq_wtpt/np.sum(liq_wtpt) * 100 liq_comps[iternum+1,:] = liq_wtpt olv_comps[iternum+1,:-1] = olv_mols olv_comps[iternum+1, -1] = Mgn T_seq.append(T) iternum += 1 liq_comps = pd.DataFrame(liq_comps[:iternum+1,:], columns=chem.OXIDE_ORDER, index=T_seq) olv_comps = pd.DataFrame(olv_comps[:iternum+1,:], columns=list(olv.endmember_names)+['Mg#'], index=T_seq) return liq_comps, olv_comps def find_liquidus_olivine(P, liq_moles, olv_omni_comps, T0=1500.0): res = root_scalar(_affncomp_root, x0=T0, args=(P, liq_moles, olv_omni_comps)) assert res.converged, "Something has gone wrong when finding the temperature, check inputs." T = res.root affn, comp = get_olv_affncomp_from_liq(T, P, liq_moles, olv_omni_comps) return T, comp def _affncomp_root(T, P, liq_moles, omni_comps): return get_olv_affncomp_from_liq(T, P, liq_moles, omni_comps)[0] def get_olv_affncomp_from_liq(T, P, liq_moles, omni_comps): liq_mu = liq.chem_potential(T, P, mol=liq_moles) olv_mu = omni_comps.dot(liq_mu) affn, comp = olv.affinity_and_comp(T, P, olv_mu) return (affn, comp) def _calc_omni_comps_for_phases(phase, omni_phase): """ Calculates the matrix for conversion of phase compositions into omni_component endmembers. """ omni_element_comps = omni_phase.endmember_element_comps inv_omni_element_comps = np.linalg.pinv(omni_element_comps) TOL = 1e-12 phs_element_comps = phase.endmember_element_comps phs_omni_comps = phs_element_comps.dot(inv_omni_element_comps) phs_omni_comps[np.abs(phs_omni_comps) <= TOL] = 0 return phs_omni_comps def check_phase_sat(T, P, oxide_wtpt): sys = magmaforge.System(oxide_wtpt, T_K=T, P_bar=P, database='MELTS_v1_2') phs_mass = sys._system_calculator._total_mass_of_every_phase for phs in phs_mass: if phs not in ['Liquid', 'Olivine'] and phs_mass[phs] > 0.0: print(f"Warning: {phs} is saturated: {phs_mass[phs]:.2f} wt% present at olvine-saturation temperature.") .. GENERATED FROM PYTHON SOURCE LINES 185-189 User Inputs ----------- Provide the starting composition of the magma here. You must have already determined the amount of FeO and Fe2O3 in the magma. .. GENERATED FROM PYTHON SOURCE LINES 189-208 .. code-block:: Python oxide_comp = pd.Series({ 'SiO2': 48.32, 'TiO2': 0.93, 'Al2O3': 15.99, 'Fe2O3': 0.67, 'FeO': 8.36, 'MnO': 0.16, 'MgO': 10.56, 'CaO': 14.01, 'Na2O': 1.72, 'K2O': 0.05, 'P2O5': 0.03, 'H2O': 0.3, 'CO2': 0.05 }) # Composition of olivine-saturated glass from Kistufell, Iceland (Breddam, 2002) .. GENERATED FROM PYTHON SOURCE LINES 209-210 Provide the pressure of crystallisation and the target Mg# for the equilibrium olivine: .. GENERATED FROM PYTHON SOURCE LINES 210-214 .. code-block:: Python pressure_bar = 1000.0 target_Mgn = 90.0 .. GENERATED FROM PYTHON SOURCE LINES 215-216 Run the calculation: .. GENERATED FROM PYTHON SOURCE LINES 216-224 .. code-block:: Python liq_comp, olv_comp = calculate_olivine_control_line( oxide_comp, P_bar=1000.0, target_Mgn=90.0 ) .. GENERATED FROM PYTHON SOURCE LINES 225-229 Results ------- We can see the results as tables of the liquid and olivine compositions along the extrapolated liquid line of descent: .. GENERATED FROM PYTHON SOURCE LINES 229-232 .. code-block:: Python liq_comp .. raw:: html
SiO2 TiO2 Al2O3 Fe2O3 Cr2O3 FeO MnO MgO NiO CoO CaO Na2O K2O P2O5 H2O CO2
1499.443504 47.770638 0.919427 15.808206 0.662383 0.0 8.264953 0.158181 10.439941 0.0 0.0 13.850717 1.700445 0.049432 0.029659 0.296589 0.049432
1506.843444 47.714975 0.912681 15.692221 0.657523 0.0 8.298730 0.158723 10.702024 0.0 0.0 13.753162 1.687969 0.049069 0.029441 0.294413 0.049069
1514.052214 47.660542 0.906041 15.578063 0.652739 0.0 8.330148 0.159229 10.961531 0.0 0.0 13.657094 1.675689 0.048712 0.029227 0.292271 0.048712
1521.078311 47.607292 0.899505 15.465683 0.648030 0.0 8.359348 0.159701 11.218464 0.0 0.0 13.562476 1.663601 0.048360 0.029016 0.290163 0.048360
1527.929701 47.555178 0.893069 15.355034 0.643394 0.0 8.386461 0.160139 11.472831 0.0 0.0 13.469269 1.651698 0.048014 0.028809 0.288087 0.048014
1534.613862 47.504160 0.886732 15.246070 0.638828 0.0 8.411609 0.160548 11.724643 0.0 0.0 13.377439 1.639977 0.047674 0.028604 0.286043 0.047674
1541.137830 47.454196 0.880490 15.138749 0.634332 0.0 8.434903 0.160927 11.973911 0.0 0.0 13.286951 1.628433 0.047338 0.028403 0.284029 0.047338
1547.508210 47.405250 0.874341 15.033029 0.629902 0.0 8.456448 0.161278 12.220653 0.0 0.0 13.197772 1.617061 0.047008 0.028205 0.282046 0.047008
1553.731227 47.357285 0.868283 14.928871 0.625537 0.0 8.476341 0.161603 12.464886 0.0 0.0 13.109872 1.605857 0.046682 0.028009 0.280091 0.046682
1559.812738 47.310269 0.862314 14.826237 0.621237 0.0 8.494671 0.161903 12.706628 0.0 0.0 13.023219 1.594817 0.046361 0.027817 0.278166 0.046361
1565.758290 47.264169 0.856431 14.725090 0.616999 0.0 8.511523 0.162180 12.945901 0.0 0.0 12.937785 1.583937 0.046045 0.027627 0.276268 0.046045
1571.573099 47.218956 0.850633 14.625395 0.612821 0.0 8.526975 0.162435 13.182727 0.0 0.0 12.853541 1.573213 0.045733 0.027440 0.274398 0.045733
1577.262098 47.174600 0.844917 14.527118 0.608703 0.0 8.541102 0.162668 13.417129 0.0 0.0 12.770461 1.562642 0.045426 0.027255 0.272554 0.045426
1582.829955 47.131076 0.839281 14.430225 0.604644 0.0 8.553970 0.162880 13.649131 0.0 0.0 12.688519 1.552219 0.045123 0.027074 0.270736 0.045123
1588.281088 47.088356 0.833725 14.334686 0.600640 0.0 8.565646 0.163073 13.878757 0.0 0.0 12.607688 1.541942 0.044824 0.026894 0.268943 0.044824
1593.619682 47.046416 0.828245 14.240470 0.596693 0.0 8.576188 0.163248 14.106034 0.0 0.0 12.527945 1.531808 0.044529 0.026718 0.267176 0.044529
1598.849698 47.005234 0.822840 14.147547 0.592799 0.0 8.585655 0.163406 14.330987 0.0 0.0 12.449266 1.521812 0.044239 0.026543 0.265432 0.044239
1603.974914 46.964785 0.817510 14.055889 0.588958 0.0 8.594099 0.163546 14.553642 0.0 0.0 12.371629 1.511953 0.043952 0.026371 0.263713 0.043952
1608.998902 46.925050 0.812251 13.965468 0.585170 0.0 8.601571 0.163671 14.774027 0.0 0.0 12.295010 1.502227 0.043669 0.026202 0.262016 0.043669
1613.925060 46.886007 0.807062 13.876258 0.581432 0.0 8.608117 0.163780 14.992167 0.0 0.0 12.219389 1.492631 0.043390 0.026034 0.260343 0.043390


.. GENERATED FROM PYTHON SOURCE LINES 233-236 .. code-block:: Python olv_comp .. raw:: html
Tephroite Fayalite Coolivine Niolivine Monticellite Forsterite Mg#
1499.443504 0.002446 0.133909 0.0 0.0 0.014781 0.848864 0.863744
1506.843444 0.002403 0.131136 0.0 0.0 0.014584 0.851877 0.866598
1514.052214 0.002361 0.128498 0.0 0.0 0.014396 0.854745 0.869312
1521.078311 0.002322 0.125984 0.0 0.0 0.014210 0.857484 0.871899
1527.929701 0.002284 0.123585 0.0 0.0 0.014032 0.860100 0.874365
1534.613862 0.002247 0.121293 0.0 0.0 0.013859 0.862601 0.876721
1541.137830 0.002212 0.119102 0.0 0.0 0.013691 0.864995 0.878974
1547.508210 0.002178 0.117003 0.0 0.0 0.013528 0.867291 0.881130
1553.731227 0.002146 0.114991 0.0 0.0 0.013370 0.869494 0.883197
1559.812738 0.002114 0.113061 0.0 0.0 0.013216 0.871610 0.885179
1565.758290 0.002084 0.111206 0.0 0.0 0.013066 0.873644 0.887083
1571.573099 0.002055 0.109423 0.0 0.0 0.012920 0.875602 0.888913
1577.262098 0.002027 0.107708 0.0 0.0 0.012777 0.877488 0.890674
1582.829955 0.002000 0.106056 0.0 0.0 0.012639 0.879306 0.892369
1588.281088 0.001973 0.104463 0.0 0.0 0.012504 0.881060 0.894002
1593.619682 0.001948 0.102927 0.0 0.0 0.012372 0.882753 0.895578
1598.849698 0.001923 0.101444 0.0 0.0 0.012243 0.884390 0.897098
1603.974914 0.001899 0.100011 0.0 0.0 0.012117 0.885972 0.898567
1608.998902 0.001876 0.098626 0.0 0.0 0.011994 0.887504 0.899986
1613.925060 0.001853 0.097287 0.0 0.0 0.011874 0.888986 0.901359


.. GENERATED FROM PYTHON SOURCE LINES 237-238 We can save the tables (which can be downloaded if you are running this in a binder): .. GENERATED FROM PYTHON SOURCE LINES 238-242 .. code-block:: Python liq_comp.to_csv('liquid_compositions.csv') olv_comp.to_csv('olivine_compositions.csv') .. GENERATED FROM PYTHON SOURCE LINES 243-244 But we can also plot the results: .. GENERATED FROM PYTHON SOURCE LINES 244-260 .. code-block:: Python fig, ax = plt.subplots(3,1, figsize=(3.75, 7)) ax[0].plot(liq_comp['MgO'], liq_comp['FeO'], marker='s', markersize=3) ax[1].plot(liq_comp['MgO'], olv_comp['Mg#']*100, marker='s', markersize=3) ax[2].plot(liq_comp['MgO'], liq_comp.index - 273.15, marker='s', markersize=3) for a in ax: a.set_xlabel('MgO (wt%)') ax[0].set_ylabel('FeO (wt%)') ax[1].set_ylabel('Fo (mol%)') ax[2].set_ylabel('Temperature (°C)') fig.tight_layout() plt.show() .. image-sg:: /auto_examples/_4_unorganized/images/sphx_glr_plot_olivine_control_line_001.png :alt: plot olivine control line :srcset: /auto_examples/_4_unorganized/images/sphx_glr_plot_olivine_control_line_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.418 seconds) .. _sphx_glr_download_auto_examples__4_unorganized_plot_olivine_control_line.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_olivine_control_line.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_olivine_control_line.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_olivine_control_line.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_