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_GoRT(self, Ts, P=1., verbose=False): """Returns the dimensionless Gibbs energy at a given temperature Parameters: Ts : float or (N,) `numpy.ndarray`_ Temperature(s) in K P : float, optional Pressure in bar. Default is 1 atm. verbose : bool Whether a table breaking down each contribution should be printed Returns: GoRT : float or (N,) `numpy.ndarray`_ Dimensionless heat capacity (G/RT) at the specified temperature """ try: iter(Ts) except TypeError: GoRT = self.model.get_gibbs_energy( Ts, pressure=P*c.convert_unit(from_='bar', to='Pa'), verbose=verbose) / \ (c.kb('eV/K') * Ts) else: GoRT = np.zeros_like(Ts) for i, T in enumerate(Ts): GoRT[i] = self.model.get_gibbs_energy( T, pressure=P*c.convert_unit(from_='bar', to='Pa'), verbose=verbose) / \ (c.kb('eV/K') * T) return GoRT
def __init__(self, A_st=None, atoms=None, symmetrynumber=None, inertia=None, geometry=None, vib_energies=None, potentialenergy=None, **kwargs): super().__init__(atoms=atoms, symmetrynumber=symmetrynumber, geometry=geometry, vib_energies=vib_energies, potentialenergy=potentialenergy, **kwargs) self.A_st = A_st self.atoms = atoms self.geometry = geometry self.symmetrynumber = symmetrynumber self.inertia = inertia self.etotal = potentialenergy self.vib_energies = vib_energies self.theta = np.array(vib_energies) / c.kb('eV/K') self.zpe = sum(np.array(vib_energies)/2.) *\ c.convert_unit(from_='eV', to='kcal')*c.Na if np.sum(vib_energies) != 0: self.q_vib = np.product( np.divide(1, (1 - np.exp(-self.theta / c.T0('K'))))) if self.phase == 'G': if self.inertia is not None: self.I3 = self.inertia else: self.I3 = atoms.get_moments_of_inertia() *\ c.convert_unit(from_='A2', to='m2') *\ c.convert_unit(from_='amu', to='kg') self.T_I = c.h('J s')**2 / (8 * np.pi**2 * c.kb('J/K')) if self.phase == 'G': Irot = np.max(self.I3) if self.geometry == 'nonlinear': self.q_rot = np.sqrt(np.pi*Irot)/self.symmetrynumber *\ (c.T0('K')/self.T_I)**(3./2.) else: self.q_rot = (c.T0('K') * Irot / self.symmetrynumber) / self.T_I else: self.q_rot = 0. if self.A_st is not None: self.MW = mw(self.elements) * c.convert_unit(from_='g', to='kg') / c.Na self.q_trans2D = self.A_st * (2 * np.pi * self.MW * c.kb('J/K') * c.T0('K')) / c.h('J s')**2
def get_GoRT(self, Ts, P=1., verbose=False): """Calculate the dimensionless Gibbs energy. Parameters: Ts : float or list 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: GoRT: float or (N,) `numpy.ndarray`_ Dimensionless heat capacity (G/RT) at the specified temperature """ press = P * c.convert_unit(from_='atm', to='Pa') try: iter(Ts) except TypeError: GoRT = self.model.get_helmholtz_energy(Ts, verbose=verbose) / \ (c.kb('eV/K') * Ts) else: GoRT = np.zeros_like(Ts) for i, T in enumerate(Ts): GoRT[i] = self.model.get_helmholtz_energy(T, verbose=verbose) / \ (c.kb('eV/K') * T) return GoRT
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 get_SoR(self, T, P=c.P0('bar')): """Calculates the dimensionless entropy :math:`\\frac{S^{trans}}{R}=1+\\frac{n_{degrees}}{2}+\\log\\bigg(\\big( \\frac{2\\pi mk_bT}{h^2})^\\frac{n_{degrees}}{2}\\frac{RT}{PN_a}\\bigg)` Parameters ---------- T : float Temperature in K P : float, optional Pressure (bar) or pressure-like quantity. Default is atmospheric pressure Returns ------- SoR_trans : float Translational dimensionless entropy """ V = self.get_V(T=T, P=P) unit_mass = self.molecular_weight *\ c.convert_unit(from_='g', to='kg')/c.Na return 1. + float(self.n_degrees)/2. \ + np.log((2.*np.pi*unit_mass*c.kb('J/K')*T/c.h('J s')**2) ** (float(self.n_degrees)/2.)*V/c.Na)
def test_convert_unit(self): self.assertEqual(c.convert_unit(num=0., from_='C', to='K'), 273.15) self.assertEqual(c.convert_unit(from_='m', to='cm'), 100.) with self.assertRaises(ValueError): c.convert_unit(from_='cm', to='J') with self.assertRaises(ValueError): c.convert_unit(from_='arbitrary unit', to='J') with self.assertRaises(ValueError): c.convert_unit(from_='cm', to='arbitrary unit')
def get_Pc(self): """Calculates the critical pressure Returns ------- Pc : float Critical pressure in bar """ return self.a / 27. / self.b**2 * c.convert_unit(from_='Pa', to='bar')
def get_rot_temperatures_from_atoms(atoms, geometry=None): """Calculate the rotational temperatures from ase.Atoms object Parameters ---------- atoms : ase.Atoms object Atoms object geometry : str, optional Geometry of molecule. If not specified, it will be guessed from Atoms object. Returns ------- rot_temperatures : list of float Rotational temperatures """ if geometry is None: geometry = get_geometry_from_atoms(atoms) rot_temperatures = [] for moment in atoms.get_moments_of_inertia(): if np.isclose(0., moment): continue moment_SI = moment*c.convert_unit(from_='amu', to='kg') \ *c.convert_unit(from_='A2', to='m2') rot_temperatures.append(c.inertia_to_temp(moment_SI)) if geometry == 'monatomic': # Expecting all modes to be 0 assert np.isclose(np.sum(rot_temperatures), 0.) return [0.] elif geometry == 'linear': # Expecting one mode to be 0 and the other modes to be identical if not np.isclose(rot_temperatures[0], rot_temperatures[1]): warn('Expected rot_temperatures for linear specie, {}, to be ' 'similar. Values found were:{}'.format( atoms, rot_temperatures)) return [max(rot_temperatures)] elif geometry == 'nonlinear': # Expecting 3 modes. May or may not be equal return rot_temperatures else: raise ValueError('Geometry, {}, not supported.'.format(geometry))
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_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_GoRT(self, temperature, pressure=1.0, verbose=False): """Calculate the dimensionless Gibbs energy. 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': G = self.model.get_gibbs_energy( temperature, pressure=pressure * c.convert_unit(from_='bar', to='Pa'), verbose=verbose) else: G = self.model.get_helmholtz_energy(temperature, verbose=verbose) return G / (c.kb('eV/K') * temperature)
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_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 get_q(self, T, P=c.P0('bar')): """Calculates the partition function :math:`q_{trans} = \\bigg(\\frac{2\\pi \\sum_{i}^{atoms}m_ikT}{h^2} \\bigg)^\\frac {n_{degrees}} {2}V` Parameters ---------- T : float Temperature in K P : float, optional Pressure (bar) or pressure-like quantity. Default is atmospheric pressure Returns ------- q_trans : float Translational partition function """ V = self.get_V(T=T, P=P) unit_mass = self.molecular_weight *\ c.convert_unit(from_='g', to='kg')/c.Na return V*(2*np.pi*c.kb('J/K')*T*unit_mass/c.h('J s')**2) \ ** (float(self.n_degrees)/2.)
# 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') axes[1].plot(T_ref, H_ref, 'ko') axes[2].plot(T_ref, S_ref, 'ko') axes[3].plot(T_ref, H_ref - T_ref * S_ref * c.convert_unit(from_='J', to='kJ'), 'ko') plt.show()