Ejemplo n.º 1
0
    def to_dict(self):
        """Represents object as dictionary with JSON-accepted datatypes

        Returns
        -------
            obj_dict : dict
        """
        obj_dict = {
            'class': str(self.__class__),
            'name': self.name,
            'trans_model': self.trans_model.to_dict(),
            'vib_model': self.vib_model.to_dict(),
            'rot_model': self.rot_model.to_dict(),
            'elec_model': self.elec_model.to_dict(),
            'nucl_model': self.nucl_model.to_dict(),
            'smiles': self.smiles,
            'notes': self.notes
        }

        if _is_iterable(self.mix_models):
            obj_dict['mix_models'] = \
                    [mix_model.to_dict() for mix_model in self.mix_models]
        else:
            obj_dict['mix_models'] = self.mix_models

        return obj_dict
Ejemplo n.º 2
0
    def to_dict(self):
        """Represents object as dictionary with JSON-accepted datatypes

        Returns
        -------
            obj_dict : dict
        """
        obj_dict = {
            'class': str(self.__class__),
            'type': 'empiricalbase',
            'name': self.name,
            'phase': self.phase,
            'elements': self.elements,
            'notes': self.notes,
            'smiles': self.smiles,
        }
        try:
            obj_dict['references'] = self.references.to_dict()
        except AttributeError:
            obj_dict['references'] = self.references

        try:
            obj_dict['statmech_model'] = self.statmech_model.to_dict()
        except AttributeError:
            obj_dict['statmech_model'] = self.statmech_model

        if _is_iterable(self.mix_models):
            obj_dict['mix_models'] = \
                    [mix_model.to_dict() for mix_model in self.mix_models]
        else:
            obj_dict['mix_models'] = self.mix_models
        return obj_dict
Ejemplo n.º 3
0
    def __init__(self,
                 name=None,
                 phase=None,
                 elements=None,
                 statmech_model=None,
                 references=None,
                 mix_models=None,
                 smiles=None,
                 notes=None,
                 **kwargs):
        self.name = name
        self.phase = phase
        self.elements = elements
        self.references = references
        self.smiles = smiles
        self.notes = notes

        # Assign self.statmech_model
        if inspect.isclass(statmech_model):
            # If you're passing a class. Note that the required
            # arguments will be guessed.
            self.statmech_model = statmech_model(**kwargs)
        else:
            # If it's an object that has already been initialized
            self.statmech_model = statmech_model

        # Assign mixing models
        # TODO Mixing models can not be initialized by passing the class
        # because all the models will have the same attributes. Figure out a
        # way to pass them. Perhaps have a dictionary that contains the
        # attributes separated by species
        if not _is_iterable(mix_models) and mix_models is not None:
            mix_models = [mix_models]
        self.mix_models = mix_models
Ejemplo n.º 4
0
    def __init__(self,
                 name=None,
                 trans_model=EmptyMode(),
                 vib_model=EmptyMode(),
                 rot_model=EmptyMode(),
                 elec_model=EmptyMode(),
                 nucl_model=EmptyMode(),
                 mix_models=None,
                 smiles=None,
                 notes=None,
                 **kwargs):
        self.name = name
        self.smiles = smiles
        self.notes = notes

        # Translational modes
        if inspect.isclass(trans_model):
            self.trans_model = _pass_expected_arguments(trans_model, **kwargs)
        else:
            self.trans_model = trans_model

        # Vibrational modes
        if inspect.isclass(vib_model):
            self.vib_model = _pass_expected_arguments(vib_model, **kwargs)
        else:
            self.vib_model = vib_model

        # Rotational modes
        if inspect.isclass(rot_model):
            self.rot_model = _pass_expected_arguments(rot_model, **kwargs)
        else:
            self.rot_model = rot_model

        # Electronic modes
        if inspect.isclass(elec_model):
            self.elec_model = _pass_expected_arguments(elec_model, **kwargs)
        else:
            self.elec_model = elec_model

        # Nuclear modes
        if inspect.isclass(nucl_model):
            self.nucl_model = _pass_expected_arguments(nucl_model, **kwargs)
        else:
            self.nucl_model = nucl_model

        # Assign mixing models
        # TODO Mixing models can not be initialized by passing the class
        # because all the models will have the same attributes. Figure out
        # a way to pass them. Perhaps have a dictionary that contains the
        # attributes separated by species
        if not _is_iterable(mix_models) and mix_models is not None:
            mix_models = [mix_models]
        self.mix_models = mix_models
Ejemplo n.º 5
0
    def get_CpoR(self, T, raise_error=True, raise_warning=True, **kwargs):
        """Calculate the dimensionless heat capacity

        Parameters
        ----------
            T : float or (N,) `numpy.ndarray`_
                Temperature(s) in K
            raise_error : bool, optional
                If True, raises an error if any of the modes do not have the
                quantity of interest. Default is True
            raise_warning : bool, optional
                Only relevant if raise_error is False. Raises a warning if any
                of the modes do not have the quantity of interest. Default is
                True
            kwargs : key-word arguments
                Arguments to calculate mixture model properties, if any
        Returns
        -------
            CpoR : float or (N,) `numpy.ndarray`_
                Dimensionless heat capacity

        .. _`numpy.ndarray`: https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.ndarray.html
        """
        # Convert T to 1D numpy format
        if not _is_iterable(T):
            T = [T]
        T = np.array(T)

        # Calculate pure properties
        CpoR = get_shomate_CpoR(a=self.a, T=T)
        # Calculate mixing properties
        for T_i in T:
            CpoR_mix = _get_mix_quantity(mix_models=self.mix_models,
                                         method_name='get_CpoR',
                                         raise_error=raise_error,
                                         raise_warning=raise_warning,
                                         default_value=0.,
                                         T=T_i,
                                         **kwargs)
        # Add mixing quantity in appropriate format
        if len(T) == 1:
            CpoR += CpoR_mix[0]
        else:
            CpoR += CpoR_mix
        return CpoR
Ejemplo n.º 6
0
    def get_SoR(self, T, raise_error=True, raise_warning=True, **kwargs):
        """Calculate the dimensionless entropy

        Parameters
        ----------
            T : float or (N,) `numpy.ndarray`_
                Temperature(s) in K
            raise_error : bool, optional
                If True, raises an error if any of the modes do not have the
                quantity of interest. Default is True
            raise_warning : bool, optional
                Only relevant if raise_error is False. Raises a warning if any
                of the modes do not have the quantity of interest. Default is
                True
            kwargs : key-word arguments
                Arguments to calculate mixture model properties, if any
        Returns
        -------
            SoR : float or (N,) `numpy.ndarray`_
                Dimensionless entropy

        .. _`numpy.ndarray`: https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.ndarray.html
        """
        if _is_iterable(T):
            SoR = np.zeros_like(T)
            for i, T_i in enumerate(T):
                a = self.get_a(T=T_i)
                SoR[i] = get_nasa_SoR(a=a, T=T_i) \
                    + np.sum(_get_mix_quantity(mix_models=self.mix_models,
                                               method_name='get_SoR',
                                               raise_error=raise_error,
                                               raise_warning=raise_warning,
                                               default_value=0.,
                                               T=T_i, **kwargs))
        else:
            a = self.get_a(T=T)
            SoR = get_nasa_SoR(a=a, T=T) \
                + np.sum(_get_mix_quantity(mix_models=self.mix_models,
                                           method_name='get_SoR',
                                           raise_error=raise_error,
                                           raise_warning=raise_warning,
                                           default_value=0.,
                                           T=T, **kwargs))
        return SoR
Ejemplo n.º 7
0
def _shomate_CpoR(T, A, B, C, D, E):
    """
    Helper function to fit shomate heat capacity.

    Paramters
    ---------
        T - float
            Temperature in K
        A, B, C, D, E - float
            Shomate parameters
    Returns
    -------
        CpoR - float
            Dimensionless heat capacity
    """
    a = np.array([A, B, C, D, E, 0., 0., 0.])
    if not _is_iterable(T):
        T = [T]
    T = np.array(T)
    return get_shomate_CpoR(a=a, T=T)
Ejemplo n.º 8
0
def _fit_CpoR(T, CpoR, T_mid=None):
    """Fit a[0]-a[4] coefficients in a_low and a_high attributes given the
    dimensionless heat capacity data

    Parameters
    ----------
        T : (N,) `numpy.ndarray`_
            Temperatures in K
        CpoR : (N,) `numpy.ndarray`_
            Dimensionless heat capacity
        T_mid : float or iterable of float, optional
            Guess for T_mid. If float, only uses that value for T_mid. If
            list, finds the best fit for each element in the list. If None,
            a range of T_mid values are screened between the lowest value
            and highest value of T.
    Returns
    -------
        a_low : (7,) `numpy.ndarray`_
            Lower coefficients of NASA polynomial
        a_high : (7,) `numpy.ndarray`_
            Higher coefficients of NASA polynomial
        T_mid : float
            Temperature in K used to split the CpoR data


    .. _`numpy.ndarray`: https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.ndarray.html
    """
    # If the Cp/R does not vary with temperature (occurs when no
    # vibrational frequencies are listed), return default values
    if (np.isclose(np.mean(CpoR), 0.) and np.isnan(variation(CpoR))) \
       or np.isclose(variation(CpoR), 0.) \
       or any([np.isnan(x) for x in CpoR]):
        T_mid = T[int(len(T) / 2)]
        a_low = np.zeros(7)
        a_high = np.zeros(7)
        return a_low, a_high, T_mid

    # If T_mid not specified, generate range between 6th smallest data point
    # and 6th largest data point
    if T_mid is None:
        T_mid = T[5:-5]

    # If a single value for T_mid is chosen, convert to a tuple
    if not _is_iterable(T_mid):
        T_mid = (T_mid, )

    # Initialize parameters for T_mid optimization
    mse_list = []
    prev_mse = np.inf
    all_a_low = []
    all_a_high = []
    for T_m in T_mid:
        # Generate temperature data
        (mse, a_low, a_high) = _get_CpoR_MSE(T=T, CpoR=CpoR, T_mid=T_m)
        mse_list.append(mse)
        all_a_low.append(a_low)
        all_a_high.append(a_high)
        # Check if the optimum T_mid has been found by determining if the
        # fit MSE value for the current T_mid is higher than the previous
        # indicating that subsequent guesses will not improve the fit
        if mse > prev_mse:
            break
        prev_mse = mse

    # Select the optimum T_mid based on the highest fit R2 value
    min_mse = min(mse_list)
    min_i = np.where(min_mse == mse_list)[0][0]

    T_mid_out = T_mid[min_i]
    a_low_rev = all_a_low[min_i]
    a_high_rev = all_a_high[min_i]

    # Reverse array and append two zeros to end
    empty_arr = np.zeros(2)
    a_low_out = np.concatenate((a_low_rev[::-1], empty_arr))
    a_high_out = np.concatenate((a_high_rev[::-1], empty_arr))
    return a_low_out, a_high_out, T_mid_out
Ejemplo n.º 9
0
    def from_statmech(cls,
                      name,
                      statmech_model,
                      T_low,
                      T_high,
                      T_mid=None,
                      references=None,
                      elements=None,
                      **kwargs):
        """Calculates the NASA polynomials using statistical mechanic models

        Parameters
        ----------
            name : str
                Name of the species
            statmech_model : `pMuTT.statmech.StatMech` object or class
                Statistical Mechanics model to generate data
            T_low : float
                Lower limit temerature in K
            T_high : float
                Higher limit temperature in K
            T_mid : float or iterable of float, optional
                Guess for T_mid. If float, only uses that value for T_mid. If
                list, finds the best fit for each element in the list. If None,
                a range of T_mid values are screened between the 6th lowest
                and 6th highest value of T.
            references : `pMuTT.empirical.references.References` object
                Reference to adjust enthalpy
            elements : dict
                Composition of the species.
                Keys of dictionary are elements, values are stoichiometric
                values in a formula unit.
                e.g. CH3OH can be represented as:
                {'C': 1, 'H': 4, 'O': 1,}.
            kwargs : keyword arguments
                Used to initalize ``statmech_model`` or ``EmpiricalBase``
                attributes to be stored.
        Returns
        -------
            Nasa : Nasa object
                Nasa object with polynomial terms fitted to data.
        """
        # Initialize the StatMech object
        if inspect.isclass(statmech_model):
            statmech_model = statmech_model(**kwargs)

        # Generate data
        T = np.linspace(T_low, T_high)
        if T_mid is not None:
            # Check to see if specified T_mid's are in T and, if not,
            # insert them into T.
            # If a single value for T_mid is chosen, convert to a tuple
            if not _is_iterable(T_mid):
                T_mid = (T_mid, )
            for x in range(0, len(T_mid)):
                if np.where(T == T_mid[x])[0].size == 0:
                    # Insert T_mid's into T and save position
                    Ts_index = np.where(T > T_mid[x])[0][0]
                    T = np.insert(T, Ts_index, T_mid[x])
        CpoR = np.array([statmech_model.get_CpoR(T=T_i) for T_i in T])
        T_ref = c.T0('K')
        HoRT_ref = statmech_model.get_HoRT(T=T_ref)
        # Add contribution of references
        if references is not None:
            descriptor_name = references.descriptor
            if descriptor_name == 'elements':
                descriptors = elements
            else:
                descriptors = kwargs[descriptor_name]
            HoRT_ref += references.get_HoRT_offset(descriptors=descriptors,
                                                   T=T_ref)
        SoR_ref = statmech_model.get_SoR(T=T_ref)

        return cls.from_data(name=name,
                             T=T,
                             CpoR=CpoR,
                             T_ref=T_ref,
                             HoRT_ref=HoRT_ref,
                             SoR_ref=SoR_ref,
                             T_mid=T_mid,
                             statmech_model=statmech_model,
                             elements=elements,
                             references=references,
                             **kwargs)