def applyInverseLaplaceTransformMethod(kinetics, E0, Elist, densStates, T=None): """ Calculate the microcanonical rate coefficient for a reaction using the inverse Laplace transform method, where `kinetics` is the high pressure limit rate coefficient, `E0` is the ground-state energy of the transition state, `Elist` is the array of energies in J/mol at which to evaluate the microcanonial rate, and `densStates` is the density of states of the reactant. """ k = numpy.zeros_like((Elist)) if isinstance(kinetics, ArrheniusModel) and (T is not None or (kinetics.Ea >= 0 and kinetics.n >= 0)): A = kinetics.A n = kinetics.n Ea = kinetics.Ea dE = Elist[1] - Elist[0] # The inverse Laplace transform is not defined for Ea < 0 or n < 0 # In these cases we move the offending portion into the preexponential # at the temperature of interest # This is an approximation, but it's not worth a more robust procedure if Ea < 0: A *= math.exp(-Ea / constants.R / T) Ea = 0.0 if n < 0: A *= T**n n = 0.0 if n == 0: # Determine the microcanonical rate directly s = int(math.floor(Ea / dE)) for r in range(len(Elist)): if Elist[r] > E0 and densStates[r] != 0: k[r] = A * densStates[r - s] / densStates[r] elif n > 0.0: import scipy.special # Evaluate the inverse Laplace transform of the T**n piece, which only # exists for n >= 0 phi = numpy.zeros(len(Elist), numpy.float64) for i, E in enumerate(Elist): if E == 0.0: phi[i] = 0.0 else: phi[i] = E**(n-1) / (constants.R**n * scipy.special.gamma(n)) # Evaluate the convolution phi = convolve(phi, densStates, Elist) # Apply to determine the microcanonical rate s = int(math.floor(Ea / dE)) for r in range(len(Elist)): if Elist[r] > E0 and densStates[r] != 0: k[r] = A * phi[r - s] / densStates[r] else: raise ReactionError('Unable to use inverse Laplace transform method for non-Arrhenius kinetics or for n < 0.') return k
def calculateDensitiesOfStates(self, Elist, E0): """ Calculate and return an array containing the density of states for each isomer and reactant channel in the network. `Elist` represents the array of energies in J/mol at which to compute each density of states. The ground-state energies `E0` in J/mol are used to shift each density of states for each configuration to the same zero of energy. The returned density of states is in units of mol/J. """ Ngrains = len(Elist) Nisom = len(self.isomers) Nreac = len(self.reactants) densStates = numpy.zeros((Nisom+Nreac, Ngrains), numpy.float64) dE = Elist[1] - Elist[0] # Densities of states for isomers for i in range(Nisom): logging.debug('Calculating density of states for isomer "%s"' % self.isomers[i]) densStates0 = self.isomers[i].states.getDensityOfStates(Elist) # Shift to common zero of energy r0 = int(round(E0[i] / dE)) if r0 < 0: r0 = 0 densStates[i,r0:] = densStates0[:-r0+len(densStates0)] # Densities of states for reactant channels # (Only if not minimizing the number of density of states calculations) if not settings.minimizeDensityOfStatesCalculations: for n in range(Nreac): r0 = int(round(E0[n+Nisom] / dE)) if self.reactants[n][0].states is not None and self.reactants[n][1].states is not None: logging.debug('Calculating density of states for reactant channel "%s"' % (' + '.join([str(spec) for spec in self.reactants[n]]))) densStates0 = self.reactants[n][0].states.getDensityOfStates(Elist) densStates1 = self.reactants[n][1].states.getDensityOfStates(Elist) densStates0 = states.convolve(densStates0, densStates1, Elist) # Shift to common zero of energy densStates[n+Nisom,r0:] = densStates0[:-r0+len(densStates0)] elif self.reactants[n][0].states is not None: logging.debug('Calculating density of states for reactant channel "%s"' % (' + '.join([str(spec) for spec in self.reactants[n]]))) densStates0 = self.reactants[n][0].states.getDensityOfStates(Elist) # Shift to common zero of energy densStates[n+Nisom,r0:] = densStates0[:-r0+len(densStates0)] elif self.reactants[n][1].states is not None: logging.debug('Calculating density of states for reactant channel "%s"' % (' + '.join([str(spec) for spec in self.reactants[n]]))) densStates0 = self.reactants[n][1].states.getDensityOfStates(Elist) # Shift to common zero of energy densStates[n+Nisom,r0:] = densStates0[:-r0+len(densStates0)] else: logging.debug('NOT calculating density of states for reactant channel "%s"' % (' + '.join([str(spec) for spec in self.reactants[n]]))) logging.debug('') return densStates