This page was generated from docs/Examples/Error_propagation/Cpx_only_contour_plot.ipynb. Interactive online version: Binder badge.

Python Notebook Download

Error propagation - Cpx-only

  • This notebook gives a worked example showing how to propagate uncertainty for Cpx-only thermometry for different amounts of EPMA-based error on Na\(_2\)O measurements.

  • We use the functionality provided by Pyrolite to contour the results from Monte Carlo simulations, as well as hexplot.

  • If you use the pyroplot.density command, please remember to cite Pyrolite! (Williams et al., (2020). pyrolite: Python for geochemistry. Journal of Open Source Software, 5(50), 2314, doi: 10.21105/joss.02314)

  • This builds on from the notebook showing how to consider error in a single phase (Liquid_Thermometry_error_prop.ipynb). We suggest you look at that first, as its simpler when you don’t have to worry about two separate phases

  • We consider an example Cpx composition from Gleeson et al. (2020) - https://doi.org/10.1093/petrology/egaa094

  • You can download the Excel spreadsheet here: https://github.com/PennyWieser/Thermobar/blob/main/docs/Examples/Error_propagation/Gleeson2020_Cpx_Comps.xlsx

You need to install Thermobar once on your machine, if you haven’t done this yet, uncomment the line below (remove the #)

[1]:
#!pip install Thermobar

You will also need to install pyrolite for this example, if you haven’t done so already, uncomment the line below

[2]:
#!pip install pyrolite
[3]:
# Import other python stuff
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import Thermobar as pt
import sympy as sym
pd.options.display.max_columns = None
import pyrolite

Importing data

  • Note, we haven’t bothered to add the “_Cpx” names after each oxide, so we simply use “suffix=”_Cpx” when we load the data

[4]:
out=pt.import_excel('Gleeson2020_Cpx_Comps.xlsx', sheet_name="Sheet1", suffix="_Cpx")
my_input=out['my_input']
myCpxs1=out['Cpxs']

Lets select 1 Cpx to run the calculations on

  • The [[ ]] keeps it as a dataframe

[5]:
myCpxs1.iloc[[0]]
[5]:
SiO2_Cpx TiO2_Cpx Al2O3_Cpx FeOt_Cpx MnO_Cpx MgO_Cpx CaO_Cpx Na2O_Cpx K2O_Cpx Cr2O3_Cpx Sample_ID_Cpx
0 51.3863 0.467 4.1961 3.8654 0.12 16.3137 21.3039 0.378 0.0007 1.0191 0

Work out errors

  • The Cameca software used to analyse these Cpxs returns the error as absolute elemental wt%, ignoring Oxygen, for each element (helpful!). These are loaded in as Na_Err, Si_Err….

  • This means that you need to do some steps to work out the analytical error on each Cpx. We show these steps below.

  • First, we decide it easiest to use the convert_oxide_percent_to_element_weight_percent function to convert measured Cpx compositions as oxides into wt% to compare to these errors. Without oxygen = True, does the calculations without oxygen. You can use this function for any phase, just specify what suffix your phase has.

[6]:
Cpx_conv=pt.convert_oxide_percent_to_element_weight_percent(df=myCpxs1, suffix='_Cpx', without_oxygen=True)
Cpx_conv.head()
[6]:
Si_wt_noO2 Mg_wt_noO2 Fe_wt_noO2 Ca_wt_noO2 Al_wt_noO2 Na_wt_noO2 K_wt_noO2 Mn_wt_noO2 Ti_wt_noO2 Cr_wt_noO2 P_wt_noO2 F_wt_noO2 H_wt_noO2 Cl_wt_noO2
0 43.155184 17.675115 5.398069 27.354400 3.989980 0.503819 0.001044 0.166972 0.502666 1.252750 0.0 0.0 0.0 0.0
1 42.432683 17.004438 5.468846 27.558497 4.967044 0.507369 0.000447 0.121440 0.625890 1.313346 0.0 0.0 0.0 0.0
2 42.399838 16.694183 5.608834 27.398388 5.209677 0.523472 0.022141 0.141541 0.681296 1.320631 0.0 0.0 0.0 0.0
3 42.649103 17.224819 5.638033 27.496627 4.631102 0.538772 0.004336 0.097927 0.632681 1.086601 0.0 0.0 0.0 0.0
4 41.057438 15.479289 7.324030 26.604851 6.874709 0.702197 0.025343 0.188588 1.005908 0.737648 0.0 0.0 0.0 0.0
  • Now we calculate the % error for each element. For example, for Na, we take the Cameca error (column Na_Err), and compare it to the concentration we just calculated in wt% for Na (column Na_wt_noO2 in the Cpx_conv dataframe)

[7]:
Perc_Err_Na=100*my_input['Na_Err']/Cpx_conv['Na_wt_noO2']
Perc_Err_Na.head()
[7]:
0    8.534819
1    8.553938
2    8.520041
3    8.315210
4    7.049307
dtype: float64
[8]:
# We now need to re-arrange these into a form that we can load into the function.
# We make a new dataframe of these errors
df_Cpx_Err=pd.DataFrame(data={'SiO2_Cpx_Err': 100*my_input['Si_Err']/Cpx_conv['Si_wt_noO2'],
                            'TiO2_Cpx_Err':100*my_input['Ti_Err']/Cpx_conv['Ti_wt_noO2'],
                             'Al2O3_Cpx_Err':100*my_input['Al_Err']/Cpx_conv['Al_wt_noO2'],
                             'FeOt_Cpx_Err':100*my_input['Fe_Err']/Cpx_conv['Fe_wt_noO2'],
                            'MnO_Cpx_Err':100*my_input['Mn_Err']/Cpx_conv['Mn_wt_noO2'],
                            'MgO_Cpx_Err':100*my_input['Mg_Err']/Cpx_conv['Mg_wt_noO2'],
                            'CaO_Cpx_Err':100*my_input['Ca_Err']/Cpx_conv['Ca_wt_noO2'],
                            'Na2O_Cpx_Err':100*my_input['Na_Err']/Cpx_conv['Na_wt_noO2'],
                            'Cr2O3_Cpx_Err':100*my_input['Cr_Err']/Cpx_conv['Cr_wt_noO2'],
                               'K2O_Cpx_Err': 100*my_input['K_Err']/Cpx_conv['K_wt_noO2']}, index=[0])

Lets select 1 Cpx, and its corresponding error as an example

  • using [[ ]] keeps it as a dataframe, we reset the index because sometimes this can cause python mayhem.

[9]:
Cpx_1=myCpxs1.iloc[[0]].reset_index(drop=True)
Cpx_1_Err=df_Cpx_Err.iloc[[0]].reset_index(drop=True)
Cpx_1_Err
[9]:
SiO2_Cpx_Err TiO2_Cpx_Err Al2O3_Cpx_Err FeOt_Cpx_Err MnO_Cpx_Err MgO_Cpx_Err CaO_Cpx_Err Na2O_Cpx_Err Cr2O3_Cpx_Err K2O_Cpx_Err
0 1.186416 2.148544 0.907273 3.056648 27.309951 1.129271 0.858363 8.534819 5.172622 1934.77739

Lets calculate the errors for this Cpx

  • You could do this for all Cpxs, rather than just this 1 sample, but it’ll take longer, so we do a cut down version here!

  • 20,000 iterations may seem like a lot, but it really helps with the contouring

[10]:
Calc_Cpx_dist=pt.add_noise_sample_1phase(phase_comp=Cpx_1, phase_err=Cpx_1_Err,
                                            phase_err_type="Perc", duplicates=20000, err_dist="normal")
All negative numbers replaced with zeros. If you wish to keep these, set positive=False

Lets calculate pressures and temperatures using a wide variety of equations for Cpx-only thermobarometry

  • This cell takes abit of time, its 20,000 calculations using a wide variety of P and T equations!

[11]:
Calc_PT=pt.calculate_cpx_only_press_all_eqs(cpx_comps=Calc_Cpx_dist)
Calc_PT.head()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
c:\Users\penny\Box\Postdoc\MyBarometers\Thermobar_outer\docs\Examples\Error_propagation\Cpx_only_contour_plot.ipynb Cell 21 line 1
----> <a href='vscode-notebook-cell:/c%3A/Users/penny/Box/Postdoc/MyBarometers/Thermobar_outer/docs/Examples/Error_propagation/Cpx_only_contour_plot.ipynb#X26sZmlsZQ%3D%3D?line=0'>1</a> Calc_PT=pt.calculate_cpx_only_press_all_eqs(cpx_comps=Calc_Cpx_dist)
      <a href='vscode-notebook-cell:/c%3A/Users/penny/Box/Postdoc/MyBarometers/Thermobar_outer/docs/Examples/Error_propagation/Cpx_only_contour_plot.ipynb#X26sZmlsZQ%3D%3D?line=1'>2</a> Calc_PT.head()

File c:\users\penny\box\postdoc\mybarometers\thermobar_outer\src\Thermobar\clinopyroxene_thermobarometry.py:3365, in calculate_cpx_only_press_all_eqs(cpx_comps, plot, return_cpx, H2O_Liq)
   3360 cpx_comps_c['T_Wang21_eq2']=calculate_cpx_only_temp(cpx_comps=cpx_comps_copy, equationT="T_Wang2021_eq2", H2O_Liq=H2O_Liq)
   3362 cpx_comps_c['T_Petrelli20']=calculate_cpx_only_temp(cpx_comps=cpx_comps_copy,
   3363 equationT="T_Petrelli2020_Cpx_only_onnx")
-> 3365 cpx_comps_c['T_Jorgenson22']=calculate_cpx_only_temp(cpx_comps=cpx_comps_copy,
   3366 equationT="T_Jorgenson2022_Cpx_only_onnx")
   3368 # cpx_comps_c['T_Petrelli21_H2O']=calculate_cpx_only_temp(cpx_comps=cpx_comps_copy,
   3369 # equationT="T_Petrelli2020_Cpx_only_withH2O_onnx").T_K_calc
   3370 #
   3371 # cpx_comps_c['P_Petrelli21_H2O']=calculate_cpx_only_press(cpx_comps=cpx_comps_copy,
   3372 # equationP="P_Petrelli2020_Cpx_only_withH2O").P_kbar_calc
   3374 cpx_comps_c['T_Put_Teq32d_Peq32a']=calculate_cpx_only_press_temp(cpx_comps=cpx_comps_copy,
   3375 equationP="P_Put2008_eq32a", equationT="T_Put2008_eq32d").T_K_calc

File c:\users\penny\box\postdoc\mybarometers\thermobar_outer\src\Thermobar\clinopyroxene_thermobarometry.py:3537, in calculate_cpx_only_temp(cpx_comps, equationT, P, H2O_Liq, eq_tests)
   3533     T_K=df_stats['T_K_calc']
   3535 elif ('Petrelli' in equationT or "Jorgenson" in equationT) and "onnx" in equationT:
-> 3537     T_K=func(cpx_comps=cpx_comps_c)
   3541 else:
   3543     kwargs = {name: cpx_components[name] for name, p in sig.parameters.items()
   3544     if p.kind == inspect.Parameter.KEYWORD_ONLY}

File c:\users\penny\box\postdoc\mybarometers\thermobar_outer\src\Thermobar\clinopyroxene_thermobarometry.py:1769, in T_Jorgenson2022_Cpx_only_onnx(P, cpx_comps)
   1767 import onnxruntime as rt
   1768 path=Path(Thermobar_onnx.__file__).parent
-> 1769 sess =  rt.InferenceSession(str(path/"Jorg21_Cpx_only_Temp.onnx"))
   1772 input_name = sess.get_inputs()[0].name
   1773 label_name = sess.get_outputs()[0].name

File c:\Users\penny\anaconda3\Lib\site-packages\onnxruntime\capi\onnxruntime_inference_collection.py:432, in InferenceSession.__init__(self, path_or_bytes, sess_options, providers, provider_options, **kwargs)
    430         raise fallback_error from e
    431 # Fallback is disabled. Raise the original error.
--> 432 raise e

File c:\Users\penny\anaconda3\Lib\site-packages\onnxruntime\capi\onnxruntime_inference_collection.py:419, in InferenceSession.__init__(self, path_or_bytes, sess_options, providers, provider_options, **kwargs)
    416 disabled_optimizers = kwargs["disabled_optimizers"] if "disabled_optimizers" in kwargs else None
    418 try:
--> 419     self._create_inference_session(providers, provider_options, disabled_optimizers)
    420 except (ValueError, RuntimeError) as e:
    421     if self._enable_fallback:

File c:\Users\penny\anaconda3\Lib\site-packages\onnxruntime\capi\onnxruntime_inference_collection.py:451, in InferenceSession._create_inference_session(self, providers, provider_options, disabled_optimizers)
    449 if not providers and len(available_providers) > 1:
    450     self.disable_fallback()
--> 451     raise ValueError(
    452         f"This ORT build has {available_providers} enabled. "
    453         "Since ORT 1.9, you are required to explicitly set "
    454         "the providers parameter when instantiating InferenceSession. For example, "
    455         f"onnxruntime.InferenceSession(..., providers={available_providers}, ...)"
    456     )
    458 session_options = self._sess_options if self._sess_options else C.get_default_session_options()
    459 if self._model_path:

ValueError: This ORT build has ['AzureExecutionProvider', 'CPUExecutionProvider'] enabled. Since ORT 1.9, you are required to explicitly set the providers parameter when instantiating InferenceSession. For example, onnxruntime.InferenceSession(..., providers=['AzureExecutionProvider', 'CPUExecutionProvider'], ...)

Lets plot these calculations using the Wang et al. (2021) thermometer and barometer

  • You can see that despite random input errors, P and T end up correlated in the resulting distribution

  • You can also see that pressure is controlled predominantly by the error on the Na2O component, with scatter around this trend

[ ]:
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5))
ax1.plot(Calc_PT['P_Wang21_eq1'], Calc_PT['T_Wang21_eq2'], '.r' )
ax2.plot(Calc_PT['P_Wang21_eq1'], Calc_PT['Na2O_Cpx'], '.b' )
ax1.set_xlabel('Calc P (kbar)')
ax2.set_xlabel('Calc P (kbar)')
ax1.set_ylabel('Calc T (K)')
ax1.set_ylabel('Na$_2$O Cpx')

Text(0, 0.5, 'Na$_2$O Cpx')
../../_images/Examples_Error_propagation_Cpx_only_contour_plot_22_1.png

A more robust way to show this

  • If you play around with duplicates = , you’ll see that the more points you ask for, the more spread in pressure space.

  • It is more robust to contour results

  • First, we show how to do this using hexbin (matplotlib)

  • We underlie the published error on the barometer, at the average calculated composition.

[ ]:
fig, (ax1)  = plt.subplots(1, 1, figsize=(6,5))

ax1.hexbin(Calc_PT['P_Wang21_eq1'],
           Calc_PT['T_Wang21_eq2'],
            cmap=plt.cm.BuPu,
            linewidths=0.2, bins=10)
ax1.set_xlabel('Calc P (kbar)')
ax1.set_xlabel('Calc T (K)')


ax1.errorbar(np.mean(Calc_PT['P_Wang21_eq1']),
             np.mean(Calc_PT['T_Wang21_eq2']),
             xerr=1.52, yerr=35.2,
             fmt='.', ecolor='k', elinewidth=0.8, mfc='k', ms=1)
<ErrorbarContainer object of 3 artists>
../../_images/Examples_Error_propagation_Cpx_only_contour_plot_24_1.png

But this is still hard to visualize!

  • We can add contours using pyrolite, so we can visualize where 67% of simulations lie, and where 95% of simulations lie

  • You will have to play around a bit with the number of bins, and the extent parameter.

  • We always plot PT first, then use that to work out the extent, which tells the code where to look for contours in. This only needs to be approximate.

  • This step can be slow, if you are using 4-8 Gb of Ram, you might want to reduce the number of duplicates when you make the samples.

  • Remember to cite Pyrolite if you use these contours!

[ ]:
fig, (ax1)  = plt.subplots(1, 1, figsize=(6,5))

# This parameter helps pyrolite converge on the contour positions faster,
# e.g. saying look for contours between 3.5 and 7 kbar
# and between 1470 and 1540 K. You'll need to change this for your particular system
extent1=(3.5, 7, 1470, 1540)
ax1.hexbin(Calc_PT['P_Wang21_eq1'],
           Calc_PT['T_Wang21_eq2'],
            cmap=plt.cm.BuPu,
            linewidths=0.2, bins=10)
ax1.set_xlabel('Calc P (kbar)')
ax1.set_xlabel('Calc T (K)')


Calc_PT.loc[:,
["P_Wang21_eq1", "T_Wang21_eq2"]].pyroplot.density(ax=ax1, contours=[0.67, 0.95], colors=['m', 'c'],
                                                   extent=extent1, cmap="viridis",
                                                   bins=30, label_contours=False)
# Adjust axes limits
ax1.set_xlim([3.5, 7.1])
ax1.set_ylim([1450,  1520])

ax1.errorbar(np.mean(Calc_PT['P_Wang21_eq1']),
             np.mean(Calc_PT['T_Wang21_eq2']),
             xerr=1.52, yerr=35.2,
             fmt='.', ecolor='k', elinewidth=0.8, mfc='k', ms=1)

ax1.set_xlabel('Calc P (kbar)')
ax1.set_ylabel('Calc T (K)')
Text(0, 0.5, 'Calc T (K)')
../../_images/Examples_Error_propagation_Cpx_only_contour_plot_26_1.png

Lets work out what would happen if we doubled the analytical error on Na2O

  • Be patient, this takes a bit of time, its 20,000 calculations!

[ ]:
# First, lets duplicate the error dataframe
df_Cpx_Err_2Na=df_Cpx_Err.copy()
# Lets set Na2O error to 2* what it was
df_Cpx_Err_2Na['Na2O_Cpx_Err']=2*df_Cpx_Err['Na2O_Cpx_Err']
# And lets take sample 0 again
Cpx_1_Err_2Na=df_Cpx_Err_2Na.iloc[[0]].reset_index(drop=True)
# Lets simulate some new Cpxs.
Calc_Cpx_dist_2Na=pt.add_noise_sample_1phase(phase_comp=Cpx_1, phase_err=Cpx_1_Err_2Na,
                                             phase_err_type="Perc", duplicates=20000, err_dist="normal")
# And lets calculate pressures
Calc_PT_2Na=pt.calculate_cpx_only_press_all_eqs(cpx_comps=Calc_Cpx_dist_2Na)
All negative numbers replaced with zeros. If you wish to keep these, set positive=False

Lets plot these new errors vs. the old errors

  • Again, the contours can take ~30s to appear (with 16 Gb of RAM)

[ ]:
fig, (ax1, ax2)  = plt.subplots(1, 2, figsize=(10,5), sharex=True, sharey=True)


extent1=(3.5, 7, 1470, 1540)
ax1.hexbin(Calc_PT['P_Wang21_eq1'],
           Calc_PT['T_Wang21_eq2'],
            cmap=plt.cm.BuPu,
            linewidths=0.2, bins=10)
ax1.set_xlabel('Calc P (kbar)')
ax1.set_xlabel('Calc T (K)')




extent2=(3.5, 7, 1470, 1540)
ax2.hexbin(Calc_PT_2Na['P_Wang21_eq1'],
           Calc_PT_2Na['T_Wang21_eq2'],
            cmap=plt.cm.BuPu,
            linewidths=0.2, bins=10)
ax2.set_xlabel('Calc P (kbar)')
ax2.set_xlabel('Calc T (K)')


Calc_PT.loc[:,
["P_Wang21_eq1", "T_Wang21_eq2"]].pyroplot.density(ax=ax1, contours=[0.67, 0.95], colors=['m', 'c'],
                                                   extent=extent1, cmap="viridis",
                                                   bins=70, label_contours=False)

Calc_PT.loc[:,
["P_Wang21_eq1", "T_Wang21_eq2"]].pyroplot.density(ax=ax2, contours=[0.67, 0.95], colors=['m', 'c'],
                                                   extent=extent1, cmap="viridis",
                                                   bins=70, label_contours=False, width=0.5)

Calc_PT_2Na.loc[:,
["P_Wang21_eq1", "T_Wang21_eq2"]].pyroplot.density(ax=ax2, contours=[0.67, 0.95], colors=['r', 'blue'],
                                                   extent=extent2, cmap="viridis",
                                                   bins=70, label_contours=False)

ax1.errorbar(np.mean(Calc_PT['P_Wang21_eq1']),
             np.mean(Calc_PT['T_Wang21_eq2']),
             xerr=1.52, yerr=35.2,
             fmt='.', ecolor='k', elinewidth=0.8, mfc='k', ms=1)

ax2.errorbar(np.mean(Calc_PT['P_Wang21_eq1']),
             np.mean(Calc_PT['T_Wang21_eq2']),
             xerr=1.52, yerr=35.2,
             fmt='.', ecolor='k', elinewidth=0.8, mfc='k', ms=1)

ax1.set_xlim([3, 8])
ax1.set_ylim([1470,  1550])

ax1.set_xlabel('Calc P (kbar)')
ax1.set_ylabel('Calc T (K)')

ax2.set_xlabel('Calc P (kbar)')
ax2.set_ylabel('Calc T (K)')
ax2.yaxis.set_tick_params(which='both', labelbottom=True)
C:\Users\penny\anaconda3\lib\site-packages\pyrolite\util\plot\density.py:202: UserWarning: The following kwargs were not used by contour: 'width'
  cs = contour(
../../_images/Examples_Error_propagation_Cpx_only_contour_plot_30_1.png

Lets make a pretty figure for publication with just the stated errors for Wang and Putirka thermobarometers, but overlay an additional contour for bigger Na2O errors

[ ]:
# Wang thermobarometers
fig, (ax1)  = plt.subplots(1, figsize=(6,5), sharex=True, sharey=True)

## LHS, Wang et al. 2021
extent1=(3.5, 7, 1470, 1540)
ax1.hexbin(Calc_PT['P_Wang21_eq1'],
           Calc_PT['T_Wang21_eq2'],
            cmap=plt.cm.BuPu,
            linewidths=0.2, bins=10)
ax1.set_xlabel('Calc P (kbar)')
ax1.set_xlabel('Calc T (K)')


Calc_PT.loc[:,
["P_Wang21_eq1", "T_Wang21_eq2"]].pyroplot.density(ax=ax1, contours=[0.67, 0.95], colors=['m', 'c'],
                                                   extent=extent1, cmap="viridis",
                                                   bins=70, label_contours=False)


Calc_PT_2Na.loc[:,
["P_Wang21_eq1", "T_Wang21_eq2"]].pyroplot.density(ax=ax1, contours=[0.95], colors=['c'],
                                                   extent=extent2, cmap="viridis",
                                                   bins=70, label_contours=False,
                                                  linestyles=['-.'])

ax1.errorbar(np.mean(Calc_PT['P_Wang21_eq1']),
             np.mean(Calc_PT['T_Wang21_eq2']),
             xerr=1.52, yerr=35.2,
             fmt='.', ecolor='k', elinewidth=0.8, mfc='k', ms=1)


ax1.set_xlim([3, 8])
ax1.set_ylim([1450,  1530])

ax1.set_xlabel('Calc P (kbar)')
ax1.set_ylabel('Calc T (K)')

ax2.set_xlabel('Calc P (kbar)')
ax2.set_ylabel('Calc T (K)')

ax2.yaxis.set_tick_params(which='both', labelbottom=True)
fig.savefig('Cpx_only_Error.png', dpi=200)
../../_images/Examples_Error_propagation_Cpx_only_contour_plot_32_0.png
[ ]:
# Putirka Cpx-only barometers
fig, (ax1)  = plt.subplots(1, figsize=(6,5), sharex=True, sharey=True)

## LHS, Wang et al. 2021
extent1=(1, 10, 1430, 1540)
ax1.hexbin(Calc_PT['P_Put_Teq32d_Peq32b'],
           Calc_PT['T_Put_Teq32d_Peq32b'],
            cmap=plt.cm.BuPu,
            linewidths=0.2, bins=10)
ax1.set_xlabel('Calc P (kbar)')
ax1.set_xlabel('Calc T (K)')


Calc_PT.loc[:,
["P_Put_Teq32d_Peq32b", "T_Put_Teq32d_Peq32b"]].pyroplot.density(ax=ax1, contours=[0.67, 0.95], colors=['m', 'c'],
                                                   extent=extent1, cmap="viridis",
                                                   bins=70, label_contours=False)

extent2=(1, 10, 1430, 1540)

Calc_PT_2Na.loc[:,
["P_Put_Teq32d_Peq32b", "T_Put_Teq32d_Peq32b"]].pyroplot.density(ax=ax1, contours=[0.95], colors=['c'],
                                                   extent=extent2, cmap="viridis",
                                                   bins=70, label_contours=False,
                                                  linestyles=['-.'])

ax1.errorbar(np.mean(Calc_PT['P_Put_Teq32d_Peq32b']),
             np.mean(Calc_PT['T_Put_Teq32d_Peq32b']),
             xerr=2.6, yerr=58,
             fmt='.', ecolor='k', elinewidth=0.8, mfc='k', ms=1)


ax1.set_xlim([1.5, 8])
ax1.set_ylim([1430,  1520])

ax1.set_xlabel('Calc P (kbar)')
ax1.set_ylabel('Calc T (K)')

ax2.set_xlabel('Calc P (kbar)')
ax2.set_ylabel('Calc T (K)')

ax2.yaxis.set_tick_params(which='both', labelbottom=True)
fig.savefig('Cpx_only_Error_Put.png', dpi=200)
../../_images/Examples_Error_propagation_Cpx_only_contour_plot_33_0.png

Lets calculate the statistics for various noise simulations

[ ]:
Stats_P=pt.av_noise_samples_series(Calc_PT['P_Wang21_eq1'], Calc_PT['Sample_ID_Cpx_Num'])
Stats_P
Sample Mean_calc Median_calc St_dev_calc Max_calc Min_calc
0 0.0 5.212426 5.237335 0.576768 7.262507 2.351651
[ ]:
Stats_T=pt.av_noise_samples_series(Calc_PT['T_Wang21_eq2'], Calc_PT['Sample_ID_Cpx_Num'])
Stats_T
Sample Mean_calc Median_calc St_dev_calc Max_calc Min_calc
0 0.0 1493.025062 1493.198869 8.540826 1522.1718 1452.932404
[ ]:
Stats_T=pt.av_noise_samples_series(Calc_PT_2Na['T_Wang21_eq2'], Calc_PT_2Na['Sample_ID_Cpx_Num'])
Stats_T
Sample Mean_calc Median_calc St_dev_calc Max_calc Min_calc
0 0.0 1493.363786 1493.548499 9.11549 1529.575166 1456.638437
[ ]:
Stats_P=pt.av_noise_samples_series(Calc_PT_2Na['P_Wang21_eq1'], Calc_PT_2Na['Sample_ID_Cpx_Num'])
Stats_P
Sample Mean_calc Median_calc St_dev_calc Max_calc Min_calc
0 0.0 5.2346 5.242048 0.714344 8.348103 2.126887
[ ]:
Stats_P_Put=pt.av_noise_samples_series(Calc_PT_2Na['P_Put_Teq32d_Peq32b'], Calc_PT_2Na['Sample_ID_Cpx_Num'])
Stats_P_Put
Sample Mean_calc Median_calc St_dev_calc Max_calc Min_calc
0 0.0 5.013051 5.022533 0.944134 8.845332 0.948181
[ ]:
Stats_P_Put=pt.av_noise_samples_series(Calc_PT['P_Put_Teq32d_Peq32b'], Calc_PT['Sample_ID_Cpx_Num'])
Stats_P_Put
Sample Mean_calc Median_calc St_dev_calc Max_calc Min_calc
0 0.0 5.019986 5.025435 0.83673 8.116579 1.594801
[ ]:
Stats_P_Put=pt.av_noise_samples_series(Calc_PT['T_Put_Teq32d_Peq32b'], Calc_PT['Sample_ID_Cpx_Num'])
Stats_P_Put
Sample Mean_calc Median_calc St_dev_calc Max_calc Min_calc
0 0.0 1485.050547 1485.152212 9.505677 1518.411815 1446.638034
[ ]: