Source code for sandlerchemeq.component

from __future__ import annotations
from dataclasses import dataclass, field, asdict
import numpy as np
import pint

from sandlerprops.compound import Compound
from sandlerprops.properties import PropertiesDatabase, get_database
from sandlermisc import ureg

[docs] @dataclass class Component(Compound): """ A chemical component with thermochemical and system properties based on the **Compound** class of `sandlerprops`. """ T: pint.Quantity = field(default_factory=lambda: 298.15 * ureg.K) """ Temperature """ P: pint.Quantity = field(default_factory=lambda: 1.0 * ureg.bar) """ Pressure """ Tref: pint.Quantity = field(default_factory=lambda: 298.15 * ureg.K) """ Reference temperature """ def __post_init__(self): super().__post_init__() if not hasattr(self.T, 'units'): self.T = self.T * ureg.K if not hasattr(self.P, 'units'): self.P = self.P * ureg.bar if not hasattr(self.Tref, 'units'): self.Tref = self.Tref * ureg.K
[docs] @classmethod def from_compound(cls, compound: Compound, **kwargs) -> Component: """ Create a Component instance from a Compound instance Parameters ---------- compound : Compound An instance of the Compound class Returns ------- component : Component An instance of the Component class """ data = asdict(compound) data.update(kwargs) return cls(**data)
[docs] def Cp_polynomial_as_tex(self): """ Returns a LaTeX-formatted string for the compound's heat capacity polynomial Returns ------- retstr : str LaTeX formatted heat capacity polynomial string """ return Cp_as_tex(self.Cp)
[docs] def CpInt_polynomial_as_tex(self): """ Returns a LaTeX-formatted string for the integral of the compound's heat capacity polynomial Returns ------- retstr : str LaTeX formatted integral of heat capacity polynomial string """ Cp = self.Cp retstr = (f"{Cp[0]:.3f}($T_2-T_1$) + " r'$\frac{1}{2}$('+format_sig(Cp[1], sig=4)+r') ($T_2^2-T_1^2$) + ' r'$\frac{1}{3}$('+format_sig(Cp[2], sig=4)+r') ($T_2^3-T_1^3$) + ' r'$\frac{1}{4}$('+format_sig(Cp[3], sig=4)+r') ($T_2^4-T_1^4$)') return(retstr)
@property def _cpI(self): """∫Cp dT from Tref to T (J/mol)""" cp = self.Cp T_K, Tref_K = self.T.m_as('K'), self.Tref.m_as('K') val = sum([cp[i]/(i+1)*(T_K**(i+1)-Tref_K**(i+1)) for i in range(len(cp))]) return val * ureg.J / ureg.mol @property def _cpTI(self): """∫(Cp/T) dT from Tref to T (J/(mol·K))""" cp = self.Cp T_K, Tref_K = self.T.m_as('K'), self.Tref.m_as('K') val = cp[0]*np.log(T_K/Tref_K)+sum([cp[i]/i*(T_K**i-Tref_K**i) for i in range(1,len(cp))]) return val * ureg.J / (ureg.mol * ureg.K) @property def dGf_T(self) -> pint.Quantity: """ Ideal-gas Gibbs energy of formation at temperature T """ T_rat = (self.T / self.Tref).m_as('') # dimensionless ratio as plain float return (self.dGf * T_rat + self.dHf * (1 - T_rat) + self._cpI - self.T * self._cpTI).to('J/mol')
[docs] def __eq__(self, other: Compound): """ Compounds are equal if their empirical formulas are identical """ return self.Formula == other.Formula
[docs] def __hash__(self): """ Hash based on object id """ return id(self)
def __str__(self): return self.Formula.split('^')[0] + ('' if self.charge==0 else r'^{'+f'{self.charge:+}'+r'}')
[docs] def countAtoms(self, a: str) -> int: """ Returns the count of atom a in the compound Parameters ---------- a : str atom name Returns ------- count : int Number of atoms of type a in the compound """ return self.atomdict.get(a, 0)