Example #1
0
    def calculate_dissolved_volatiles(self,pressure,sample,X_fluid=1.0,**kwargs):
        """Calculates the dissolved CO2 concentration using Eqn (3) of Dixon (1997).

        Parameters
        ----------
        pressure  float
            Total pressure in bars.
        sample      Sample class
            Magma major element composition.
        X_fluid      float
            The mol fraction of CO2 in the fluid.

        Returns
        -------
        float
            The CO2 concentration in wt%.
        """

        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")
        if pressure < 0:
            raise core.InputError("Pressure must be positive.")
        # if type(sample) != dict and type(sample) != pd.core.series.Series:
        #     raise core.InputError("sample must be a dict or pandas Series")
        if sample.check_oxide('SiO2') == False:
            raise core.InputError("sample must contain SiO2.")

        if pressure == 0:
            return 0

        XCO3 = self.molfrac_molecular(pressure=pressure,sample=sample,X_fluid=X_fluid,**kwargs)
        return (4400 * XCO3) / (36.594- 44*XCO3) # Following Dixon 1997 setting Mr as constant
Example #2
0
    def calculate_saturation_pressure(self, sample, **kwargs):
        """ Calculates the pressure at which a pure H2O fluid is saturated, for the given
        sample composition and H2O concentration. Calls the scipy.root_scalar routine, which makes
        repeated calls to the calculate_dissolved_volatiles method.

        Parameters
        ----------
        sample         Sample class
            Magma major element composition (including H2O).

        Returns
        -------
        float
            Saturation pressure in bar
        """
        if sample.check_oxide('H2O') == False:
            raise core.InputError("sample must contain H2O")
        if sample.get_composition('H2O') < 0:
            raise core.InputError(
                "H2O concentration must be greater than 0 wt%.")

        if sample.get_composition('H2O') < self.calculate_dissolved_volatiles(
                sample=sample, pressure=0, **kwargs):
            return np.nan

        try:
            satP = root_scalar(self.root_saturation_pressure,
                               bracket=[1e-15, 1e5],
                               args=(sample, kwargs)).root
        except:
            w.warn("Saturation pressure not found.",
                   RuntimeWarning,
                   stacklevel=2)
            satP = np.nan
        return satP
Example #3
0
    def calculate_saturation_pressure(self, sample, X_fluid=1.0, **kwargs):
        """
        Calculates the pressure at which a pure H2O fluid is saturated, for the given sample
        composition and H2O concentration. Calls the scipy.root_scalar routine, which makes
        repeated called to the calculate_dissolved_volatiles method.

        Parameters
        ----------
        sample      Sample class
            Magma major element composition (including H2O).
        X_fluid     float
            The mole fraction of H2O in the fluid. Default is 1.0.

        Returns
        -------
        float
            Calculated saturation pressure in bars.
        """
        if sample.check_oxide('H2O') is False:
            raise core.InputError("sample must contain H2O")
        if sample.get_composition('H2O') < 0:
            raise core.InputError(
                "H2O concentration must be greater than 0 wt%.")
        try:
            satP = root_scalar(self.root_saturation_pressure,
                               x0=100.0,
                               x1=1000.0,
                               args=(sample, kwargs)).root
        except Exception:
            w.warn("Saturation pressure not found.",
                   RuntimeWarning,
                   stacklevel=2)
            satP = np.nan
        return np.real(satP)
Example #4
0
    def calculate_dissolved_volatiles(self,pressure,sample,X_fluid=1.0,**kwargs):
        """Calculates the dissolved H2O concentration using Eqns (5) and (6) of Dixon (1997).

        Parameters
        ----------
        pressure  float
            Total pressure in bars.
        sample      Sample class
            Magma major element composition.
        X_fluid      float
            The mol fraction of H2O in the fluid.

        Returns
        -------
        float
            The H2O concentration in wt%.
        """
        if isinstance(sample,sample_class.Sample) == False:
            raise core.InputError("Sample must be an instance of the Sample class.")
        if sample.check_oxide('SiO2') == False:
            raise core.InputError("sample must contain SiO2.")
        if pressure < 0:
            raise core.InputError("Pressure must be positive")
        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")

        if pressure == 0:
            return 0

        XH2O = self.molfrac_molecular(pressure=pressure,sample=sample,X_fluid=X_fluid,**kwargs)
        XOH = self.XOH(pressure=pressure,sample=sample,X_fluid=X_fluid,**kwargs)

        XB = XH2O + 0.5*XOH
        return 1801.5*XB/(36.594-18.579*XB) # Following Dixon spreadsheet
Example #5
0
    def NBO_O(self, sample, coeffs='webapp'):
        """
        Calculates NBO/O according to Appendix A.1. of Iacono-Marziano et al. (2012). NBO/O
        is calculated on either a hydrous or anhyrous basis, as set when initialising the
        Model class.

        Parameters
        ----------
        sample     pandas Series or dict
            Major element oxides in wt% (including H2O if using the hydrous parameterization).

        coeffs  str
            One of:
            - 'webapp' or 'manuscript' to include H2O in NBO/O
            - 'anhydrous' to exclude H2O from NBO/O

        Returns
        -------
        float
            NBO/O.
        """
        if isinstance(sample, sample_class.Sample) is False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if all(
                sample.check_oxide(ox) for ox in [
                    'K2O', 'Na2O', 'CaO', 'MgO', 'FeO', 'Al2O3', 'SiO2', 'TiO2'
                ]) is False:
            raise core.InputError(
                "Sample must contain K2O, Na2O, CaO, MgO, FeO, Al2O3, SiO2, "
                "and TiO2.")

        X = sample.get_composition(units='mol_oxides',
                                   oxide_masses=self.IM_oxideMasses)

        if 'Fe2O3' in X:
            Fe2O3 = X['Fe2O3']
        else:
            Fe2O3 = 0

        NBO = 2 * (X['K2O'] + X['Na2O'] + X['CaO'] + X['MgO'] + X['FeO'] +
                   2 * Fe2O3 - X['Al2O3'])
        Ox = (2 * X['SiO2'] + 2 * X['TiO2'] + 3 * X['Al2O3'] + X['MgO'] +
              X['FeO'] + 2 * Fe2O3 + X['CaO'] + X['Na2O'] + X['K2O'])

        if coeffs == 'webapp' or coeffs == 'manuscript':
            if 'H2O' not in X:
                raise core.InputError(
                    "sample must contain H2O if using the hydrous"
                    " parameterization.")
            NBO = NBO + 2 * X['H2O']
            Ox = Ox + X['H2O']

        return NBO / Ox
Example #6
0
	def check_inputs(custom_H2O, custom_CO2):
		if custom_H2O is not None:
			if custom_CO2 is None:
				raise core.InputError("If x data is passed, y data must also be passed.")
			else:
				if len(custom_H2O) == len(custom_CO2):
					pass
				else:
					raise core.InputError("x and y data must be same length")
		if custom_CO2 is not None:
			if custom_H2O is None:
				raise core.InputError("If y data is passed, x data must also be passed.")
Example #7
0
    def calculate_saturation_pressure(self,
                                      temperature,
                                      sample,
                                      X_fluid=1.0,
                                      **kwargs):
        """
        Calculates the pressure at which a an CO2-bearing fluid is saturated. Calls the
        scipy.root_scalar routine, which makes repeated called to the
        calculate_dissolved_volatiles method.

        Parameters
        ----------
        sample:        Sample class
            Magma major element composition.

        temperature float
            Temperature in degrees C.

        X_fluid float
            OPTIONAL. Default is 0. Mole fraction of CO2 in the H2O-CO2 fluid.

        Returns
        -------
        float
            Calculated saturation pressure in bars.
        """

        temperatureK = temperature + 273.15
        if temperatureK <= 0.0:
            raise core.InputError("Temperature must be greater than 0K.")
        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")
        if isinstance(sample, sample_class.Sample) is False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if sample.check_oxide('CO2') is False:
            raise core.InputError("sample must contain CO2.")
        if sample.get_composition('CO2') < 0.0:
            raise core.InputError(
                "Dissolved CO2 concentration must be greater than 0 wt%.")

        try:
            satP = root_scalar(self.root_saturation_pressure,
                               args=(temperature, sample, X_fluid, kwargs),
                               x0=10.0,
                               x1=2000.0).root
        except Exception:
            w.warn("Saturation pressure not found.",
                   RuntimeWarning,
                   stacklevel=2)
            satP = np.nan
        return np.real(satP)
Example #8
0
    def calculate_dissolved_volatiles(self,
                                      pressure,
                                      sample,
                                      X_fluid=1.0,
                                      **kwargs):
        """Calculates the dissolved H2O concentration using Eqn (9) of Shishkina et al. (2014).

        Parameters
        ----------
        pressure     float
            Total pressure in bars
        sample         Sample class
            Magma major element composition.
        X_fluid     float
            The mol fraction of H2O in the fluid

        Returns
        -------
        float
            The H2O concentration in wt%
        """
        if isinstance(sample, sample_class.Sample) == False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")

        if sample.check_oxide('Na2O') == False or sample.check_oxide(
                'K2O') == False:
            raise core.InputError("Na2O and K2O must be present in sample.")

        if pressure < 0:
            raise core.InputError("Pressure must be positive.")

        _mols = sample.get_composition(units='mol_cations')
        _mol_volatiles = 0
        if 'H' in _mols:
            _mol_volatiles += _mols['H']
        if 'C' in _mols:
            _mol_volatiles += _mols['C']

        total_alkalis = (_mols['Na'] + _mols['K']) / (1 - _mol_volatiles)

        fugacity = self.fugacity_model.fugacity(pressure,
                                                X_fluid=X_fluid,
                                                **kwargs)

        a = 3.36e-7 * (fugacity / 10)**3 - 2.33e-4 * (
            fugacity / 10)**2 + 0.0711 * (fugacity / 10) - 1.1309
        b = -1.2e-5 * (fugacity / 10)**2 + 0.0196 * (fugacity / 10) + 1.1297

        return a * total_alkalis + b
Example #9
0
    def NBO_O(self, sample, hydrous_coeffs=True):
        """
        Calculates NBO/O according to Appendix A.1. of Iacono-Marziano et al. (2012). NBO/O
        is calculated on either a hydrous or anhyrous basis, as set when initialising the
        Model class.

        Parameters
        ----------
        sample     pandas Series or dict
            Major element oxides in wt% (including H2O if using the hydrous parameterization).

        Returns
        -------
        float
            NBO/O.
        """
        if isinstance(sample, sample_class.Sample) == False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if all(
                sample.check_oxide(ox) for ox in [
                    'K2O', 'Na2O', 'CaO', 'MgO', 'FeO', 'Al2O3', 'SiO2', 'TiO2'
                ]) == False:
            raise core.InputError(
                "sample must contain K2O, Na2O, CaO, MgO, FeO, Al2O3, SiO2, and TiO2."
            )

        # X = sample.get_composition(units='mol_oxides',normalization='additionalvolatiles')
        X = sample.get_composition(units='mol_oxides')

        if 'Fe2O3' in X:
            Fe2O3 = X['Fe2O3']
        else:
            Fe2O3 = 0

        NBO = 2 * (X['K2O'] + X['Na2O'] + X['CaO'] + X['MgO'] + X['FeO'] +
                   2 * Fe2O3 - X['Al2O3'])
        O = 2 * X['SiO2'] + 2 * X['TiO2'] + 3 * X['Al2O3'] + X['MgO'] + X[
            'FeO'] + 2 * Fe2O3 + X['CaO'] + X['Na2O'] + X['K2O']

        if hydrous_coeffs == True:
            if 'H2O' not in X:
                raise core.InputError(
                    "sample must contain H2O if using the hydrous parameterization."
                )
            NBO = NBO + 2 * X['H2O']
            O = O + X['H2O']

        return NBO / O
Example #10
0
def scatterplot(custom_x, custom_y, xlabel=None, ylabel=None, **kwargs):
    """
    Custom x-y plotting using VESIcal's built-in plot() function, built
    Matplotlib's plot and scatter functions.

    Parameters
    ----------
    custom_x: list
        List of groups of x-values to plot as points or lines

    custom_y: list
        List of groups of y-values to plot as points or lines

    xlabel: str
        OPTIONAL. What to display along the x-axis.

    ylabel: str
        OPTIONAL. What to display along the y-axis.

    kwargs:
        Can take in any key word agruments that can be passed to `plot()`.

    Returns
    -------
    fig, ax matplotlib objects
        X-y plot with custom x and y axis values and labels.
    """

    if isinstance(custom_x, list) and isinstance(custom_y, list):
        if len(custom_x) != len(custom_y):
            raise core.InputError("X and y lists must be same length")

    if xlabel is not None:
        if isinstance(xlabel, str):
            pass
        else:
            raise core.InputError("xlabel must be string")

    if ylabel is not None:
        if isinstance(ylabel, str):
            pass
        else:
            raise core.InputError("ylabel must be string")

    return plot(custom_x=custom_x,
                custom_y=custom_y,
                xlabel=xlabel,
                ylabel=ylabel,
                **kwargs)
Example #11
0
    def set_default_normalization(self, default_normalization):
        """ Set the default type of normalization to use with the get_composition() method.

        Parameters
        ----------
        default_normalization:    str
            The type of normalization to apply to the data. One of:
            - 'none' (no normalization)
            - 'standard' (default): Normalizes an input composition to 100%.
            - 'fixedvolatiles': Normalizes major element oxides to 100 wt%, including volatiles.
            The volatile wt% will remain fixed, whilst the other major element oxides are reduced
            proportionally so that the total is 100 wt%.
            - 'additionalvolatiles': Normalises major element oxide wt% to 100%, assuming it is
            volatile-free. If H2O or CO2 are passed to the function, their un-normalized values will
            be retained in addition to the normalized non-volatile oxides, summing to >100%.

        """
        if default_normalization in [
                'none', 'standard', 'fixedvolatiles', 'additionalvolatiles'
        ]:
            self.default_normalization = default_normalization
        else:
            raise core.InputError(
                "The normalization method must be one of 'none', 'standard', 'fixedvolatiles',\
             or 'additionalvolatiles'.")
Example #12
0
    def PiStar(self, sample):
        """Shishkina et al. (2014) Eq (11)

        Calculates the Pi* parameter for use in calculating CO2 solubility.

        Parameters
        ----------
        sample:        Sample class
            The magma composition stored in a Sample class.

        Returns
        -------
        float
            The value of the Pi* compositional parameter.
        """

        _mols = sample.get_composition(units='mol_cations')

        if all(cation in _mols for cation in
               ['Ca', 'K', 'Na', 'Mg', 'Fe', 'Si', 'Al']) == False:
            raise core.InputError(
                "To calculate PiStar, values for CaO, K2O, Na2O, MgO, FeO, SiO2, and Al2O3\
                                must be provided in sample.")

        _pi = (_mols['Ca'] + 0.8*_mols['K'] + 0.7*_mols['Na'] + 0.4*_mols['Mg'] + 0.4*_mols['Fe'])/\
                (_mols['Si']+_mols['Al'])

        return _pi
Example #13
0
    def calculate_saturation_pressure(self, temperature, sample, **kwargs):
        """
        Calculates the pressure at which a pure H2O fluid is saturated, for the given sample
        composition and H2O concentration. Calls the scipy.root_scalar routine, which makes
        repeated called to the calculate_dissolved_volatiles method.

        Parameters
        ----------
        temperature     float
            The temperature of the system in C.
        sample         pandas Series or dict
            Major element oxides in wt% (including H2O).
        X_fluid     float
            The mole fraction of H2O in the fluid. Default is 1.0.

        Returns
        -------
        float
            Calculated saturation pressure in bars.
        """

        if isinstance(sample, sample_class.Sample) is False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if sample.check_oxide('H2O') is False:
            raise core.InputError("sample must contain H2O.")
        if sample.get_composition('H2O') < 0.0:
            raise core.InputError("Dissolved H2O must be greater than 0 wt%.")

        try:
            # Checks whether the upper bound for the numerical solver returns a positive H2O
            # concentration, and if it doesn't, it will progressively decrease the bound until
            # it does.
            upperbound = 1e5
            while self.calculate_dissolved_volatiles(upperbound, temperature,
                                                     sample, **kwargs) < 0:
                upperbound = upperbound * 0.9

            satP = root_scalar(self.root_saturation_pressure,
                               args=(temperature, sample, kwargs),
                               bracket=[1e-15, upperbound]).root
        except Exception:
            w.warn("Saturation pressure not found.",
                   RuntimeWarning,
                   stacklevel=2)
            satP = np.nan
        return satP
Example #14
0
    def calculate_saturation_pressure(self,
                                      sample,
                                      temperature=1200,
                                      X_fluid=1.0,
                                      **kwargs):
        """
        Calculates the pressure at which a pure CO2 fluid is saturated, for the given sample
        composition and CO2 concentration. Calls the scipy.root_scalar routine, which makes
        repeated called to the calculate_dissolved_volatiles method.

        Parameters
        ----------
        temperature     float
            The temperature of the system in C.
        sample:        Sample class
            Magma major element composition (including CO2).
        X_fluid     float
            The mole fraction of H2O in the fluid. Default is 1.0.

        Returns
        -------
        float
            Calculated saturation pressure in bars.
        """

        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")
        if isinstance(sample, sample_class.Sample) is False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if sample.check_oxide('CO2') is False:
            raise core.InputError("sample must contain CO2.")
        if sample.get_composition('CO2') < 0.0:
            raise core.InputError(
                "Dissolved CO2 concentration must be greater than 0 wt%.")

        try:
            satP = root_scalar(self.root_saturation_pressure,
                               args=(temperature, sample, X_fluid, kwargs),
                               x0=1000.0,
                               x1=2000.0).root
        except Exception:
            w.warn("Saturation pressure not found.",
                   RuntimeWarning,
                   stacklevel=2)
            satP = np.nan
        return satP
Example #15
0
	def check_colors(custom_colors):
		if custom_colors == "VESIcal":
			use_colors = color_list
		elif isinstance(custom_colors, list):
			use_colors = custom_colors
		else:
			raise core.InputError("Argument custom_colors must be type list. Just passing one item? Try putting square brackets, [], around it.")
		return use_colors
Example #16
0
    def calculate_dissolved_volatiles(self,
                                      pressure,
                                      sample,
                                      X_fluid=1,
                                      **kwargs):
        """ Calculates the dissolved CO2 concentration in wt%, using equation (13) of Shishkina et al. (2014).

        Parameters
        ----------
        pressure:    float
            (Total) pressure in bars.
        sample:        Sample class
            Magma composition.
        X_fluid:    float
            The mol-fraction of the fluid that is CO2. Default is 1, i.e. a pure CO2 fluid.

        Returns
        -------
        float
            The dissolved CO2 concentration in wt%.
        """

        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")
        if pressure < 0:
            raise core.InputError("pressure must be a positive value.")

        PiStar = self.PiStar(sample)
        fugacity = self.fugacity_model.fugacity(pressure=pressure,
                                                X_fluid=X_fluid,
                                                **kwargs)

        A = 1.150
        B = 6.71
        C = -1.345

        if fugacity == 0:
            return 0
        else:
            return np.exp(A * np.log(fugacity / 10) + B * PiStar + C) / 1e4
Example #17
0
    def calculate_saturation_pressure(self, temperature, sample, **kwargs):
        """
        Calculates the pressure at which a pure CO2 fluid is saturated, for the given sample
        composition and CO2 concentration. Calls the scipy.root_scalar routine, which makes
        repeated called to the calculate_dissolved_volatiles method.

        Parameters
        ----------
        temperature     float
            The temperature of the system in C.
        sample         Sample class
            Magma major element composition (including CO2).

        Returns
        -------
        float
            Calculated saturation pressure in bars.
        """

        if temperature <= 0:
            raise core.InputError("Temperature must be greater than 0K.")
        if isinstance(sample, sample_class.Sample) is False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if sample.check_oxide('CO2') is False:
            raise core.InputError("sample must contain CO2")
        if sample.get_composition('CO2') < 0:
            raise core.InputError("Dissolved CO2 must be greater than 0 wt%.")

        try:
            satP = root_scalar(self.root_saturation_pressure,
                               args=(temperature, sample, kwargs),
                               bracket=[1e-15, 1e5]).root
        except Exception:
            w.warn("Saturation pressure not found.",
                   RuntimeWarning,
                   stacklevel=2)
            satP = np.nan
        return satP
Example #18
0
    def _normalize_FixedVolatiles(self, composition, units='wtpt_oxides'):
        """
        Normalizes major element oxides to 100 wt%, including volatiles. The volatile
        wt% will remain fixed, whilst the other major element oxides are reduced proportionally
        so that the total is 100 wt%.

        Intended to be called only by the get_composition() method.

        Parameters
        ----------
        composition:     pandas Series
            Major element composition

        units:      str
            The units of composition. Should be one of:
            - wtpt_oxides (default)
            - mol_oxides
            - mol_cations

        Returns
        -------
        pandas Series
            Normalized major element oxides.
        """
        comp = composition.copy()
        normalized = pd.Series({}, dtype=float)
        volatiles = 0
        if 'CO2' in list(comp.index):
            volatiles += comp['CO2']
        if 'H2O' in list(comp.index):
            volatiles += comp['H2O']

        for ox in list(comp.index):
            if ox != 'H2O' and ox != 'CO2':
                normalized[ox] = comp[ox]

        if units == 'wtpt_oxides':
            normalized = normalized / np.sum(normalized) * (100 - volatiles)
        elif units == 'mol_oxides' or units == 'mol_cations':
            normalized = normalized / np.sum(normalized) * (1 - volatiles)
        else:
            raise core.InputError(
                "Units must be one of 'wtpt_oxides', 'mol_oxides', or 'mol_cations'."
            )

        if 'CO2' in list(comp.index):
            normalized['CO2'] = comp['CO2']
        if 'H2O' in list(comp.index):
            normalized['H2O'] = comp['H2O']

        return normalized
Example #19
0
    def __init__(self,
                 sample,
                 model='MagmaSat',
                 silence_warnings=False,
                 **kwargs):
        """
        Initializes the calculation.

        Parameters
        ----------
        sample:    Sample class
            The rock composition as a Sample object.
        model:     string or Model class
            Which model to use for the calculation. If passed a string, it
            will look up the name in the default_models dictionary. Default is
            MagmaSat.
        silence_warnings:     bool
            Silence warnings about calibration ranges. Default is False.
        preprocess_sample:     bool
            Before running the calculation, run the sample through the
            preprocessing routine. As of Feb 2021 this functionality should be
            redundant.
        """
        self.model_name = model
        if model == 'MagmaSat':
            self.model = magmasat.MagmaSat()
        elif type(model) == str:
            if model in models.default_models.keys():
                self.model = models.default_models[model]
            else:
                raise core.InputError("The model name given is not recognised."
                                      " Run the method get_model_names() to "
                                      "find allowed names.")
        else:
            self.model = model

        self.sample = sample

        self.result = self.calculate(sample=self.sample, **kwargs)
        self.calib_check = self.check_calibration_range(sample=self.sample,
                                                        **kwargs)

        if self.calib_check is not None and silence_warnings is False:
            if self.calib_check != '':
                w.warn(self.calib_check, RuntimeWarning)
Example #20
0
    def _normalize_AdditionalVolatiles(self, composition, units='wtpt_oxides'):
        """
        Normalises major element oxide wt% to 100%, assuming it is volatile-free. If
        H2O or CO2 are passed to the function, their un-normalized values will be retained
        in addition to the normalized non-volatile oxides, summing to >100%.

        Intended to be called only by the get_composition() method.

        Parameters
        ----------
        composition:     pandas.Series
            Major element composition

        units:      str
            The units of composition. Should be one of:
            - wtpt_oxides (default)
            - mol_oxides
            - mol_cations

        Returns
        -------
        pandas.Series
            Normalized major element oxides.
        """
        comp = composition.copy()
        normalized = pd.Series({}, dtype=float)
        for ox in list(comp.index):
            if ox != 'H2O' and ox != 'CO2':
                normalized[ox] = comp[ox]

        if units == 'wtpt_oxides':
            normalized = normalized / np.sum(normalized) * 100
        elif units == 'mol_oxides' or units == 'mol_cations':
            normalized = normalized / np.sum(normalized)
        else:
            raise core.InputError(
                "Units must be one of 'wtpt_oxides', 'mol_oxides', or 'mol_cations'."
            )

        if 'H2O' in comp.index:
            normalized['H2O'] = comp['H2O']
        if 'CO2' in comp.index:
            normalized['CO2'] = comp['CO2']

        return normalized
Example #21
0
 def calculate(self,
               sample,
               pressure='saturation',
               fractionate_vapor=0.0,
               final_pressure=100.0,
               **kwargs):
     check = getattr(self.model, "calculate_degassing_path", None)
     if callable(check):
         data = self.model.calculate_degassing_path(
             sample=sample,
             pressure=pressure,
             fractionate_vapor=fractionate_vapor,
             final_pressure=final_pressure,
             **kwargs)
         return data
     else:
         raise core.InputError(
             "This model does not have a calculate_isobars_and_isopleths method built in, most likely because it is a pure fluid model."
         )
Example #22
0
    def set_default_units(self, default_units):
        """ Set the default units of composition to return when using the get_composition() method.

        Parameters
        ----------
        default_units     str
            The type of composition to return, one of:
            - wtpt_oxides (default)
            - mol_oxides
            - mol_cations
            - mol_singleO
        """
        if default_units in [
                'wtpt_oxides', 'mol_oxides', 'mol_cations', 'mol_singleO'
        ]:
            self.default_units = default_units
        else:
            raise core.InputError(
                "The units must be one of 'wtpt_oxides','mol_oxides','mol_cations','mol_singleO'."
            )
Example #23
0
 def calculate(self,
               sample,
               pressure_list,
               isopleth_list=[0, 1],
               points=101,
               **kwargs):
     check = getattr(self.model, "calculate_isobars_and_isopleths", None)
     if callable(check):
         # samplenorm = sample.copy()
         # samplenorm = normalize_AdditionalVolatiles(samplenorm)
         isobars, isopleths = self.model.calculate_isobars_and_isopleths(
             sample=self.sample,
             pressure_list=pressure_list,
             isopleth_list=isopleth_list,
             points=points,
             **kwargs)
         return isobars, isopleths
     else:
         raise core.InputError(
             "This model does not have a calculate_isobars_and_isopleths method built in, most likely because it is a pure fluid model."
         )
Example #24
0
    def _normalize_Standard(self, composition, units='wtpt_oxides'):
        """
        Normalizes the given composition to 100 wt%, including volatiles. This method
        is intended only to be called by the get_composition() method.

        Parameters
        ----------
        composition:     pandas.Series
            A rock composition with oxide names as keys and concentrations as values.

        units:      str
            The units of composition. Should be one of:
            - wtpt_oxides (default)
            - mol_oxides
            - mol_cations

        Returns
        -------
        pandas.Series
            Normalized oxides in wt%.
        """
        comp = composition.copy()
        comp = dict(comp)

        if units == 'wtpt_oxides':
            normed = pd.Series(
                {k: 100.0 * v / sum(comp.values())
                 for k, v in comp.items()})
        elif units == 'mol_oxides' or units == 'mol_cations':
            normed = pd.Series(
                {k: v / sum(comp.values())
                 for k, v in comp.items()})
        else:
            raise core.InputError(
                "Units must be one of 'wtpt_oxides', 'mol_oxides', or 'mol_cations'."
            )

        return normed
Example #25
0
    def change_composition(self,
                           new_composition,
                           units='wtpt_oxides',
                           inplace=True):
        """
        Change the concentration of some component of the composition.

        If the units are moles, they are read as moles relative to the present composition,
        i.e. if you wish to double the moles of MgO, if the present content is 0.1 moles,
        you should provide {'MgO':0.2}. The composition will then be re-normalized. If the
        original composition was provided in un-normalized wt%, the unnormalized total will
        be lost.

        Parameters
        ----------
        new_composition:    dict or pandas.Series
            The components to be updated.
        units:      str
            The units of new_composition. Should be one of:
            - wtpt_oxides (default)
            - mol_oxides
            - mol_cations
        inplace:    bool
            If True the object will be modified in place. If False, a copy of the Sample
            object will be created, modified, and then returned.

        Returns
        -------
        Sample class
            Modified Sample class.
        """

        # if new_composition is pandas.Series, convert to dict
        if isinstance(new_composition, pd.Series):
            new_composition = dict(new_composition)

        if inplace == False:
            newsample = deepcopy(self)
            return newsample.change_composition(new_composition, units=units)

        if units == 'wtpt_oxides':
            for ox in new_composition:
                self._composition[ox] = new_composition[ox]

        elif units == 'mol_oxides':
            _comp = self.get_composition(units='mol_oxides')
            for ox in new_composition:
                _comp[ox] = new_composition[ox]
            self._composition = self._molOxides_to_wtpercentOxides(_comp)

        elif units == 'mol_cations':
            _comp = self.get_composition(units='mol_cations')
            for el in new_composition:
                _comp[el] = new_composition[el]
            self._composition = self._molCations_to_wtpercentOxides(_comp)

        else:
            raise core.InputError(
                "Units must be one of 'wtpt_oxides', 'mol_oxides', or 'mol_cations'."
            )

        return self
Example #26
0
    def __init__(self,
                 composition,
                 units='wtpt_oxides',
                 default_normalization='none',
                 default_units='wtpt_oxides'):
        """ Initialises the sample class.

        The composition is stored as wtpt. If the composition
        is provided as wtpt, no normalization will be applied. If the composition is supplied as
        mols, the composition will be normalized to 100 wt%.

        Parameters
        ----------
        composition     dict or pandas.Series
            The composition of the sample in the format specified by the composition_type
            parameter. Default is oxides in wtpt.

        units     str
            Specifies the units and type of compositional information passed in the
            composition parameter. Choose from 'wtpt_oxides', 'mol_oxides', 'mol_cations'.

        default_normalization:     None or str
            The type of normalization to apply to the data by default. One of:
                - None (no normalization)
                - 'standard' (default): Normalizes an input composition to 100%.
                - 'fixedvolatiles': Normalizes major element oxides to 100 wt%, including volatiles.
                The volatile wt% will remain fixed, whilst the other major element oxides are reduced
                proportionally so that the total is 100 wt%.
                - 'additionalvolatiles': Normalises major element oxide wt% to 100%, assuming it is
                volatile-free. If H2O or CO2 are passed to the function, their un-normalized values will
                be retained in addition to the normalized non-volatile oxides, summing to >100%.

        default_units     str
            The type of composition to return by default, one of:
            - wtpt_oxides (default)
            - mol_oxides
            - mol_cations
            - mol_singleO
        """

        composition = deepcopy(composition)

        if isinstance(composition, dict):
            composition = pd.Series(composition, dtype='float64')
        elif isinstance(composition, pd.Series) == False:
            raise core.InputError(
                "The composition must be given as either a dictionary or a pandas Series."
            )

        if units == 'wtpt_oxides':
            self._composition = composition
        elif units == 'mol_oxides':
            self._composition = self._molOxides_to_wtpercentOxides(composition)
        elif units == 'mol_cations':
            self._composition = self._molCations_to_wtpercentOxides(
                composition)
        else:
            raise core.InputError(
                "Units must be one of 'wtpt_oxides', 'mol_oxides', or 'mol_cations'."
            )

        self.set_default_normalization(default_normalization)
        self.set_default_units(default_units)
Example #27
0
    def get_composition(self,
                        species=None,
                        normalization=None,
                        units=None,
                        exclude_volatiles=False,
                        asSampleClass=False):
        """ Returns the composition in the format requested, normalized as requested.

        Parameters
        ----------
        species:    NoneType or str
            The name of the oxide or cation to return the concentration of. If NoneType (default) the
            whole composition will be returned as a pandas.Series. If an oxide is passed, the value in
            wtpt will be returned unless units is set to 'mol_oxides', even if the default units for the
            sample object are mol_oxides. If an element is passed, the concentration will be returned as
            mol_cations, unless 'mol_singleO' is specified as units, even if the default units for the
            sample object are mol_singleO. Unless normalization is specified in the method call, none
            will be applied.
        normalization:     NoneType or str
            The type of normalization to apply to the data. One of:
            - 'none' (no normalization)
            - 'standard' (default): Normalizes an input composition to 100%.
            - 'fixedvolatiles': Normalizes major element oxides to 100 wt%, including volatiles.
            The volatile wt% will remain fixed, whilst the other major element oxides are reduced
            proportionally so that the total is 100 wt%.
            - 'additionalvolatiles': Normalises major element oxide wt% to 100%, assuming it is
            volatile-free. If H2O or CO2 are passed to the function, their un-normalized values will
            be retained in addition to the normalized non-volatile oxides, summing to >100%.

            If NoneType is passed the default normalization option will be used (self.default_normalization).

        units:     NoneType or str
            The units of composition to return, one of:
            - wtpt_oxides (default)
            - mol_oxides
            - mol_cations
            - mol_singleO

            If NoneType is passed the default units option will be used (self.default_type).

        exclude_volatiles   bool
            If True, volatiles will be excluded from the returned composition, prior to normalization and
            conversion.

        asSampleClass:  bool
            If True, the sample composition will be returned as a sample class, with default options. In this case
            any normalization instructions will be ignored.

        Returns
        -------
        pandas.Series, float, or Sample class
            The sample composition, as specified.
        """

        # Fetch the default return types if not specified in function call
        if normalization == None and species == None:
            normalization = self.default_normalization
        if units == None and species == None:
            units = self.default_units

        # Check whether to exclude volatiles
        # note that here composition is gotten as wtpt_oxides
        if exclude_volatiles == True:
            composition = self._composition.copy()
            if 'H2O' in composition.index:
                composition = composition.drop(index='H2O')
            if 'CO2' in composition.index:
                composition = composition.drop(index='CO2')
        else:
            composition = self._composition.copy()

        # Check for a species being provided, if so, work out which units to return.
        if isinstance(species, str):
            if species in composition.index:  # if the requested species has a value, proceed
                if species in core.oxides:
                    if units in ['mol_cations, mol_singleO'] or units == None:
                        units = 'wtpt_oxides'
                elif species in core.cations_to_oxides:
                    if units in ['wtpt_oxides', 'mol_oxides'] or units == None:
                        units = 'mol_cations'
                else:
                    raise core.InputError(
                        species +
                        " was not recognised, check spelling, capitalization and stoichiometry."
                    )
                if normalization == None:
                    normalization = 'none'
            else:
                return 0.0  # if the requested species has no set value, return a float of 0.0
        elif species != None:
            raise core.InputError(
                "Species must be either a string or a NoneType.")

        # Get the requested type of composition
        if units == 'wtpt_oxides':
            converted = composition
        elif units == 'mol_oxides':
            converted = self._wtpercentOxides_to_molOxides(composition)
        elif units == 'mol_cations':
            converted = self._wtpercentOxides_to_molCations(composition)
        elif units == 'mol_singleO':
            converted = self._wtpercentOxides_to_molSingleO(composition)
        else:
            raise core.InputError(
                "The units must be one of 'wtpt_oxides', 'mol_oxides', 'mol_cations', \
            or 'mol_singleO'.")

        # Do requested normalization
        if normalization == 'none':
            final = converted
        elif normalization == 'standard':
            final = self._normalize_Standard(converted, units=units)
        elif normalization == 'fixedvolatiles':
            final = self._normalize_FixedVolatiles(converted, units=units)
        elif normalization == 'additionalvolatiles':
            final = self._normalize_AdditionalVolatiles(converted, units=units)
        else:
            raise core.InputError(
                "The normalization method must be one of 'none', 'standard', 'fixedvolatiles',\
             or 'additionalvolatiles'.")

        if species == None:
            if asSampleClass == False:
                return final
            else:
                return Sample(final)
        elif isinstance(species, str):
            if asSampleClass == True:
                w.warn(
                    "Cannot return single species as Sample class. Returning as float.",
                    RuntimeWarning,
                    stacklevel=2)
            return final[species]
Example #28
0
    def calculate_dissolved_volatiles(self,
                                      pressure,
                                      temperature,
                                      sample,
                                      X_fluid=1,
                                      coeffs='webapp',
                                      **kwargs):
        """
        Calculates the dissolved CO2 concentration, using Eq (12) of Iacono-Marziano et al. (2012).
        If using the hydrous parameterization, it will use the scipy.root_scalar routine to find
        the root of the root_dissolved_volatiles method.

        Parameters
        ----------
        pressure    float
            Total pressure in bars.
        temperature     float
            Temperature in C
        sample     Sample class
            Magma major element composition.
        X_fluid      float
            Mole fraction of H2O in the fluid. Default is 1.0.
        coeffs  str
            Which set of coefficients should be used for H2O calculations:
            - 'webapp' (default) for the hydrous NBO/O parameterisation coefficients used in the
              Iacono-Marziano webapp.
            - 'manuscript' for the hydrous NBO/O parameterisation coefficients given in the
              Iacono-Marziano et al. (2012) manuscript.
            - 'anhydrous' for the anhydrous NBO/O parameterisation coefficients given in the
              Iacono-Marziano et al. (2012) manuscript.

        Returns
        -------
        float
            Dissolved H2O concentration in wt%.
        """

        if coeffs not in ['webapp', 'manuscript', 'anhydrous']:
            raise core.InputError(
                "The coeffs argument must be one of 'webapp', 'manuscript', "
                "or 'anhydrous'")

        temperature = temperature + 273.15  # translate T from C to K

        if isinstance(sample, sample_class.Sample) is False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if pressure < 0:
            raise core.InputError("Pressure must be positive.")
        if temperature <= 0:
            raise core.InputError("Temperature must be greater than 0K.")
        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")

        if pressure == 0:
            return 0

        if coeffs == 'webapp' or coeffs == 'manuscript':
            im_h2o_model = water()
            h2o = im_h2o_model.calculate_dissolved_volatiles(
                pressure=pressure,
                temperature=temperature - 273.15,
                sample=sample,
                X_fluid=1 - X_fluid,
                coeffs=coeffs,
                **kwargs)

            sample_h2o = sample.change_composition({'H2O': h2o}, inplace=False)

            d = np.array([-16.4, 4.4, -17.1, 22.8])
            a = 1.0
            b = 17.3
            B = -6.0
            C = 0.12

            NBO_O = self.NBO_O(sample=sample_h2o, coeffs=coeffs)

        else:
            im_h2o_model = water()
            h2o = im_h2o_model.calculate_dissolved_volatiles(
                pressure=pressure,
                temperature=temperature - 273.15,
                sample=sample,
                X_fluid=1 - X_fluid,
                coeffs=coeffs,
                **kwargs)

            sample_h2o = sample.change_composition({'H2O': h2o}, inplace=False)

            d = np.array([2.3, 3.8, -16.3, 20.1])
            a = 1.0
            b = 15.8
            B = -5.3
            C = 0.14

            NBO_O = self.NBO_O(sample=sample, coeffs=coeffs)

        fugacity = self.fugacity_model.fugacity(pressure=pressure,
                                                X_fluid=X_fluid,
                                                temperature=temperature -
                                                273.15,
                                                **kwargs)

        if fugacity == 0:
            return 0

        molarProps = sample_h2o.get_composition(
            units='mol_oxides', oxide_masses=self.IM_oxideMasses)

        if all(ox in molarProps for ox in [
                'Al2O3', 'CaO', 'K2O', 'Na2O', 'FeO', 'MgO', 'Na2O', 'K2O'
        ]) is False:
            raise core.InputError(
                "sample must contain Al2O3, CaO, K2O, Na2O, FeO, MgO, Na2O, "
                "and K2O.")
        if 'Fe2O3' in molarProps:
            Fe2O3 = molarProps['Fe2O3']
        else:
            Fe2O3 = 0

        x = list()
        if 'H2O' in molarProps:
            x.append(molarProps['H2O'])
        else:
            x.append(0.0)
        x.append(molarProps['Al2O3'] /
                 (molarProps['CaO'] + molarProps['K2O'] + molarProps['Na2O']))
        x.append((molarProps['FeO'] + Fe2O3 * 2 + molarProps['MgO']))
        x.append((molarProps['Na2O'] + molarProps['K2O']))
        x = np.array(x)

        CO3 = np.exp(
            np.sum(x * d) + a * np.log(fugacity) + b * NBO_O + B +
            C * pressure / temperature)
        CO2 = CO3 / 1e4

        return CO2
Example #29
0
    def calculate_dissolved_volatiles(self,
                                      pressure,
                                      temperature,
                                      sample,
                                      X_fluid=1.0,
                                      coeffs='webapp',
                                      **kwargs):
        """
        Calculates the dissolved H2O concentration, using Eq (13) of Iacono-Marziano et al. (2012).
        If using the hydrous parameterization, it will use the scipy.root_scalar routine to find
        the root of the root_dissolved_volatiles method.

        Parameters
        ----------
        pressure    float
            Total pressure in bars.
        temperature     float
            Temperature in C
        sample     pandas Series or dict
            Major element oxides in wt%.
        X_fluid      float
            Mole fraction of H2O in the fluid. Default is 1.0.
        coeffs  str
            Which set of coefficients should be used in the calculations:
            - 'webapp' (default) for the hydrous NBO/O parameterisation coefficients used in
              the Iacono-Marziano webapp.
            - 'manuscript' for the hydrous NBO/O parameterisation coefficients given in the
              Iacono-Marziano et al. (2012) manuscript, but were incorrect (pers.comm.).
            - 'anhydrous' for the anhydrous NBO/O parameterisation coefficients given in the
              Iacono-Marziano et al. (2012) manuscript.

        Returns
        -------
        float
            Dissolved H2O concentration in wt%.
        """

        if coeffs not in ['webapp', 'manuscript', 'anhydrous']:
            raise core.InputError(
                "The coeffs argument must be one of 'webapp', 'manuscript', "
                "or 'anhydrous'")

        temperature = temperature + 273.15  # translate T from C to K

        if isinstance(sample, sample_class.Sample) is False:
            raise core.InputError(
                "Sample must be an instance of the Sample class.")
        if pressure < 0:
            raise core.InputError("Pressure must be positive.")
        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")

        if pressure == 0:
            return 0

        if coeffs == 'webapp' or coeffs == 'manuscript':
            if X_fluid == 0:
                return 0
            H2O = root_scalar(self.root_dissolved_volatiles,
                              args=(pressure, temperature, sample, X_fluid,
                                    coeffs, kwargs),
                              x0=1.0,
                              x1=2.0).root
            return H2O
        else:
            a = 0.54
            b = 1.24
            B = -2.95
            C = 0.02

            fugacity = self.fugacity_model.fugacity(pressure=pressure,
                                                    X_fluid=X_fluid,
                                                    temperature=temperature -
                                                    273.15,
                                                    **kwargs)
            if fugacity == 0:
                return 0
            NBO_O = self.NBO_O(sample=sample, coeffs=coeffs)

            H2O = np.exp(a * np.log(fugacity) + b * NBO_O + B +
                         C * pressure / temperature)

            return H2O
Example #30
0
    def calculate_dissolved_volatiles(self,
                                      pressure,
                                      temperature=1200,
                                      sample=None,
                                      X_fluid=1.0,
                                      **kwargs):
        """
        Calclates the dissolved CO2 concentration using (Eqns) 2-7 or 10-11 from Allison et al.
        (2019).

        Parameters
        ----------
        pressure     float
            Pressure in bars.
        temperature     float
            Temperature in C.
        sample      NoneType or Sample class
            Magma major element composition. Not required for this model, therefore None may be
            passed.
        X_fluid     float
            The mole fraction of CO2 in the fluid. Default is 1.0.

        Returns
        -------
        float
            Dissolved CO2 concentration in wt%.
        """
        # temperature = 1200 #temp in degrees C
        temperature = temperature + 273.15  # translate T from C to K

        if pressure < 0.0:
            raise core.InputError("Pressure must be positive.")
        if X_fluid < 0 or X_fluid > 1:
            raise core.InputError("X_fluid must have a value between 0 and 1.")

        if self.model_fit not in ['power', 'thermodynamic']:
            raise core.InputError(
                "model_fit must be one of 'power', or 'thermodynamic'.")
        if self.model_loc not in [
                'sunset', 'sfvf', 'erebus', 'vesuvius', 'etna', 'stromboli'
        ]:
            raise core.InputError(
                "model_loc must be one of 'sunset', 'sfvf', 'erebus', ",
                "'vesuvius', 'etna', or 'stromboli'.")

        if pressure == 0:
            return 0

        if self.model_fit == 'thermodynamic':
            P0 = 1000  # bar
            params = dict({
                'sunset': [16.4, -14.67],
                'sfvf': [15.02, -14.87],
                'erebus': [15.83, -14.65],
                'vesuvius': [24.42, -14.04],
                'etna': [21.59, -14.28],
                'stromboli': [14.93, -14.68]
            })

            DV = params[self.model_loc][0]
            lnK0 = params[self.model_loc][1]

            lnK = lnK0 - (pressure - P0) * DV / (10 * 8.3141 * temperature)
            fCO2 = self.fugacity_model.fugacity(pressure=pressure,
                                                temperature=temperature -
                                                273.15,
                                                X_fluid=X_fluid,
                                                **kwargs)
            Kf = np.exp(lnK) * fCO2
            XCO3 = Kf / (1 - Kf)

            FWone = 36.594
            wtCO2 = (44.01 * XCO3) / ((44.01 * XCO3) +
                                      (1 - XCO3) * FWone) * 100
            return wtCO2

        if self.model_fit == 'power':
            params = dict({
                'stromboli': [1.05, 0.883],
                'etna': [2.831, 0.797],
                'vesuvius': [4.796, 0.754],
                'sfvf': [3.273, 0.74],
                'sunset': [4.32, 0.728],
                'erebus': [5.145, 0.713]
            })

            fCO2 = self.fugacity_model.fugacity(pressure=pressure,
                                                temperature=temperature -
                                                273.15,
                                                X_fluid=X_fluid,
                                                **kwargs)

            return params[self.model_loc][0] * fCO2**params[
                self.model_loc][1] / 1e4