<commit_before># -*- coding: utf-8 -*-
'''Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
Copyright (C) 2016, 2017, 2018, 2019, 2020 <NAME> <<EMAIL>>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

This module contains a base class :obj:`GibbsExcess` for handling activity
coefficient based
models. The design is for a sub-class to provide the minimum possible number of
derivatives of Gibbs energy, and for this base class to provide the rest of the
methods.  An ideal-liquid class with no excess Gibbs energy
:obj:`IdealSolution` is also available.

For reporting bugs, adding feature requests, or submitting pull requests,
please use the `GitHub issue tracker <https://github.com/CalebBell/thermo/>`_.

.. contents:: :local:

Base Class
==========

.. autoclass:: GibbsExcess
    :members:
    :undoc-members:
    :show-inheritance:
    :exclude-members:

Idea Liquid Class
=================

.. autoclass:: IdealSolution
    :members: to_T_xs, GE, dGE_dT, d2GE_dT2, d3GE_dT3, d2GE_dTdxs, dGE_dxs, d2GE_dxixjs, d3GE_dxixjxks
    :undoc-members:
    :show-inheritance:
    :exclude-members: gammas

Notes
=====
Excellent references for working with activity coefficient models are [1]_ and
[2]_.

References
----------
.. [1] <NAME>. Phase Equilibria in Chemical Engineering.
   Butterworth-Heinemann, 1985.
.. [2] <NAME>. Chemical Thermodynamics: For Process
   Simulation. Weinheim, Germany: Wiley-VCH, 2012.

'''

from __future__ import division

__all__ = ['GibbsExcess', 'IdealSolution']
from fluids.constants import R, R_inv
from fluids.numerics import numpy as np
from chemicals.utils import exp
from chemicals.utils import normalize, dxs_to_dns, dxs_to_dn_partials, dns_to_dn_partials, d2xs_to_dxdn_partials
from thermo import utils
from thermo.utils import dump_json_np

try:
    npexp, ones, zeros, array = np.exp, np.ones, np.zeros, np.array
except:
    pass

def gibbs_excess_gammas(xs, dG_dxs, GE, T, gammas=None):
    xdx_totF = GE
    N = len(xs)
    for i in range(N):
        xdx_totF -= xs[i]*dG_dxs[i]
    RT_inv = R_inv/T
    if gammas is None:
        gammas = [0.0]*N
    for i in range(N):
        gammas[i] = exp((dG_dxs[i] + xdx_totF)*RT_inv)
    return gammas

def gibbs_excess_dHE_dxs(dGE_dxs, d2GE_dTdxs, N, T, dHE_dxs=None):
    if dHE_dxs is None:
        dHE_dxs = [0.0]*N
    for i in range(N):
        dHE_dxs[i] = -T*d2GE_dTdxs[i] + dGE_dxs[i]
    return dHE_dxs


def gibbs_excess_dgammas_dns(xs, gammas, d2GE_dxixjs, N, T, dgammas_dns=None, vec0=None):
    if vec0 is None:
        vec0 = [0.0]*N
    if dgammas_dns is None:
        dgammas_dns = [[0.0]*N for _ in range(N)] # numba : delete
#        dgammas_dns = zeros((N, N)) # numba : uncomment

    for j in range(N):
        tot = 0.0
        row = d2GE_dxixjs[j]
        for k in range(N):
            tot += xs[k]*row[k]
        vec0[j] = tot

    RT_inv = R_inv/(T)

    for i in range(N):
        gammai_RT = gammas[i]*RT_inv
        for j in range(N):
            dgammas_dns[i][j] = gammai_RT*(d2GE_dxixjs[i][j] - vec0[j])

    return dgammas_dns

def gibbs_excess_dgammas_dT(xs, GE, dGE_dT, dG_dxs, d2GE_dTdxs, N, T, dgammas_dT=None):
    if dgammas_dT is None:
        dgammas_dT = [0.0]*N

    xdx_totF0 = dGE_dT
    for j in range(N):
        xdx_totF0 -= xs[j]*d2GE_dTdxs[j]
    xdx_totF1 = GE
    for j in range(N):
        xdx_totF1 -= xs[j]*dG_dxs[j]

    T_inv = 1.0/T
    RT_inv = R_inv*T_inv
    for i in range(N):
        dG_dni = xdx_totF1 + dG_dxs[i]
        dgammas_dT[i] = RT_inv*(d2GE_dTdxs[i] - dG_dni*T_inv + xdx_totF0)*exp(dG_dni*RT_inv)
    return dgammas_dT

class GibbsExcess(object):
    r'''Class for representing an activity coefficient model.
    While these are typically presented as tools to compute activity
    coefficients, in truth they are excess Gibbs energy models and activity
    coefficients are just one derived aspect of them.

    This class does not implement any activity coefficient models itself; it
    must be subclassed by another model. All properties are
    derived with the CAS SymPy, not relying on any derivations previously
    published, and checked numerically for consistency.

    Different subclasses have different parameter requirements for
    initialization; :obj:`IdealSolution` is
    available as a simplest model with activity coefficients of 1 to show
    what needs to be implemented in subclasses. It is also intended subclasses
    implement the method `to_T_xs`, which creates a new object at the
    specified temperature and composition but with the same parameters.

    These objects are intended to lazy-calculate properties as much as
    possible, and for the temperature and composition of an object to be
    immutable.

    '''
    def __repr__(self):
        # Other classes with different parameters should expose them here too
        s = '%s(T=%s, xs=%s)' %(self.__class__.__name__, repr(self.T), repr(self.xs))
        return s

    def as_JSON(self):
        # vaguely jsonpickle compatible
        mod_name = self.__class__.__module__
        d = self.__dict__
        d["py/object"] = "%s.%s" %(mod_name, self.__class__.__name__)
        d["json_version"] = 1
        if self.scalar:
            ans = utils.json.dumps(self.__dict__)
        else:
            ans = dump_json_np(self.__dict__)
        del d["py/object"]
        del d["json_version"]
        return ans

    @classmethod
    def from_JSON(cls, json_repr):
#        mod_name = cls.__module__

        d = utils.json.loads(json_repr)

#        if cls is GibbsExcess:
#            model_name = d['py/object']

        del d['py/object']
        del d["json_version"]

        new = cls.__new__(cls)
        new.__dict__ = d
        return new

    def HE(self):
        r'''Calculate and return the excess entropy of a liquid phase using an
        activity coefficient model.

        .. math::
            h^E = -T \frac{\partial g^E}{\partial T} + g^E

        Returns
        -------
        HE : float
            Excess enthalpy of the liquid phase, [J/mol]

        Notes<commit_msg>Add more docs to json export<commit_after># -*- coding: utf-8 -*-
'''Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
Copyright (C) 2016, 2017, 2018, 2019, 2020 <NAME> <<EMAIL>>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

This module contains a base class :obj:`GibbsExcess` for handling activity
coefficient based
models. The design is for a sub-class to provide the minimum possible number of
derivatives of Gibbs energy, and for this base class to provide the rest of the
methods.  An ideal-liquid class with no excess Gibbs energy
:obj:`IdealSolution` is also available.

For reporting bugs, adding feature requests, or submitting pull requests,
please use the `GitHub issue tracker <https://github.com/CalebBell/thermo/>`_.

.. contents:: :local:

Base Class
==========

.. autoclass:: GibbsExcess
    :members:
    :undoc-members:
    :show-inheritance:
    :exclude-members:

Idea Liquid Class
=================

.. autoclass:: IdealSolution
    :members: to_T_xs, GE, dGE_dT, d2GE_dT2, d3GE_dT3, d2GE_dTdxs, dGE_dxs, d2GE_dxixjs, d3GE_dxixjxks
    :undoc-members:
    :show-inheritance:
    :exclude-members: gammas

Notes
=====
Excellent references for working with activity coefficient models are [1]_ and
[2]_.

References
----------
.. [1] <NAME>. Phase Equilibria in Chemical Engineering.
   Butterworth-Heinemann, 1985.
.. [2] <NAME>. Chemical Thermodynamics: For Process
   Simulation. Weinheim, Germany: Wiley-VCH, 2012.

'''

from __future__ import division

__all__ = ['GibbsExcess', 'IdealSolution']
from fluids.constants import R, R_inv
from fluids.numerics import numpy as np
from chemicals.utils import exp
from chemicals.utils import normalize, dxs_to_dns, dxs_to_dn_partials, dns_to_dn_partials, d2xs_to_dxdn_partials
from thermo import utils
from thermo.utils import dump_json_np

try:
    npexp, ones, zeros, array = np.exp, np.ones, np.zeros, np.array
except:
    pass

def gibbs_excess_gammas(xs, dG_dxs, GE, T, gammas=None):
    xdx_totF = GE
    N = len(xs)
    for i in range(N):
        xdx_totF -= xs[i]*dG_dxs[i]
    RT_inv = R_inv/T
    if gammas is None:
        gammas = [0.0]*N
    for i in range(N):
        gammas[i] = exp((dG_dxs[i] + xdx_totF)*RT_inv)
    return gammas

def gibbs_excess_dHE_dxs(dGE_dxs, d2GE_dTdxs, N, T, dHE_dxs=None):
    if dHE_dxs is None:
        dHE_dxs = [0.0]*N
    for i in range(N):
        dHE_dxs[i] = -T*d2GE_dTdxs[i] + dGE_dxs[i]
    return dHE_dxs


def gibbs_excess_dgammas_dns(xs, gammas, d2GE_dxixjs, N, T, dgammas_dns=None, vec0=None):
    if vec0 is None:
        vec0 = [0.0]*N
    if dgammas_dns is None:
        dgammas_dns = [[0.0]*N for _ in range(N)] # numba : delete
#        dgammas_dns = zeros((N, N)) # numba : uncomment

    for j in range(N):
        tot = 0.0
        row = d2GE_dxixjs[j]
        for k in range(N):
            tot += xs[k]*row[k]
        vec0[j] = tot

    RT_inv = R_inv/(T)

    for i in range(N):
        gammai_RT = gammas[i]*RT_inv
        for j in range(N):
            dgammas_dns[i][j] = gammai_RT*(d2GE_dxixjs[i][j] - vec0[j])

    return dgammas_dns

def gibbs_excess_dgammas_dT(xs, GE, dGE_dT, dG_dxs, d2GE_dTdxs, N, T, dgammas_dT=None):
    if dgammas_dT is None:
        dgammas_dT = [0.0]*N

    xdx_totF0 = dGE_dT
    for j in range(N):
        xdx_totF0 -= xs[j]*d2GE_dTdxs[j]
    xdx_totF1 = GE
    for j in range(N):
        xdx_totF1 -= xs[j]*dG_dxs[j]

    T_inv = 1.0/T
    RT_inv = R_inv*T_inv
    for i in range(N):
        dG_dni = xdx_totF1 + dG_dxs[i]
        dgammas_dT[i] = RT_inv*(d2GE_dTdxs[i] - dG_dni*T_inv + xdx_totF0)*exp(dG_dni*RT_inv)
    return dgammas_dT

class GibbsExcess(object):
    r'''Class for representing an activity coefficient model.
    While these are typically presented as tools to compute activity
    coefficients, in truth they are excess Gibbs energy models and activity
    coefficients are just one derived aspect of them.

    This class does not implement any activity coefficient models itself; it
    must be subclassed by another model. All properties are
    derived with the CAS SymPy, not relying on any derivations previously
    published, and checked numerically for consistency.

    Different subclasses have different parameter requirements for
    initialization; :obj:`IdealSolution` is
    available as a simplest model with activity coefficients of 1 to show
    what needs to be implemented in subclasses. It is also intended subclasses
    implement the method `to_T_xs`, which creates a new object at the
    specified temperature and composition but with the same parameters.

    These objects are intended to lazy-calculate properties as much as
    possible, and for the temperature and composition of an object to be
    immutable.

    '''
    def __repr__(self):
        # Other classes with different parameters should expose them here too
        s = '%s(T=%s, xs=%s)' %(self.__class__.__name__, repr(self.T), repr(self.xs))
        return s

    def as_JSON(self):
        r'''Method to create a JSON serialization of the Gibbs Excess model
        which can be stored, and reloaded later.

        Returns
        -------
        json_repr : str
            Json representation, [-]

        Notes
        -----

        Examples
        --------
        >>> model = IdealSolution(T=300.0, xs=[.1, .2, .3, .4])
        >>> string = model.as_JSON()
        >>> type(string)
        str
        '''
        # vaguely jsonpickle compatible
        mod_name = self.__class__.__module__
        d = self.__dict__
        d["py/object"] = "%s.%s" %(mod_name, self.__class__.__name__)
        d["json_version"] = 1
        if self.scalar:
            ans = utils.json.dumps(self.__dict__)
        else:
            ans = dump_json_np(self.__dict__)
        del d["py/object"]
        del d["json_version"]
        return ans

    @classmethod
    def from_JSON(cls, json_repr):
        r'''Method to create a Gibbs Excess model from a JSON
        serialization of another Gibbs Excess model.

        Parameters
        ----------
        json_repr : str
            JSON representation, [-]

        Returns
        -------
        model : :obj:`GibbsExcess`
            Newly created object from the json serialization, [-]

        Notes
        -----
        It is important that the input string be in the same format as that
        created by :obj:`GibbsExcess.as_JSON`.

        Examples
        --------
        >>> model = IdealSolution(T=300.0, xs=[.1, .2, .3, .4])
        >>> string = model.as_JSON()
        >>> new_model = GibbsExcess.from_JSON(string)
        >>> assert model.__dict__ == new_model.__dict__
        '''

        d = utils.json.loads(json_repr)

#        if cls is GibbsExcess:
#            model_name = d['py/object']

        del d['py/object']
        del d["json_version"]

        new = cls.__new__(cls)
        new.__dict__ = d
        return new

    def HE(self):
        r'''Calculate and return the excess entropy of a liquid phase using an
        activity coefficient model.

        .. math::
            h^E = -T \frac{\partial g^E}{\partial T} + g^E

        Returns
        -------
        HE : float
            Excess enthalpy of the liquid phase, [J/mol]

        Notes
{'fraction_non_alphanumeric': 0.09569315355413012, 'fraction_numerical': 0.010996203691582667, 'mean_word_length': 2.855412566237699, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 36, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1025508', 'n_tokens_mistral': 5206, 'n_tokens_neox': 4922, 'n_words': 1680}