def fit_shomate_species(cls, symbol, T, Cp, H0, S0, T_ref = c.T0('K'), elements = None): """ Derives the shomate species from fitting the heat capacity (J/mol/K) and temperature (K) data and including the formation of enthalpy (kJ/mol) and entropy (J/mol/K). Paramters --------- symbol - str Name of shomate polynomial T - (N,) ndarray Temperatures (K) Cp - (N,) ndarray Heat capacities corresponding to T (J/mol/K) H0 - float Enthalpy at reference temperature (kJ/mol) S0 - float Entropy at reference temperature (J/mol/K) T_ref - float Reference temperature (K) elements - dict Stoichiometric make up of species. Keys should be elements. e.g. H2O = {'H': 2, 'O': 1} Returns ------- shomate - Shomate object """ T_low = min(T) T_high = max(T) t = np.array(T)/1000. [a, pcov] = curve_fit(_shomate_Cp, t, np.array(Cp)) a = np.append(a, [0., 0., 0.]) a[5] = H0 - _get_HoRT(T = T_ref, a = a)*c.R('kJ/mol/K')*T_ref a[6] = S0 - _get_SoR(T = T_ref, a = a)*c.R('J/mol/K') a[7] = - _get_HoRT(T = c.T0('K'), a = a)*c.R('kJ/mol/K')*c.T0('K') return cls(symbol = symbol, T_low = T_low, T_high = T_high, a = np.array(a), elements = elements)
def get_reverse_rate_constant(self, T, thermos, use_BEP=False): """ Calculates the reverse rate constant, k_rev Arguments --------- T - float Temperature (K) thermos - dict-like object of thermodynamic objects (e.g. Shomates) Container that holds the thermodynamic objects. The container should use the species found in stoichiometry as keys and the objects should have the methods: get_HoRT() and get_SoRT() use_BEP - boolean Whether to use BEP relationship Returns ------- k_rev - float Reverse rate constant """ #Get activation energy of reverse direction if use_BEP: EoRT = self.get_EoRT_from_BEP( T=T, thermos=thermos) - self.get_HoRT(T=T, thermos=thermos) else: EoRT = self.EoRT - self.get_HoRT(T=T, thermos=thermos) #Get preexponential factor of reverse direction if use_sticking_coefficient: A_fwd = self.get_A_from_sticking_coefficient(T=T) else: A_fwd = self.A A_rev = A_fwd * np.exp(self.get_SoR(T=T, thermos=thermos)) return A_rev * (T / self.T_ref)**self.beta * np.exp( EoRT * c.T0('K') / T)
def get_forward_rate_constant(self, T, thermos=None, use_BEP=False): """ Calculates the forward rate constant, k_fwd Arguments --------- T - float Temperature (K) thermos - dict-like object of thermodynamic objects (e.g. Shomates) Container that holds the thermodynamic objects. The container should use the species found in stoichiometry as keys and the objects should have the method: get_HoRT() use_BEP - boolean Whether to use BEP relationship Returns ------- k_fwd - float Forward rate constant """ if use_BEP: EoRT = self.get_EoRT_from_BEP(T=T, thermos=thermos) else: EoRT = self.EoRT if use_sticking_coefficient: A_fwd = self.get_A_from_sticking_coefficient(T=T) else: A_fwd = self.A return self.A * (T / self.T_ref)**self.beta * np.exp( EoRT * c.T0('K') / T)
def _fit_SoR(self, SoR0, T0=c.T0('K')): """ Calculates the a7 parameter for the NASA polynomial. Parameters ---------- SoR0 - float Dimensionless entropy at reference temperature T0 - float Reference temperature (K) """ T_mid = self.T_mid a7_low = SoR0 - self._custom_SoR(T=T0, a=self.a_low) a7_high = SoR0 - self._custom_SoR(T=T0, a=self.a_high) #Correcting for offset S_low_last_T = self._custom_SoR(T_mid, self.a_low) + a7_low S_high_first_T = self._custom_SoR(T_mid, self.a_high) + a7_high S_offset = S_low_last_T - S_high_first_T self.a_low[6] = a7_low self.a_high[6] = a7_high + S_offset
def fit_NASA(self, T, CpoR, HoRT0, SoR0, T0=c.T0('K')): """ Generate a NASA polynomial given dimensionless heat capacity as a function of temperature, dimensionless enthalpy of formation and dimensionless entropy of formation. Parameters ---------- T - (N,) ndarray Temperatures (K) to fit the polynomial CpoR - (N,) ndarray Dimensionless heat capacities that correspond to T array HoRT0 - float Dimensionless enthalpy to be used as reference. Used to find a6. Value should correspond to T0 SoR0 - float Dimensionless entropy to be used as reference. Used to find a7. Value should correspond to T0 T0 - float Reference temperature used for fitting a6 and a7. """ self.fit_CpoR(T=T, CpoR=CpoR) self._fit_HoRT(HoRT0=HoRT0, T0=T0) self._fit_SoR(SoR0=SoR0, T0=T0)
def _fit_HoRT(self, HoRT0, T0=c.T0('K')): """ Calculates the a6 parameter for the NASA polynomial. Parameters ---------- HoRT0 - float Dimensionless enthalpy at reference temperature T0 - float Reference temperature (K) """ T_mid = self.T_mid a6_low = (HoRT0 - self._custom_HoRT(T0, self.a_low)) * T0 a6_high = (HoRT0 - self._custom_HoRT(T0, self.a_high)) * T0 #Correcting for offset H_low_last_T = self._custom_HoRT(T_mid, self.a_low) + a6_low / T_mid H_high_first_T = self._custom_HoRT(T_mid, self.a_high) + a6_high / T_mid H_offset = H_low_last_T - H_high_first_T self.a_low[5] = a6_low self.a_high[5] = T_mid * (a6_high / T_mid + H_offset)
def read_ref(path, freq_cut_off=0., verbose=True, warn=True): """ Read the reference file to adjust DFT energies to real energies Parameters ---------- path - string Path to the .csv file that will be read freq_cut_off - float Frequency cut off. If frequencies inputted are less than this value, they are ignored since they are assumed to be frustrated rotational modes verbose - boolean Whether or not more detailed information should be printed warn - boolean Whether or not warnings should be printed Returns ------- energies_fund - (N,) ndarray Fundamental energies of the reference species. Used as a correction factor fund_species_CHON - (N, 4) ndarray Number of carbon, hydrogen, oxygen and nitrogen in the fundamental species. rm_list - list Indices to be removed from the fund_species_CHON since the element is not present in any of the reference species """ #Read the reference files species_ref = read_freq(path, freq_cut_off=freq_cut_off, verbose=verbose, warn=warn) #Prepare the fundamental species e_list = ['C', 'H', 'O', 'N'] rm_list = [] fund_species_CHON = np.array([[1, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 2]]) n_s = len(species_ref) H0_dft = np.array([0.] * n_s) H0_exp = np.array([0.] * n_s) species_CHON = np.array([[0] * 4] * n_s) e_sum_list = np.array([0.] * 4) for i, species in enumerate(species_ref): #Calculate properties at T0 H0_dft[i] = species.IdealGasThermo.get_enthalpy(c.T0('K'), verbose=False) H0_exp[i] = species.H0 * c.convert_unit( from_='kJ/mol', to='eV/molecule') #species.H0 from literature #Sets up the CHON matrix species_CHON[i, :] = species.CHON e_sum_list += species.CHON #Used to determine if elements are not needed. e.g. O, N not needed for hydrocarbons #Cleans up the CHON matrix if necessary for i, (element, e_sum) in reversed(list(enumerate(zip(e_list, e_sum_list)))): if e_sum == 0: if verbose: print(('Element %s not needed' % element)) species_CHON = np.delete(species_CHON, i, 1) fund_species_CHON = np.delete(fund_species_CHON, i, 0) fund_species_CHON = np.delete(fund_species_CHON, i, 1) rm_list.append(i) n_e = len(fund_species_CHON) if n_e != n_s and verbose: print(( "Warning: Mismatch between number of elements (%d) and number of reference species." % (n_e, n_s))) #Finds the fundamental energy H0_fund = np.array( [0.] * n_e) #Formation enthalpy for fundamental species. Usually 0 ref_species_from_fund = np.dot( species_CHON, np.linalg.inv(fund_species_CHON) ) #Matrix to go from fundamental species --> reference species H0_rxn = H0_exp - np.dot(ref_species_from_fund, H0_fund) #Since H0_fund = 0, this is H0_exp energies_fund = np.linalg.solve( ref_species_from_fund, (H0_dft - H0_rxn)) #Energy of fundamental species. Used as correction factor return (energies_fund, fund_species_CHON, rm_list)
def dft_to_thermdat(input_path, ref_path='thermdat_ref.csv', T_low=300., T_high=800., write_files=True, out_path='thermdat', verbose=False, warn=False, freq_cut_off=0., pressure=1., add_gas_species=True, gas_path='thermdat_gas'): """ Convert DFT energies and frequencies to NASA polynomials that can be written as a thermdat file Attributes ---------- input_path - string Path to .csv file that contains the DFT information of all species to be written in thermdat ref_path - string Path to .csv file that contains the gas-phase reference information T_low - float Lower temperature bound for the NASA polynomials T_high - float Higher temperature bound for the NASA polynomials write_files - boolean Whether or not the thermdat file should be written out_path - string Path where the thermdat file will be written verbose - boolean Whether or not more detailed output should be given warn - boolean Whether or not warnings should be given freq_cut_off - float Cut off frequency. If vibrational frequencies (in 1/cm) are below this value, they are ignored since they are assumed to be frustrated rotational or translational modes pressure - float Pressure (in atmospheres) to generate the NASA polynomials. Usually this value is left at 1 atm add_gas_species - boolean Whether or not gas-phase species from gas_path should be written to the thermdat file gas_path - string Path to thermdat file that contains the gas-phase species to add Returns ------- thermdat - Thermdat object The successfully converted thermdat object """ T_range = np.linspace(T_low, T_high, T_high - T_low) n_T = len(T_range) print("Opening reference file: %s" % ref_path) (fund_energies, fund_CHON, rm_list) = read_ref(ref_path, verbose=verbose, warn=warn) print('Fundamental energies:') print(fund_energies) print('Fund CHON') print(fund_CHON) #Read the species to be processed print("Opening input file: %s" % input_path) thermdats_dft = read_freq(input_path, verbose=verbose, freq_cut_off=freq_cut_off, warn=warn) for i, thermdat_dft in enumerate(thermdats_dft): print("-" * 10) print("Processing %s..." % thermdat_dft.symbol) thermdat_dft.nasa = Nasa(symbol=thermdat_dft.symbol, T_low=min(T_range), T_high=max(T_range)) print("Calculating heat capacities.") CpoR = thermdat_dft.get_CpoR(T_range) print("Calculating DFT enthalpy of formation") if thermdat_dft.is_gas: H0 = thermdat_dft.IdealGasThermo.get_enthalpy( temperature=c.T0('K'), verbose=verbose) thermo_parameters = thermdat_dft.IdealGasThermo.__dict__ else: thermo_parameters = thermdat_dft.HarmonicThermo.__dict__ if np.sum(thermdat_dft.HarmonicThermo.vib_energies) == 0: H0 = thermdat_dft.HarmonicThermo.potentialenergy else: H0 = thermdat_dft.HarmonicThermo.get_internal_energy( temperature=c.T0('K'), verbose=verbose) print("Adjusting enthalpy to reference") CHON = np.delete(thermdat_dft.CHON, rm_list) TransformCHON = np.dot(CHON, np.linalg.inv(fund_CHON)) HoRT0 = (H0 - np.dot(TransformCHON, fund_energies)) / (c.T0('K') * c.kb('eV/K')) print("Calculating DFT enthalpy of formation") if thermdat_dft.is_gas: S0 = thermdat_dft.IdealGasThermo.get_entropy( temperature=c.T0('K'), pressure=pressure * c.convert_unit(from_='atm', to='Pa'), verbose=verbose) else: if np.sum(thermdat_dft.HarmonicThermo.vib_energies) == 0: S0 = 0. else: S0 = thermdat_dft.HarmonicThermo.get_entropy( temperature=c.T0('K'), verbose=verbose) SoR0 = S0 / c.kb('eV/K') print('Species: {}'.format(thermdat_dft.symbol)) print('thermo parameters: {}'.format(thermo_parameters)) print('HoRT0 = {}'.format(HoRT0)) print('S/R0 = {}'.format(SoR0)) print('#') print("Calculating NASA polynomials") thermdat_dft.nasa.fit_NASA(T_range, CpoR, HoRT0, SoR0) #Add gas-phase species if add_gas_species: print("Opening gas phase file: {}".format(gas_path)) thermdats_gas = Thermdats.from_thermdat(thermdat_path=gas_path, verbose=verbose, warn=warn) print("Attaching gas species") thermdats_dft.extend(thermdats_gas) if write_files: print("Writing to thermdat") thermdats_dft.write_thermdat(out_path, verbose=False) return thermdats_dft
def test_T0(self): self.assertEqual(c.T0('K'), 298.15)