def get_SoR(self, Ts, P=1., verbose=False): """Calculate the dimensionless entropy. Parameters: Ts : float or (N,) `numpy.ndarray`_ Temperature(s) in K P : float, optional Pressure in atm. Default is 1 atm. verbose : bool Whether a table breaking down each contribution should be printed Returns: SoR : float or (N,) `numpy.ndarray`_ Dimensionless entropy (S/R) at the specified temperature and pressure """ try: iter(Ts) except TypeError: SoR = self.model.get_entropy( Ts, pressure=P * c.convert_unit(from_='atm', to='Pa'), verbose=verbose) / c.R('eV/K') else: SoR = np.zeros_like(Ts) for i, T in enumerate(Ts): SoR[i] = self.model.get_entropy( T, pressure=P * c.convert_unit(from_='atm', to='Pa'), verbose=verbose) / c.R('eV/K') return SoR
def get_Vm(self, T=c.T0('K'), P=c.P0('bar'), gas_phase=True): """Calculates the molar volume of a van der Waals gas Parameters ---------- T : float, optional Temperature in K. Default is standard temperature P : float, optional Pressure in bar. Default is standard pressure gas_phase : bool, optional Relevant if system is in vapor-liquid equilibrium. If True, return the larger volume (gas phase). If False, returns the smaller volume (liquid phase). Returns ------- Vm : float Volume in m3 """ P_SI = P * c.convert_unit(from_='bar', to='Pa') Vm = np.roots([ P_SI, -(P_SI * self.b + c.R('J/mol/K') * T), self.a, -self.a * self.b ]) real_Vm = np.real([Vm_i for Vm_i in Vm if np.isreal(Vm_i)]) if gas_phase: return np.max(real_Vm) else: return np.min(real_Vm)
def from_critical(cls, Tc, Pc): """Creates the van der Waals object from critical temperature and pressure Parameters ---------- Tc : float Critical temperature in K Pc : float Critical pressure in bar Returns ------- vanDerWaalsEOS : vanDerWaalsEOS object """ Pc_SI = Pc * c.convert_unit(from_='bar', to='Pa') a = 27. / 64. * (c.R('J/mol/K') * Tc)**2 / Pc_SI b = c.R('J/mol/K') * Tc / 8. / Pc_SI return cls(a=a, b=b)
def get_SoR(self, Ts, verbose=False): """Calculate dimensionless entropy at a given temperature. Args: Ts (float or numpy array): Temperature(s) in K verbose (bool): Flag to print each contribution to entropy Returns: SoR (float or numpy array): Dimensionless entropy (S/R) """ try: iter(Ts) except TypeError: SoR = self.model.get_entropy(Ts, verbose) / c.R('eV/K') else: SoR = np.zeros_like(Ts) for i, T in enumerate(Ts): SoR[i] = self.model.get_entropy(T, verbose) / c.R('eV/K') return SoR
def get_Tc(self): """Calculates the critical temperature Returns ------- Tc : float Critcial temperature in K """ return 8. * self.a / 27. / self.b / c.R('J/mol/K')
def get_SoR(self, Ts, P=1., verbose=False): """Calculate the dimensionless entropy. Parameters: Ts : float or (N,) `numpy.ndarray`_ Temperature(s) in K P : float Pressure in atm. Default is 1 atm verbose : bool Whether a table breaking down each contribution should be printed Returns: SoR : float or (N,) `numpy.ndarray`_ Dimensionless entropy (S/R) """ try: iter(Ts) except TypeError: SoR = self.model.get_entropy(Ts, verbose)/c.R('eV/K') else: SoR = np.zeros_like(Ts) for i, T in enumerate(Ts): SoR[i] = self.model.get_entropy(T, verbose)/c.R('eV/K') return SoR
def setUp(self): unittest.TestCase.setUp(self) H2_thermo = BaseThermo(name='H2', phase='G', elements={'H': 2}, thermo_model=IdealGasThermo, T_ref=c.T0('K'), HoRT_ref=0., vib_energies=np.array([4306.1793]) * c.c('cm/s') * c.h('eV s'), potentialenergy=-6.7598, geometry='linear', symmetrynumber=2, spin=0, atoms=molecule('H2')) H2O_thermo = BaseThermo( name='H2O', phase='G', elements={ 'H': 2, 'O': 1 }, thermo_model=IdealGasThermo, T_ref=c.T0('K'), HoRT_ref=-241.826 / (c.R('kJ/mol/K') * c.T0('K')), vib_energies=np.array([3825.434, 3710.264, 1582.432]) * c.c('cm/s') * c.h('eV s'), potentialenergy=-14.2209, geometry='nonlinear', symmetrynumber=2, spin=0, atoms=molecule('H2O')) O2_thermo = BaseThermo(name='H2O', phase='G', elements={'O': 2}, thermo_model=IdealGasThermo, T_ref=c.T0('K'), HoRT_ref=0., vib_energies=np.array([2205.]) * c.c('cm/s') * c.h('eV s'), potentialenergy=-9.86, geometry='linear', symmetrynumber=2, spin=1, atoms=molecule('O2')) self.references = References( references=[H2_thermo, H2O_thermo, O2_thermo])
def get_V(self, T=c.T0('K'), P=c.P0('bar'), n=1.): """Calculates the volume of an ideal gas Parameters ---------- T : float, optional Temperature in K. Default is standard temperature P : float, optional Pressure in bar. Default is standard pressure n : float, optional Number of moles (in mol). Default is 1 mol Returns ------- V : float Volume in m3 """ return n * c.R('m3 bar/mol/K') * T / P
def get_T(self, V=c.V0('m3'), P=c.P0('bar'), n=1.): """Calculates the volume of an ideal gas Parameters ---------- V : float, optional Volume in m3. Default is standard volume P : float, optional Pressure in bar. Default is standard pressure n : float, optional Number of moles (in mol). Default is 1 mol Returns ------- T : float Temperature in K """ return P * V / c.R('m3 bar/mol/K') / n
def get_n(self, V=c.V0('m3'), P=c.P0('bar'), T=c.T0('K')): """Calculates the volume of an ideal gas Parameters ---------- V : float, optional Volume in m3. Default is standard volume P : float, optional Pressure in bar. Default is standard pressure T : float, optional Temperature in K. Default is standard temperature Returns ------- n : float Number of moles in mol """ return P * V / c.R('m3 bar/mol/K') / T
def get_V(self, T, P): """Calculates the molar volume of an ideal gas at T and P :math:`V_m=\\frac{RT}{P}` Parameters ---------- T : float Temperature in K P : float Pressure in bar Returns ------- V : float Molar volume in m3 """ return T * c.R('J/mol/K') / (P * c.convert_unit(from_='bar', to='Pa'))
def get_SoR(self, temperature, pressure=1.0, verbose=False): """Calculate the dimensionless entropy. Parameters: temperature (float): Temperature(s) in K pressure (float, optional): Pressure in atm. Default is 1 atm. verbose (bool): Print a table breaking down each contribution """ if self.specie_type == 'ideal gas': entropy = self.model.get_entropy( temperature, pressure=pressure * c.convert_unit(from_='atm', to='Pa'), verbose=verbose) else: entropy = self.model.get_entropy(temperature, verbose=verbose) return entropy / c.R('eV/K')
def get_P(self, T=c.T0('K'), V=c.V0('m3'), n=1.): """Calculates the pressure of an ideal gas Parameters ---------- T : float, optional Temperature in K. Default is standard temperature V : float, optional Volume in m3. Default is standard volume n : float, optional Number of moles (in mol). Default is 1 mol Returns ------- P : float Pressure in bar """ return n * c.R('m3 bar/mol/K') * T / V
def get_P(self, T=c.T0('K'), V=c.V0('m3'), n=1.): """Calculates the pressure of a van der Waals gas Parameters ---------- T : float, optional Temperature in K. Default is standard temperature V : float, optional Volume in m3. Default is standard volume n : float, optional Number of moles (in mol). Default is 1 mol Returns ------- P : float Pressure in bar """ Vm = V / n return (c.R('J/mol/K')*T/(Vm - self.b) - self.a*(1./Vm)**2) \ *c.convert_unit(from_='Pa', to='bar')
def get_T(self, V=c.V0('m3'), P=c.P0('bar'), n=1.): """Calculates the volume of a van der Waals gas Parameters ---------- V : float, optional Volume in m3. Default is standard volume P : float, optional Pressure in bar. Default is standard pressure n : float, optional Number of moles (in mol). Default is 1 mol Returns ------- T : float Temperature in K """ Vm = V / n return (P*c.convert_unit(from_='bar', to='Pa') + self.a/Vm**2) \ *(Vm - self.b)/c.R('J/mol/K')
def plot_statmech_and_empirical(self, T_low=None, T_high=None, Cp_units=None, H_units=None, S_units=None, G_units=None): """Plots the thermodynamic profiles between ``T_low`` and ``T_high`` using empirical relationship Parameters ---------- T_low : float Lower temperature in K. If not specified, ``T_low`` attribute used T_high : float Upper temperature in K. If not specified, ``T_high`` attribute used Cp_units : str Units to plot heat capacity. See ``PyMuTT.constants.R`` for accepted units. If not specified, dimensionless units used. H_units : str Units to plot enthalpy. See ``PyMuTT.constants.R`` for accepted units but omit the '/K' (e.g. J/mol). If not specified, dimensionless units used. S_units : str Units to plot entropy. See ``PyMuTT.constants.R`` for accepted units. If not specified, dimensionless units used. G_units : str Units to plot Gibbs free energy. See ``PyMuTT.constants.R`` for accepted units but omit the '/K' (e.g. J/mol). If not specified, dimensionless units used. Returns ------- figure : `matplotlib.figure.Figure`_ Figure axes : tuple of `matplotlib.axes.Axes.axis`_ Axes of the plots. 0. Cp 1. H 2. S 3. G .. _`matplotlib.figure.Figure`: https://matplotlib.org/api/_as_gen/matplotlib.figure.Figure.html .. _`matplotlib.axes.Axes.axis`: https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.axis.html """ if T_low is None: T_low = self.T_low if T_high is None: T_high = self.T_high T = np.linspace(T_low, T_high) f, ax = plt.subplots(4, sharex=True) ''' Heat Capacity ''' ax[0].set_title('Specie: {}'.format(self.name)) T, Cp_plot_statmech, Cp_plot_empirical = self.compare_CpoR(T=T) if Cp_units is None: ax[0].set_ylabel('Cp/R') else: ax[0].set_ylabel('Cp ({})'.format(Cp_units)) Cp_plot_statmech = Cp_plot_statmech * c.R(Cp_units) Cp_plot_empirical = Cp_plot_empirical * c.R(Cp_units) ax[0].plot(T, Cp_plot_statmech, 'r-', label='Stat Mech Model') ax[0].plot(T, Cp_plot_empirical, 'b-', label='Empirical Model') ax[0].legend() ''' Enthalpy ''' T, H_plot_statmech, H_plot_empirical = self.compare_HoRT(T=T) if H_units is None: ax[1].set_ylabel('H/RT') else: ax[1].set_ylabel('H ({})'.format(H_units)) H_plot_statmech = H_plot_statmech *\ c.R('{}/K'.format(H_units))*T H_plot_empirical = H_plot_empirical *\ c.R('{}/K'.format(H_units))*T ax[1].plot(T, H_plot_statmech, 'r-') ax[1].plot(T, H_plot_empirical, 'b-') ''' Entropy ''' T, S_plot_statmech, S_plot_empirical = self.compare_SoR(T=T) if S_units is None: ax[2].set_ylabel('S/R') else: ax[2].set_ylabel('S ({})'.format(S_units)) S_plot_statmech = S_plot_statmech * c.R(S_units) S_plot_empirical = S_plot_empirical * c.R(S_units) ax[2].plot(T, S_plot_statmech, 'r-') ax[2].plot(T, S_plot_empirical, 'b-') ''' Gibbs energy ''' ax[3].set_xlabel('Temperature (K)') T, G_plot_statmech, G_plot_empirical = self.compare_GoRT(T=T) if G_units is None: ax[3].set_ylabel('G/RT') else: ax[3].set_ylabel('G ({})'.format(G_units)) G_plot_statmech = G_plot_statmech *\ c.R('{}/K'.format(G_units))*T G_plot_empirical = G_plot_empirical *\ c.R('{}/K'.format(G_units))*T ax[3].plot(T, G_plot_statmech, 'r-') ax[3].plot(T, G_plot_empirical, 'b-') return f, ax
def plot_empirical(self, T_low=None, T_high=None, Cp_units=None, H_units=None, S_units=None, G_units=None): """Plot the thermodynamic profiles between ``T_low`` and ``T_high`` using empirical relationship Parameters: T_low : float Lower temperature in K. If not specified, ``T_low`` attribute used. T_high : float Upper temperature in K. If not specified, ``T_high`` attribute used. Cp_units : str Units to plot heat capacity. See ``PyMuTT.constants.R`` for accepted units. If not specified, dimensionless units used. H_units : str Units to plot enthalpy. See ``PyMuTT.constants.R`` for accepted units but omit the '/K' (e.g. J/mol). If not specified, dimensionless units used. S_units : str Units to plot entropy. See ``PyMuTT.constants.R`` for accepted units. If not specified, dimensionless units used. G_units : str Units to plot Gibbs free energy. See ``PyMuTT.constants.R`` for accepted units but omit the '/K' (e.g. J/mol). If not specified, dimensionless units used. Returns: figure : `matplotlib.figure.Figure`_ Figure axes : tuple of `matplotlib.axes.Axes.axis`_ Axes of the plots. 0. Cp 1. H 2. S 3. G """ if T_low is None: T_low = self.T_low if T_high is None: T_high = self.T_high Ts = np.linspace(T_low, T_high) f, ax = plt.subplots(4, sharex=True) ''' Heat Capacity ''' ax[0].set_title('Specie: {}'.format(self.name)) Cp_plot = np.array(map(self.get_CpoR, Ts)) if Cp_units is None: ax[0].set_ylabel('Cp/R') else: ax[0].set_ylabel('Cp ({})'.format(Cp_units)) Cp_plot *= c.R(Cp_units) ax[0].plot(Ts, Cp_plot, 'r-') ''' Enthalpy ''' H_plot = np.array(map(self.get_HoRT, Ts)) if H_units is None: ax[1].set_ylabel('H/RT') else: ax[1].set_ylabel('H ({})'.format(H_units)) H_plot *= c.R('{}/K'.format(H_units)) * Ts ax[1].plot(Ts, H_plot, 'g-') ''' Entropy ''' S_plot = np.array(map(self.get_SoR, Ts)) if S_units is None: ax[2].set_ylabel('S/R') else: ax[2].set_ylabel('S ({})'.format(S_units)) S_plot *= c.R(S_units) ax[2].plot(Ts, S_plot, 'b-') ''' Gibbs energy ''' ax[3].set_xlabel('Temperature (K)') G_plot = np.array(map(self.get_GoRT, Ts)) if G_units is None: ax[3].set_ylabel('G/RT') else: ax[3].set_ylabel('G ({})'.format(G_units)) G_plot *= c.R('{}/K'.format(G_units)) * Ts ax[3].plot(Ts, G_plot, 'k-') return f, ax
def test_R(self): self.assertEqual(c.R('J/mol/K'), 8.3144598) with self.assertRaises(KeyError): c.R('arbitrary unit')
def test_get_GoRT(self): expected_GoRT_CO2 = \ self.CO2_ASE.get_gibbs_energy(temperature=self.T0, pressure=self.P0, verbose=False)/c.R('eV/K')/self.T0 calc_GoRT_CO2 = self.CO2_PyMuTT.get_GoRT(T=self.T0, V=self.V0) self.assertTrue(np.isclose(expected_GoRT_CO2, calc_GoRT_CO2))
def test_get_SoR(self): expected_SoR_CO2 = \ self.CO2_ASE.get_entropy(temperature=self.T0, pressure=self.P0, verbose=False)/c.R('eV/K') calc_SoR_CO2 = self.CO2_PyMuTT.get_SoR(T=self.T0, V=self.V0) self.assertTrue(np.isclose(expected_SoR_CO2, calc_SoR_CO2))
def test_get_HoRT(self): expected_HoRT_CO2 = \ self.CO2_ASE.get_enthalpy(temperature=self.T0, verbose=False) \ /c.R('eV/K')/self.T0 calc_HoRT_CO2 = self.CO2_PyMuTT.get_HoRT(T=self.T0) self.assertTrue(np.isclose(expected_HoRT_CO2, calc_HoRT_CO2))
import numpy as np import matplotlib.pyplot as plt from PyMuTT import constants as c from PyMuTT.models.empirical.nasa import Nasa # Gas phase heat capacity data (in J/mol/K) for CH3OH from NIST # https://webbook.nist.gov/cgi/cbook.cgi?ID=C67561&Units=SI&Mask=1#Thermo-Gas Ts = np.array([50, 100, 150, 200, 273.15, 298.15, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1750, 2000, 2250, 2500, 2750, 3000]) Cp = np.array([34.00, 36.95, 38.64, 39.71, 42.59, 44.06, 44.17, 51.63, 59.7, 67.19, 73.86, 79.76, 84.95, 89.54, 93.57, 97.12, 100.24, 102.98, 105.4, 110.2, 113.8, 116.5, 118.6, 120, 121]) CpoR = Cp/c.R('J/mol/K') #Enthalpy of Formation for CH3OH (in kJ/mol) from NIST T_ref = c.T0('K') H_ref = -205. HoRT_ref = H_ref/c.R('kJ/mol/K')/T_ref #Standard molar entropy (in J/mol/K) from Wikipedia, https://en.wikipedia.org/wiki/Methanol_(data_page) S_ref = 239.9 SoR_ref = S_ref/c.R('J/mol/K') #Units to plot the figure Cp_units = 'J/mol/K' H_units = 'kJ/mol' S_units = 'J/mol/K' G_units = 'kJ/mol' #Input the experimental data and fitting to a NASA polynomial CH3OH_nasa = Nasa(name ='CH3OH', Ts=Ts, CpoR=CpoR, T_ref=T_ref, HoRT_ref=HoRT_ref, SoR_ref=SoR_ref) #Compare the Nasa polynomial to the input data fig, axes = CH3OH_nasa.plot_empirical(Cp_units=Cp_units, H_units=H_units, S_units=S_units, G_units=G_units) axes[0].plot(Ts, Cp, 'ko')
def setUp(self): unittest.TestCase.setUp(self) H2_thermo = Reference(name='H2', phase='G', elements={'H': 2}, T_ref=c.T0('K'), HoRT_ref=0., statmech_model=StatMech, trans_model=trans.IdealTrans, n_degrees=3, vib_model=vib.HarmonicVib, elec_model=elec.IdealElec, rot_model=rot.RigidRotor, vib_wavenumbers=np.array([4306.1793]), potentialenergy=-6.7598, geometry='linear', symmetrynumber=2, spin=0, atoms=molecule('H2')) H2O_thermo = Reference( name='H2O', phase='G', elements={ 'H': 2, 'O': 1 }, T_ref=c.T0('K'), HoRT_ref=-241.826 / (c.R('kJ/mol/K') * c.T0('K')), statmech_model=StatMech, trans_model=trans.IdealTrans, n_degrees=3, vib_model=vib.HarmonicVib, elec_model=elec.IdealElec, rot_model=rot.RigidRotor, vib_wavenumbers=np.array([3825.434, 3710.264, 1582.432]), potentialenergy=-14.2209, geometry='nonlinear', symmetrynumber=2, spin=0, atoms=molecule('H2O')) O2_thermo = Reference(name='H2O', phase='G', elements={'O': 2}, T_ref=c.T0('K'), HoRT_ref=0., statmech_model=StatMech, trans_model=trans.IdealTrans, n_degrees=3, vib_model=vib.HarmonicVib, elec_model=elec.IdealElec, rot_model=rot.RigidRotor, vib_wavenumbers=np.array([2205.]), potentialenergy=-9.86, geometry='linear', symmetrynumber=2, spin=1, atoms=molecule('O2')) self.references = References( references=[H2_thermo, H2O_thermo, O2_thermo])