def test_remove_isotope_for_reactions(self): """ Test that remove isotope algorithm works with Reaction objects. """ eth = Species().from_adjacency_list(""" 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """) ethi = Species().from_adjacency_list(""" 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 i13 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """) unlabeled_rxn = Reaction(reactants=[eth], products=[eth]) labeled_rxn = Reaction(reactants=[ethi], products=[ethi]) stripped = remove_isotope(labeled_rxn) self.assertTrue(unlabeled_rxn.is_isomorphic(stripped))
def __init__(self, index=-1, reactants=None, products=None, kinetics=None, reversible=True, transitionState=None, duplicate=False, degeneracy=1, pairs=None, library=None, entry=None): Reaction.__init__(self, index=index, reactants=reactants, products=products, kinetics=kinetics, reversible=reversible, transitionState=transitionState, duplicate=duplicate, degeneracy=degeneracy, pairs=pairs) self.library = library self.family = library self.entry = entry
def __init__(self, index=-1, reactants=None, products=None, specificCollider=None, kinetics=None, network_kinetics=None, reversible=True, transitionState=None, duplicate=False, degeneracy=1, pairs=None, library=None, allow_pdep_route=False, elementary_high_p=False, entry=None): Reaction.__init__( self, index=index, reactants=reactants, products=products, specificCollider=specificCollider, kinetics=kinetics, network_kinetics=network_kinetics, reversible=reversible, transitionState=transitionState, duplicate=duplicate, degeneracy=degeneracy, pairs=pairs, allow_pdep_route=allow_pdep_route, elementary_high_p=elementary_high_p, ) self.library = library self.family = library self.entry = entry
def __init__(self, index=-1, reactants=None, products=None, specific_collider=None, kinetics=None, reversible=True, transition_state=None, duplicate=False, degeneracy=1, pairs=None, depository=None, family=None, entry=None): Reaction.__init__(self, index=index, reactants=reactants, products=products, specific_collider=specific_collider, kinetics=kinetics, reversible=reversible, transition_state=transition_state, duplicate=duplicate, degeneracy=degeneracy, pairs=pairs) self.depository = depository self.family = family self.entry = entry
def fitInterpolationModels(self): configurations = [] configurations.extend(self.network.isomers) configurations.extend(self.network.reactants) configurations.extend(self.network.products) self.network.netReactions = [] Nreac = self.network.Nisom + self.network.Nreac Nprod = Nreac + self.network.Nprod Tmin = self.Tmin.value_si Tmax = self.Tmax.value_si Tdata = self.Tlist.value_si Pmin = self.Pmin.value_si Pmax = self.Pmax.value_si Pdata = self.Plist.value_si for prod in range(Nprod): for reac in range(Nreac): if reac == prod: continue reaction = Reaction( reactants = configurations[reac].species, products = configurations[prod].species, ) kdata = self.K[:,:,prod,reac].copy() order = len(reaction.reactants) kdata *= 1e6 ** (order-1) kunits = {1: 's^-1', 2: 'cm^3/(mol*s)', 3: 'cm^6/(mol^2*s)'}[order] reaction.kinetics = self.fitInterpolationModel(Tdata, Pdata, kdata, kunits) self.network.netReactions.append(reaction)
def fit_interpolation_models(self): """Fit all pressure dependent rates with interpolation models""" configurations = [] configurations.extend(self.network.isomers) configurations.extend(self.network.reactants) configurations.extend(self.network.products) self.network.net_reactions = [] n_reac = self.network.n_isom + self.network.n_reac n_prod = n_reac + self.network.n_prod Tdata, Pdata = self.Tlist.value_si, self.Plist.value_si for prod in range(n_prod): for reac in range(n_reac): if reac == prod: continue reaction = Reaction(reactants=configurations[reac].species, products=configurations[prod].species) kdata = self.K[:, :, prod, reac].copy() order = len(reaction.reactants) kdata *= 1e6 ** (order - 1) k_units = {1: 's^-1', 2: 'cm^3/(mol*s)', 3: 'cm^6/(mol^2*s)'}[order] logging.debug('Fitting master eqn data to kinetics for reaction {}.'.format(reaction)) reaction.kinetics = self.fit_interpolation_model(Tdata, Pdata, kdata, k_units) self.network.net_reactions.append(reaction)
def reaction(index, label, reactant1, product1, forwardDegeneracy, reverseDegeneracy, reactant2=None, product2=None): global forwardReaction, reverseReaction, reactionIndex, reactionLabel reactants = [] reactants.append(Species().fromAdjacencyList(reactant1)) if reactant2: reactants.append(Species().fromAdjacencyList(reactant2)) for reactant in reactants: if reactant.label == '': reactant.label = reactant.molecule[0].toSMILES() products = [] products.append(Species().fromAdjacencyList(product1)) if product2: products.append(Species().fromAdjacencyList(product2)) for product in products: if product.label == '': product.label = product.molecule[0].toSMILES() reactionIndex = index reactionLabel = label forwardReaction = Reaction(reactants=reactants, products=products, degeneracy=forwardDegeneracy) reverseReaction = Reaction(reactants=products, products=reactants, degeneracy=reverseDegeneracy)
def loadEntry(self, index, reactant1, product1, kinetics, reactant2=None, reactant3=None, product2=None, product3=None, degeneracy=1, label='', duplicate=False, reversible=True, reference=None, referenceType='', shortDesc='', longDesc='', history=None ): reactants = [Species(label=reactant1.strip().splitlines()[0].strip(), molecule=[Molecule().fromAdjacencyList(reactant1)])] if reactant2 is not None: reactants.append(Species(label=reactant2.strip().splitlines()[0].strip(), molecule=[Molecule().fromAdjacencyList(reactant2)])) if reactant3 is not None: reactants.append(Species(label=reactant3.strip().splitlines()[0].strip(), molecule=[Molecule().fromAdjacencyList(reactant3)])) products = [Species(label=product1.strip().splitlines()[0].strip(), molecule=[Molecule().fromAdjacencyList(product1)])] if product2 is not None: products.append(Species(label=product2.strip().splitlines()[0].strip(), molecule=[Molecule().fromAdjacencyList(product2)])) if product3 is not None: products.append(Species(label=product3.strip().splitlines()[0].strip(), molecule=[Molecule().fromAdjacencyList(product3)])) comment = "Reaction and kinetics from {0}.".format(self.label) if shortDesc.strip(): comment += "{0!s}\n".format(shortDesc.strip()) if longDesc.strip(): comment += str(re.sub('\s*\n\s*','\n',longDesc)) kinetics.comment = comment.strip() # Perform mass balance check on the reaction rxn = Reaction(reactants=reactants, products=products, degeneracy=degeneracy, duplicate=duplicate, reversible=reversible) if not rxn.isBalanced(): raise DatabaseError('Reaction {0} in kinetics library {1} was not balanced! Please reformulate.'.format(rxn, self.label)) assert index not in self.entries, "Reaction with index {0} already present!".format(index) self.entries[index] = Entry( index = index, label = label, item = rxn, data = kinetics, reference = reference, referenceType = referenceType, shortDesc = shortDesc, longDesc = longDesc.strip(), history = history or [], ) # Convert SMILES to Molecule objects in collision efficiencies if isinstance(kinetics, PDepKineticsModel): efficiencies = {} for smiles, eff in kinetics.efficiencies.items(): if isinstance(smiles, str): efficiencies[Molecule().fromSMILES(smiles)] = eff kinetics.efficiencies = efficiencies
def __init__(self, index=-1, reactants=None, products=None, specificCollider=None, kinetics=None, reversible=True, transitionState=None, duplicate=False, degeneracy=1, pairs=None, depository=None, family=None, entry=None ): Reaction.__init__(self, index=index, reactants=reactants, products=products, specificCollider=specificCollider, kinetics=kinetics, reversible=reversible, transitionState=transitionState, duplicate=duplicate, degeneracy=degeneracy, pairs=pairs ) self.depository = depository self.family = family self.entry = entry
def __init__(self, index=-1, reactants=None, products=None, kinetics=None, reversible=True, transitionState=None, duplicate=False, degeneracy=1, pairs=None, library=None, entry=None ): Reaction.__init__(self, index=index, reactants=reactants, products=products, kinetics=kinetics, reversible=reversible, transitionState=transitionState, duplicate=duplicate, degeneracy=degeneracy, pairs=pairs ) self.library = library self.family = library self.entry = entry
def testRemoveIsotopeForReactions(self): """ Test that remove isotope algorithm works with Reaction objects. """ eth = Species().fromAdjacencyList( """ 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """ ) ethi = Species().fromAdjacencyList( """ 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 i13 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """ ) unlabeledRxn = Reaction(reactants=[eth], products = [eth]) labeledRxn = Reaction(reactants=[ethi], products = [ethi]) stripped = remove_isotope(labeledRxn) self.assertTrue(unlabeledRxn.isIsomorphic(stripped))
def test_compute_derivative(self): rxnList = [] rxnList.append(Reaction(reactants=[self.C2H6], products=[self.CH3,self.CH3], kinetics=Arrhenius(A=(686.375e6,'1/s'), n=4.40721, Ea=(7.82799,'kcal/mol'), T0=(298.15,'K')))) rxnList.append(Reaction(reactants=[self.C2H6,self.CH3], products=[self.C2H5,self.CH4], kinetics=Arrhenius(A=(46.375*6,'m^3/(mol*s)'), n=3.40721, Ea=(6.82799,'kcal/mol'), T0=(298.15,'K')))) rxnList.append(Reaction(reactants=[self.C2H6,self.CH3,self.CH3], products=[self.C2H5,self.C2H5,self.H2], kinetics=Arrhenius(A=(146.375*6,'m^6/(mol^2*s)'), n=2.40721, Ea=(8.82799,'kcal/mol'), T0=(298.15,'K')))) coreSpecies = [self.CH4,self.CH3,self.C2H6,self.C2H5, self.H2] edgeSpecies = [] coreReactions = rxnList edgeReactions = [] numCoreSpecies = len(coreSpecies) c0={self.CH4:0.2,self.CH3:0.1,self.C2H6:0.35,self.C2H5:0.15, self.H2:0.2} rxnSystem0 = LiquidReactor(self.T, c0,termination=[]) rxnSystem0.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions) dfdt0 = rxnSystem0.residual(0.0, rxnSystem0.y, numpy.zeros(rxnSystem0.y.shape))[0] solver_dfdk = rxnSystem0.computeRateDerivative() #print 'Solver d(dy/dt)/dk' #print solver_dfdk integrationTime = 1e-8 modelSettings = ModelSettings(toleranceKeepInEdge = 0,toleranceMoveToCore=1,toleranceInterruptSimulation=0) simulatorSettings = SimulatorSettings() rxnSystem0.termination.append(TerminationTime((integrationTime,'s'))) rxnSystem0.simulate(coreSpecies, coreReactions, [], [], [],[], modelSettings=modelSettings,simulatorSettings=simulatorSettings) y0 = rxnSystem0.y dfdk = numpy.zeros((numCoreSpecies,len(rxnList))) # d(dy/dt)/dk c0={self.CH4:0.2,self.CH3:0.1,self.C2H6:0.35,self.C2H5:0.15, self.H2:0.2} for i in xrange(len(rxnList)): k0 = rxnList[i].getRateCoefficient(self.T) rxnList[i].kinetics.A.value_si = rxnList[i].kinetics.A.value_si*(1+1e-3) dk = rxnList[i].getRateCoefficient(self.T) - k0 rxnSystem = LiquidReactor(self.T, c0,termination=[]) rxnSystem.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions) dfdt = rxnSystem.residual(0.0, rxnSystem.y, numpy.zeros(rxnSystem.y.shape))[0] dfdk[:,i]=(dfdt-dfdt0)/dk rxnSystem.termination.append(TerminationTime((integrationTime,'s'))) modelSettings = ModelSettings(toleranceKeepInEdge = 0,toleranceMoveToCore=1,toleranceInterruptSimulation=0) simulatorSettings = SimulatorSettings() rxnSystem.simulate(coreSpecies, coreReactions,[],[],[],[], modelSettings=modelSettings,simulatorSettings=simulatorSettings) rxnList[i].kinetics.A.value_si = rxnList[i].kinetics.A.value_si/(1+1e-3) # reset A factor for i in xrange(numCoreSpecies): for j in xrange(len(rxnList)): self.assertAlmostEqual(dfdk[i,j], solver_dfdk[i,j], delta=abs(1e-3*dfdk[i,j]))
def setUpClass(cls): """ A method that is run before all unit tests in this class. """ cls.maxDiff = None cls.rmgdb = rmgdb.make_rmg_database_object() rmgdb.load_families_only(cls.rmgdb) cls.rxn1 = ARCReaction(reactants=['CH4', 'OH'], products=['CH3', 'H2O']) cls.rxn1.rmg_reaction = Reaction(reactants=[ Species().fromSMILES(str('C')), Species().fromSMILES(str('[OH]')) ], products=[ Species().fromSMILES( str('[CH3]')), Species().fromSMILES(str('O')) ]) cls.rxn2 = ARCReaction(reactants=['C2H5', 'OH'], products=['C2H4', 'H2O']) cls.rxn2.rmg_reaction = Reaction(reactants=[ Species().fromSMILES(str('C[CH2]')), Species().fromSMILES(str('[OH]')) ], products=[ Species().fromSMILES(str('C=C')), Species().fromSMILES(str('O')) ]) cls.rxn3 = ARCReaction(reactants=['CH3CH2NH'], products=['CH2CH2NH2']) cls.rxn3.rmg_reaction = Reaction( reactants=[Species().fromSMILES(str('CC[NH]'))], products=[Species().fromSMILES(str('[CH2]CN'))])
def determine_rmg_kinetics( rmgdb: RMGDatabase, reaction: Reaction, dh_rxn298: float, ) -> List[Reaction]: """ Determine kinetics for `reaction` (an RMG Reaction object) from RMG's database, if possible. Assigns a list of all matching entries from both libraries and families. Args: rmgdb (RMGDatabase): The RMG database instance. reaction (Reaction): The RMG Reaction object. dh_rxn298 (float): The heat of reaction at 298 K in J/mol. Returns: List[Reaction] All matching RMG reactions (both libraries and families) with a populated ``.kinetics`` attribute. """ reaction = reaction.copy( ) # use a copy to avoid changing atom order in the molecules by RMG rmg_reactions = list() # Libraries: for library in rmgdb.kinetics.libraries.values(): library_reactions = library.get_library_reactions() for library_reaction in library_reactions: if reaction.is_isomorphic(library_reaction): library_reaction.comment = f'Library: {library.label}' rmg_reactions.append(library_reaction) break # Families: fam_list = loop_families(rmgdb, reaction) for family, degenerate_reactions in fam_list: for deg_rxn in degenerate_reactions: template = family.retrieve_template(deg_rxn.template) kinetics = family.estimate_kinetics_using_rate_rules(template)[0] kinetics.change_rate(deg_rxn.degeneracy) kinetics = kinetics.to_arrhenius( dh_rxn298 ) # Convert ArrheniusEP to Arrhenius using the dHrxn at 298K deg_rxn.kinetics = kinetics deg_rxn.comment = f'Family: {deg_rxn.family}' deg_rxn.reactants = reaction.reactants deg_rxn.products = reaction.products rmg_reactions.extend(degenerate_reactions) worked_through_nist_fams = [] # NIST: for family, degenerate_reactions in fam_list: if family not in worked_through_nist_fams: worked_through_nist_fams.append(family) for depo in family.depositories: if 'NIST' in depo.label: for entry in depo.entries.values(): rxn = entry.item rxn.kinetics = entry.data rxn.comment = f'NIST: {entry.index}' if entry.reference is not None: rxn.comment += f'{entry.reference.authors[0]} {entry.reference.year}' rmg_reactions.append(rxn) return rmg_reactions
def reaction(label, reactants, products, transitionState=None, kinetics=None, tunneling=''): """Load a reaction from an input file""" global reaction_dict, species_dict, transition_state_dict if label in reaction_dict: label = label + transitionState if label in reaction_dict: raise ValueError('Multiple occurrences of reaction with label {0!r}.'.format(label)) logging.info('Loading reaction {0}...'.format(label)) reactants = sorted([species_dict[spec] for spec in reactants]) products = sorted([species_dict[spec] for spec in products]) if transitionState: transitionState = transition_state_dict[transitionState] if transitionState and (tunneling == '' or tunneling is None): transitionState.tunneling = None elif tunneling.lower() == 'wigner': transitionState.tunneling = Wigner(frequency=None) elif tunneling.lower() == 'eckart': transitionState.tunneling = Eckart(frequency=None, E0_reac=None, E0_TS=None, E0_prod=None) elif transitionState and not isinstance(tunneling, TunnelingModel): raise ValueError('Unknown tunneling model {0!r}.'.format(tunneling)) rxn = Reaction(label=label, reactants=reactants, products=products, transition_state=transitionState, kinetics=kinetics) if rxn.transition_state is None and rxn.kinetics is None: logging.info('estimating rate of reaction {0} using RMG-database') if not all([m.molecule != [] for m in rxn.reactants + rxn.products]): raise ValueError('chemical structures of reactants and products not available for RMG estimation of ' 'reaction {0}'.format(label)) db = get_db('kinetics') rxns = db.generate_reactions_from_libraries(reactants=rxn.reactants, products=rxn.products) rxns = [r for r in rxns if r.elementary_high_p] if rxns: for r in rxns: if isinstance(rxn.kinetics, PDepKineticsModel): boo = rxn.generate_high_p_limit_kinetics() if boo: rxn = r break if rxns == [] or not boo: logging.info('No library reactions tagged with elementary_high_p found for reaction {0}, generating ' 'reactions from RMG families'.format(label)) rxn = list(db.generate_reactions_from_families(reactants=rxn.reactants, products=rxn.products)) model = CoreEdgeReactionModel() model.verbose_comments = True for r in rxn: model.apply_kinetics_to_reaction(r) if isinstance(rxn, Reaction): reaction_dict[label] = rxn else: for i in range(len(rxn)): reaction_dict[label + str(i)] = rxn[i] return rxn
def compute(self): """ Compute the pressure-dependent rate coefficients :math:`k(T,P)` for the loaded MEASURE calculation. """ # Only proceed if the input network is valid if self.network is None or self.network.errorString != '': raise PDepError('Attempted to run MEASURE calculation with invalid input.') Nisom = len(self.network.isomers) Nreac = len(self.network.reactants) Nprod = len(self.network.products) network = self.network Tmin = self.Tmin.value_si Tmax = self.Tmax.value_si Tlist = self.Tlist.value_si Pmin = self.Pmin.value_si Pmax = self.Pmax.value_si Plist = self.Plist.value_si method = self.method model = self.model # Calculate the rate coefficients K = network.calculateRateCoefficients(Tlist, Plist, method, grainCount=self.grainCount, grainSize=self.grainSize.value_si) # Fit interpolation model from rmgpy.reaction import Reaction from rmgpy.measure.reaction import fitInterpolationModel if model[0] != '': logging.info('Fitting {0} interpolation models...'.format(model[0])) configurations = [] configurations.extend([[isom] for isom in network.isomers]) configurations.extend([reactants for reactants in network.reactants]) configurations.extend([products for products in network.products]) for i in range(Nisom+Nreac+Nprod): for j in range(Nisom+Nreac): if i != j: # Check that we have nonzero k(T,P) values if (numpy.any(K[:,:,i,j]) and not numpy.all(K[:,:,i,j])): raise NetworkError('Zero rate coefficient encountered while updating network {0}.'.format(network)) # Make a new net reaction netReaction = Reaction( reactants=configurations[j], products=configurations[i], kinetics=None, reversible=(i<Nisom+Nreac), ) network.netReactions.append(netReaction) # Set/update the net reaction kinetics using interpolation model netReaction.kinetics = fitInterpolationModel(netReaction, Tlist, Plist, K[:,:,i,j], model, Tmin, Tmax, Pmin, Pmax, errorCheck=True) logging.info('')
def compute(self): """ Compute the pressure-dependent rate coefficients :math:`k(T,P)` for the loaded MEASURE calculation. """ # Only proceed if the input network is valid if self.network is None or self.network.errorString != '': raise PDepError('Attempted to run MEASURE calculation with invalid input.') Nisom = len(self.network.isomers) Nreac = len(self.network.reactants) Nprod = len(self.network.products) network = self.network Tmin = self.Tmin.value Tmax = self.Tmax.value Tlist = self.Tlist.values Pmin = self.Pmin.value Pmax = self.Pmax.value Plist = self.Plist.values method = self.method model = self.model # Calculate the rate coefficients K = network.calculateRateCoefficients(Tlist, Plist, method, grainCount=self.grainCount, grainSize=self.grainSize.value) # Fit interpolation model from rmgpy.reaction import Reaction from rmgpy.measure.reaction import fitInterpolationModel if model[0] != '': logging.info('Fitting {0} interpolation models...'.format(model[0])) configurations = [] configurations.extend([[isom] for isom in network.isomers]) configurations.extend([reactants for reactants in network.reactants]) configurations.extend([products for products in network.products]) for i in range(Nisom+Nreac+Nprod): for j in range(Nisom+Nreac): if i != j: # Check that we have nonzero k(T,P) values if (numpy.any(K[:,:,i,j]) and not numpy.all(K[:,:,i,j])): raise NetworkError('Zero rate coefficient encountered while updating network {0}.'.format(network)) # Make a new net reaction netReaction = Reaction( reactants=configurations[j], products=configurations[i], kinetics=None, reversible=(i<Nisom+Nreac), ) network.netReactions.append(netReaction) # Set/update the net reaction kinetics using interpolation model netReaction.kinetics = fitInterpolationModel(netReaction, Tlist, Plist, K[:,:,i,j], model, Tmin, Tmax, Pmin, Pmax, errorCheck=True) logging.info('')
def loadReaction(label, reactants, products, transitionState, degeneracy=1): global speciesDict, transitionStateDict, reactionDict logging.info('Loading reaction %s...' % label) rxn = Reaction( reactants=[speciesDict[s] for s in reactants], products=[speciesDict[s] for s in products], transitionState=transitionStateDict[transitionState], ) rxn.degeneracy = degeneracy reactionDict[label] = rxn
def reaction(label, reactants, products, transitionState=None, kinetics=None, tunneling=''): global reactionDict, speciesDict, transitionStateDict if label in reactionDict: label = label + transitionState if label in reactionDict: raise ValueError('Multiple occurrences of reaction with label {0!r}.'.format(label)) logging.info('Loading reaction {0}...'.format(label)) reactants = sorted([speciesDict[spec] for spec in reactants]) products = sorted([speciesDict[spec] for spec in products]) if transitionState: transitionState = transitionStateDict[transitionState] if tunneling.lower() == 'wigner': transitionState.tunneling = Wigner(frequency=None) elif tunneling.lower() == 'eckart': transitionState.tunneling = Eckart(frequency=None, E0_reac=None, E0_TS=None, E0_prod=None) elif transitionState and (tunneling == '' or tunneling is None): transitionState.tunneling = None elif transitionState and not isinstance(tunneling, TunnelingModel): raise ValueError('Unknown tunneling model {0!r}.'.format(tunneling)) rxn = Reaction(label=label, reactants=reactants, products=products, transitionState=transitionState, kinetics=kinetics) if rxn.transitionState is None and rxn.kinetics is None: logging.info('estimating rate of reaction {0} using RMG-database') if not all([m.molecule != [] for m in rxn.reactants+rxn.products]): raise ValueError('chemical structures of reactants and products not available for RMG estimation of reaction {0}'.format(label)) for spc in rxn.reactants+rxn.products: print spc.label print spc.molecule db = getDB('kinetics') rxns = db.generate_reactions_from_libraries(reactants=rxn.reactants,products=rxn.products) rxns = [r for r in rxns if r.elementary_high_p] if rxns != []: for r in rxns: if isinstance(rxn.kinetics, PDepKineticsModel): boo = rxn.generate_high_p_limit_kinetics() if boo: rxn = r break if rxns == [] or not boo: logging.info('No library reactions tagged with elementary_high_p found for reaction {0}, generating reactions from RMG families'.format(label)) rxn = list(db.generate_reactions_from_families(reactants=rxn.reactants,products=rxn.products)) model = CoreEdgeReactionModel() model.verboseComments = True for r in rxn: model.applyKineticsToReaction(r) if isinstance(rxn,Reaction): reactionDict[label] = rxn else: for i in xrange(len(rxn)): reactionDict[label+str(i)] = rxn[i] return rxn
def test_rmg_reaction(self): test_reaction = RMGReaction( reactants=[RMGMolecule(smiles="CC"), RMGMolecule(smiles="[O]O")], products=[RMGMolecule(smiles="[CH2]C"), RMGMolecule(smiles="OO")]) self.assertTrue( test_reaction.is_isomorphic(self.reaction.get_rmg_reaction())) self.assertTrue( test_reaction.is_isomorphic(self.reaction2.get_rmg_reaction()))
def getKinetics(self): """ Extracts the kinetic data from the chemkin file for plotting purposes. """ from rmgpy.chemkin import loadChemkinFile from rmgpy.kinetics import ArrheniusEP, Chebyshev from rmgpy.reaction import Reaction from rmgpy.data.base import Entry kineticsDataList = [] chemkinPath = self.path + "/chemkin/chem.inp" dictionaryPath = self.path + "RMG_Dictionary.txt" speciesList, reactionList = loadChemkinFile(chemkinPath, dictionaryPath) for reaction in reactionList: # If the kinetics are ArrheniusEP, replace them with Arrhenius if isinstance(reaction.kinetics, ArrheniusEP): reaction.kinetics = reaction.kinetics.toArrhenius(reaction.getEnthalpyOfReaction(298)) reactants = " + ".join([moleculeToInfo(reactant) for reactant in reaction.reactants]) arrow = "⇔" if reaction.reversible else "→" products = " + ".join([moleculeToInfo(reactant) for reactant in reaction.products]) source = str(reaction).replace("<=>", "=") href = reaction.getURL() entry = Entry() entry.result = reactionList.index(reaction) + 1 forwardKinetics = reaction.kinetics forward = True chemkin = reaction.toChemkin(speciesList) reverseKinetics = reaction.generateReverseRateCoefficient() reverseKinetics.comment = "Fitted reverse reaction. " + reaction.kinetics.comment rev_reaction = Reaction(reactants=reaction.products, products=reaction.reactants, kinetics=reverseKinetics) chemkin_rev = rev_reaction.toChemkin(speciesList) kineticsDataList.append( [ reactants, arrow, products, entry, forwardKinetics, source, href, forward, chemkin, reverseKinetics, chemkin_rev, ] ) return kineticsDataList
def getKinetics(self): """ Extracts the kinetic data from the chemkin file for plotting purposes. """ from rmgpy.chemkin import loadChemkinFile from rmgpy.kinetics import ArrheniusEP, Chebyshev from rmgpy.reaction import Reaction from rmgpy.data.base import Entry kineticsDataList = [] chemkinPath= self.path + '/chemkin/chem.inp' dictionaryPath = self.path + 'RMG_Dictionary.txt' if self.Foreign: readComments = False else: readComments = True if os.path.exists(dictionaryPath): speciesList, reactionList = loadChemkinFile(chemkinPath, dictionaryPath, readComments=readComments) else: speciesList, reactionList = loadChemkinFile(chemkinPath, readComments=readComments) for reaction in reactionList: # If the kinetics are ArrheniusEP, replace them with Arrhenius if isinstance(reaction.kinetics, ArrheniusEP): reaction.kinetics = reaction.kinetics.toArrhenius(reaction.getEnthalpyOfReaction(298)) if os.path.exists(dictionaryPath): reactants = ' + '.join([moleculeToInfo(reactant) for reactant in reaction.reactants]) arrow = '⇔' if reaction.reversible else '→' products = ' + '.join([moleculeToInfo(product) for product in reaction.products]) href = reaction.getURL() else: reactants = ' + '.join([reactant.label for reactant in reaction.reactants]) arrow = '⇔' if reaction.reversible else '→' products = ' + '.join([product.label for product in reaction.products]) href = '' source = str(reaction).replace('<=>','=') entry = Entry() entry.result = reactionList.index(reaction)+1 forwardKinetics = reaction.kinetics forward = True chemkin = reaction.toChemkin(speciesList) reverseKinetics = reaction.generateReverseRateCoefficient() reverseKinetics.comment = 'Fitted reverse reaction. ' + reaction.kinetics.comment rev_reaction = Reaction(reactants = reaction.products, products = reaction.reactants, kinetics = reverseKinetics) chemkin_rev = rev_reaction.toChemkin(speciesList) kineticsDataList.append([reactants, arrow, products, entry, forwardKinetics, source, href, forward, chemkin, reverseKinetics, chemkin_rev]) return kineticsDataList
def getKinetics(self): """ Extracts the kinetic data from the chemkin file for plotting purposes. """ from rmgpy.chemkin import loadChemkinFile from rmgpy.kinetics import ArrheniusEP, Chebyshev from rmgpy.reaction import Reaction from rmgpy.data.base import Entry kineticsDataList = [] chemkinPath= os.path.join(self.path, 'chemkin','chem.inp') dictionaryPath = os.path.join(self.path, 'RMG_Dictionary.txt' ) if self.Foreign: readComments = False else: readComments = True if os.path.exists(dictionaryPath): speciesList, reactionList = loadChemkinFile(chemkinPath, dictionaryPath, readComments=readComments) else: speciesList, reactionList = loadChemkinFile(chemkinPath, readComments=readComments) for reaction in reactionList: # If the kinetics are ArrheniusEP, replace them with Arrhenius if isinstance(reaction.kinetics, ArrheniusEP): reaction.kinetics = reaction.kinetics.toArrhenius(reaction.getEnthalpyOfReaction(298)) if os.path.exists(dictionaryPath): reactants = ' + '.join([moleculeToInfo(reactant) for reactant in reaction.reactants]) arrow = '⇔' if reaction.reversible else '→' products = ' + '.join([moleculeToInfo(product) for product in reaction.products]) href = reaction.getURL() else: reactants = ' + '.join([reactant.label for reactant in reaction.reactants]) arrow = '⇔' if reaction.reversible else '→' products = ' + '.join([product.label for product in reaction.products]) href = '' source = str(reaction).replace('<=>','=') entry = Entry() entry.result = reactionList.index(reaction)+1 forwardKinetics = reaction.kinetics forward = True chemkin = reaction.toChemkin(speciesList) reverseKinetics = reaction.generateReverseRateCoefficient() reverseKinetics.comment = 'Fitted reverse reaction. ' + reaction.kinetics.comment rev_reaction = Reaction(reactants = reaction.products, products = reaction.reactants, kinetics = reverseKinetics) chemkin_rev = rev_reaction.toChemkin(speciesList) kineticsDataList.append([reactants, arrow, products, entry, forwardKinetics, source, href, forward, chemkin, reverseKinetics, chemkin_rev]) return kineticsDataList
def getKineticsDepository(FullDatabase, family, depositoryLabel): """ Retrieve dictionaries of exact kinetics from NIST depository and approximated kinetics for those same reactions from RMG. Note: does NOT average up the database or create any rate rules from training data. If that is desired it must be done prior to entering this function. """ depository = None for tempDepository in family.depositories: if re.search(re.escape(depositoryLabel), tempDepository.label): depository=tempDepository break else: print 'Depository {} not found in {} family.'.format(depositoryLabel, family.label) return exactKinetics={} approxKinetics={} for key, entry in depository.entries.iteritems(): try: reaction=entry.item template=family.getReactionTemplate(reaction) exactKinetics[key]=entry.data approxKinetics[key]=family.rules.estimateKinetics(template)[0] except UndeterminableKineticsError: # See if the reaction was written in the reverse direction reaction = Reaction(reactants = copy.deepcopy(entry.item.products), products = copy.deepcopy(entry.item.reactants), kinetics = copy.deepcopy(entry.data) ) template=family.getReactionTemplate(reaction) # Getting thermo data erases the atomLabels, so do this after finding the template # But we need it for setting the reverse kinetics for spec in reaction.reactants + reaction.products: spec.getThermoData() reverseKinetics = reaction.generateReverseRateCoefficient() reaction.kinetics = reverseKinetics exactKinetics[key]=reaction.kinetics approxKinetics[key]=family.rules.estimateKinetics(template)[0] return exactKinetics, approxKinetics
def test_corespeciesRate(self): "Test if a specific core species rate is equal to 0 over time" c0={self.C2H5: 0.1, self.CH3: 0.1, self.CH4: 0.4, self.C2H6: 0.4} rxn1 = Reaction(reactants=[self.C2H6,self.CH3], products=[self.C2H5,self.CH4], kinetics=Arrhenius(A=(686.375*6,'m^3/(mol*s)'), n=4.40721, Ea=(7.82799,'kcal/mol'), T0=(298.15,'K'))) coreSpecies = [self.CH4,self.CH3,self.C2H6,self.C2H5] edgeSpecies = [] coreReactions = [rxn1] edgeReactions = [] sensitivity=[] terminationConversion = [] sensitivityThreshold=0.001 ConstSpecies = ["CH4"] rxnSystem = LiquidReactor(self.T, c0, terminationConversion, sensitivity,sensitivityThreshold,ConstSpecies) ##The test regarding the writting of constantSPCindices from input file is check with the previous test. rxnSystem.constSPCIndices=[0] rxnSystem.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions) tlist = numpy.array([10**(i/10.0) for i in range(-130, -49)], numpy.float64) # Integrate to get the solution at each time point t = []; y = []; reactionRates = []; speciesRates = [] for t1 in tlist: rxnSystem.advance(t1) t.append(rxnSystem.t) self.assertEqual(rxnSystem.coreSpeciesRates[0], 0,"Core species rate has to be equal to 0 for species hold constant. Here it is equal to {0}".format(rxnSystem.coreSpeciesRates[0]))
def load_old(self, path, groups, num_labels): """ Load a set of old rate rules for kinetics groups into this depository. """ warnings.warn("The old kinetics databases are no longer supported and may be" " removed in version 2.3.", DeprecationWarning) # Parse the old library entries = self.parse_old_library(os.path.join(path, 'rateLibrary.txt'), num_parameters=10, num_labels=num_labels) self.entries = {} for entry in entries: index, label, data, shortDesc = entry if isinstance(data, str): kinetics = data rank = 0 elif isinstance(data, tuple) and len(data) == 2: kinetics, rank = data else: raise DatabaseError('Unexpected data {0!r} for entry {1!s}.'.format(data, entry)) reactants = [groups.entries[l].item for l in label.split(';')] item = Reaction(reactants=reactants, products=[]) entry = Entry( index=index, label=label, item=item, data=kinetics, rank=rank, short_desc=shortDesc ) try: self.entries[label].append(entry) except KeyError: self.entries[label] = [entry] self._load_old_comments(path)
def test_compute_flux(self): """ Test the liquid batch reactor with a simple kinetic model. """ rxn1 = Reaction(reactants=[self.C2H6, self.CH3], products=[self.C2H5, self.CH4], kinetics=Arrhenius(A=(686.375 * 6, 'm^3/(mol*s)'), n=4.40721, Ea=(7.82799, 'kcal/mol'), T0=(298.15, 'K'))) core_species = [self.CH4, self.CH3, self.C2H6, self.C2H5] edge_species = [] core_reactions = [rxn1] edge_reactions = [] c0 = {self.C2H5: 0.1, self.CH3: 0.1, self.CH4: 0.4, self.C2H6: 0.4} rxn_system = LiquidReactor(self.T, c0, 1, termination=[]) rxn_system.initialize_model(core_species, core_reactions, edge_species, edge_reactions) tlist = np.array([10**(i / 10.0) for i in range(-130, -49)], np.float64) # Integrate to get the solution at each time point t, y, reaction_rates, species_rates = [], [], [], [] for t1 in tlist: rxn_system.advance(t1) t.append(rxn_system.t) # You must make a copy of y because it is overwritten by DASSL at # each call to advance() y.append(rxn_system.y.copy()) reaction_rates.append(rxn_system.core_reaction_rates.copy()) species_rates.append(rxn_system.core_species_rates.copy()) # Convert the solution vectors to np arrays t = np.array(t, np.float64) reaction_rates = np.array(reaction_rates, np.float64) species_rates = np.array(species_rates, np.float64) # Check that we're computing the species fluxes correctly for i in range(t.shape[0]): self.assertAlmostEqual(reaction_rates[i, 0], species_rates[i, 0], delta=1e-6 * reaction_rates[i, 0]) self.assertAlmostEqual(reaction_rates[i, 0], -species_rates[i, 1], delta=1e-6 * reaction_rates[i, 0]) self.assertAlmostEqual(reaction_rates[i, 0], -species_rates[i, 2], delta=1e-6 * reaction_rates[i, 0]) self.assertAlmostEqual(reaction_rates[i, 0], species_rates[i, 3], delta=1e-6 * reaction_rates[i, 0]) # Check that we've reached equilibrium self.assertAlmostEqual(reaction_rates[-1, 0], 0.0, delta=1e-2)
def testDuplicateReaction(self): """ Test that the radical addition reaction HCJ=O + CH2O = [CH2]OC=O present in the reaction library "Methylformate", only appears once in the model. """ from rmgpy.reaction import Reaction from rmgpy.molecule import Molecule folder = os.path.join(os.path.dirname(rmgpy.__file__),'tools/data/generate/duplicates') inputFile = os.path.join(folder,'input.py') rmg = RMG() rmg = execute(rmg, inputFile, folder) self.assertIsNotNone(rmg) rxnFlagged = Reaction(reactants=[Molecule(SMILES='[CH]=O'),Molecule(SMILES='C=O')], products=[Molecule(SMILES='[CH2]OC=O')]) count = 0 for reaction in rmg.reactionModel.core.reactions: if reaction.isIsomorphic(rxnFlagged): count += 1 self.assertEquals(count, 1) shutil.rmtree(os.path.join(folder,'pdep'))
def test_determining_rmg_kinetics(self): """Test the determine_rmg_kinetics() function""" r1 = Species().from_smiles('C') r2 = Species().from_smiles('O[O]') p1 = Species().from_smiles('[CH3]') p2 = Species().from_smiles('OO') r1.thermo = self.rmgdb.thermo.get_thermo_data(r1) r2.thermo = self.rmgdb.thermo.get_thermo_data(r2) p1.thermo = self.rmgdb.thermo.get_thermo_data(p1) p2.thermo = self.rmgdb.thermo.get_thermo_data(p2) rxn = Reaction(reactants=[r1, r2], products=[p1, p2]) dh_rxn298 = sum([product.get_enthalpy(298) for product in rxn.products])\ - sum([reactant.get_enthalpy(298) for reactant in rxn.reactants]) rmg_reactions = rmgdb.determine_rmg_kinetics(rmgdb=self.rmgdb, reaction=rxn, dh_rxn298=dh_rxn298) self.assertFalse(rmg_reactions[0].kinetics.is_pressure_dependent()) found_rxn = False for rxn in rmg_reactions: if isinstance(rxn, LibraryReaction ) and rxn.library == 'Klippenstein_Glarborg2016': self.assertAlmostEqual( rxn.kinetics.get_rate_coefficient(1000, 1e5), 38.2514795642, 7) found_rxn = True self.assertTrue(found_rxn)
def loadOld(self, path, groups, numLabels): """ Load a set of old rate rules for kinetics groups into this depository. """ # Parse the old library entries = self.parseOldLibrary(os.path.join(path, 'rateLibrary.txt'), numParameters=10, numLabels=numLabels) self.entries = {} for entry in entries: index, label, data, shortDesc = entry if isinstance(data, (str, unicode)): kinetics = data rank = 0 elif isinstance(data, tuple) and len(data) == 2: kinetics, rank = data else: raise DatabaseError( 'Unexpected data {0!r} for entry {1!s}.'.format( data, entry)) reactants = [groups.entries[l].item for l in label.split(';')] item = Reaction(reactants=reactants, products=[]) entry = Entry(index=index, label=label, item=item, data=kinetics, rank=rank, shortDesc=shortDesc) try: self.entries[label].append(entry) except KeyError: self.entries[label] = [entry] self.__loadOldComments(path)
def getKineticsDepository(FullDatabase, family, depositoryLabel): """ Retrieve dictionaries of exact kinetics from NIST depository and approximated kinetics for those same reactions from RMG. Note: does NOT average up the database or create any rate rules from training data. If that is desired it must be done prior to entering this function. """ depository = None for tempDepository in family.depositories: if re.search(re.escape(depositoryLabel), tempDepository.label): depository = tempDepository break else: print 'Depository {} not found in {} family.'.format( depositoryLabel, family.label) return exactKinetics = {} approxKinetics = {} for key, entry in depository.entries.iteritems(): try: reaction = entry.item template = family.getReactionTemplate(reaction) exactKinetics[key] = entry.data approxKinetics[key] = family.rules.estimateKinetics(template)[0] except UndeterminableKineticsError: # See if the reaction was written in the reverse direction reaction = Reaction(reactants=copy.deepcopy(entry.item.products), products=copy.deepcopy(entry.item.reactants), kinetics=copy.deepcopy(entry.data)) template = family.getReactionTemplate(reaction) # Getting thermo data erases the atomLabels, so do this after finding the template # But we need it for setting the reverse kinetics for spec in reaction.reactants + reaction.products: spec.getThermoData() reverseKinetics = reaction.generateReverseRateCoefficient() reaction.kinetics = reverseKinetics exactKinetics[key] = reaction.kinetics approxKinetics[key] = family.rules.estimateKinetics(template)[0] return exactKinetics, approxKinetics
def load_entry( self, index, label, kinetics, degeneracy=1, duplicate=False, reversible=True, reference=None, referenceType='', shortDesc='', longDesc='', allow_pdep_route=False, elementary_high_p=False, allow_max_rate_violation=False, metal=None, site=None, facet=None, ): """ Method for parsing entries in database files. Note that these argument names are retained for backward compatibility. """ # reactants = [Species(label=reactant1.strip().splitlines()[0].strip(), molecule=[Molecule().from_adjacency_list(reactant1)])] # if reactant2 is not None: reactants.append(Species(label=reactant2.strip().splitlines()[0].strip(), molecule=[Molecule().from_adjacency_list(reactant2)])) # if reactant3 is not None: reactants.append(Species(label=reactant3.strip().splitlines()[0].strip(), molecule=[Molecule().from_adjacency_list(reactant3)])) # # products = [Species(label=product1.strip().splitlines()[0].strip(), molecule=[Molecule().from_adjacency_list(product1)])] # if product2 is not None: products.append(Species(label=product2.strip().splitlines()[0].strip(), molecule=[Molecule().from_adjacency_list(product2)])) # if product3 is not None: products.append(Species(label=product3.strip().splitlines()[0].strip(), molecule=[Molecule().from_adjacency_list(product3)])) # Make a blank reaction rxn = Reaction(reactants=[], products=[], degeneracy=degeneracy, duplicate=duplicate, reversible=reversible, allow_pdep_route=allow_pdep_route, elementary_high_p=elementary_high_p, allow_max_rate_violation=allow_max_rate_violation) # if not rxn.is_balanced(): # raise DatabaseError('Reaction {0} in kinetics library {1} was not balanced! Please reformulate.'.format(rxn, self.label)) # label = str(rxn) assert index not in self.entries, "Index of reaction {0} is not unique!".format( label) self.entries[index] = Entry( index=index, label=label, item=rxn, data=kinetics, reference=reference, reference_type=referenceType, short_desc=shortDesc, long_desc=longDesc.strip(), metal=metal, site=site, facet=facet, )
def makeReaction(self, reaction_string): """" Make a Reaction (containing PseudoSpecies) of from a string like 'Ab=CD' """ reactants, products = reaction_string.split('=') reactants = [PseudoSpecies(i) for i in reactants] products = [PseudoSpecies(i) for i in products] return Reaction(reactants=reactants, products=products)
def testIsDissociation(self): """ Test the Reaction.isDissociation() method. """ isomerization = Reaction(reactants=[Species()], products=[Species()]) association = Reaction(reactants=[Species(),Species()], products=[Species()]) dissociation = Reaction(reactants=[Species()], products=[Species(),Species()]) bimolecular = Reaction(reactants=[Species(),Species()], products=[Species(),Species()]) self.assertFalse(isomerization.isDissociation()) self.assertFalse(association.isDissociation()) self.assertTrue(dissociation.isDissociation()) self.assertFalse(bimolecular.isDissociation())
def rmg_reaction_from_str(self, reaction_string: str): """A helper function for regenerating the RMG Reaction object from a string representation""" reactants, products = reaction_string.split(self.arrow) reactants = [ Species(smiles=smiles) for smiles in reactants.split(self.plus) ] products = [ Species(smiles=smiles) for smiles in products.split(self.plus) ] self.rmg_reaction = Reaction(reactants=reactants, products=products)
def test_give_tlist_for_kineticsjob(self): """ Ensures that the proper temperature ranges are set when Tlist is specified """ rxn = Reaction(transition_state=TransitionState()) t_list = [50.7, 100, 300, 800, 1255] kjob = KineticsJob(rxn, Tlist=(t_list, 'K')) self.assertEqual(min(t_list), kjob.Tmin.value_si) self.assertEqual(max(t_list), kjob.Tmax.value_si) self.assertEqual(len(t_list), kjob.Tcount)
def get_reaction_for_entry(entry, database): """ Return a Reaction object for a given entry that uses Species instead of Molecules (so that we can compute the reaction thermo). """ reaction = Reaction(reactants=[], products=[]) for reactant in entry.item.reactants: reactant.generate_resonance_structures() reactant.thermo = generate_thermo_data(reactant, database) reaction.reactants.append(reactant) for product in entry.item.products: product.generate_resonance_structures() product.thermo = generate_thermo_data(product, database) reaction.products.append(product) reaction.kinetics = entry.data reaction.degeneracy = entry.item.degeneracy return reaction
def pdepreaction(reactants, products, kinetics=None): global network try: rxn = Reaction( reactants = reactants, products = products, kinetics=kinetics, ) except KeyError, e: raise InputError('A reaction was encountered with species "{0}", but that species was not found in the input file.'.format(e.args[0]))
def testDeflateReaction(self): """ Test if the deflateReaction function works. """ molA = Species().fromSMILES('[OH]') molB = Species().fromSMILES('CC') molC = Species().fromSMILES('[CH3]') reactants = [molA, molB] # both reactants were already part of the core: reactantIndices = [1, 2] molDict = {molA.molecule[0]: 1, molB.molecule[0]: 2} rxn = Reaction(reactants=[molA, molB], products=[molC], pairs=[(molA, molC), (molB, molC)]) deflateReaction(rxn, molDict) for spc, t in zip(rxn.reactants, [int, int]): self.assertTrue(isinstance(spc, t)) self.assertEquals(rxn.reactants, reactantIndices) for spc in rxn.products: self.assertTrue(isinstance(spc, Species)) # one of the reactants was not yet part of the core: reactantIndices = [-1, 2] molDict = {molA.molecule[0]: molA, molB.molecule[0]: 2} rxn = Reaction(reactants=[molA, molB], products=[molC], pairs=[(molA, molC), (molB, molC)]) deflateReaction(rxn, molDict) for spc, t in zip(rxn.reactants, [Species, int]): self.assertTrue(isinstance(spc, t), 'Species {} is not of type {}'.format(spc, t)) for spc in rxn.products: self.assertTrue(isinstance(spc, Species))
def test_rmg_reaction_to_str(self): """Test the rmg_reaction_to_str() method and the reaction label generated""" spc1 = Species().fromSMILES(str('CON=O')) spc1.label = str('CONO') spc2 = Species().fromSMILES(str('C[N+](=O)[O-]')) spc2.label = str('CNO2') rmg_reaction = Reaction(reactants=[spc1], products=[spc2]) rxn = ARCReaction(rmg_reaction=rmg_reaction) rxn_str = rxn.rmg_reaction_to_str() self.assertEqual(rxn_str, 'CON=O <=> [O-][N+](=O)C') self.assertEqual(rxn.label, 'CONO <=> CNO2')
def setUp(self): from rmgpy.reaction import Reaction mol1 = MockMolecule(label='mol1') mol2 = MockMolecule(label='mol2') mol3 = MockMolecule(label='mol3') mol4 = MockMolecule(label='mol4') self.rxn = Reaction(reactants=[mol1, mol2], products=[mol3, mol4]) self.rrxn = ReductionReaction(self.rxn)
def testInplaceRemoveIsotopeForReactions(self): """ Test that removeIsotope and redoIsotope works with reactions """ eth = Species().fromAdjacencyList( """ 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """ ) ethi = Species().fromAdjacencyList( """ 1 C u0 p0 c0 {2,S} {3,S} {4,S} {5,S} 2 C u0 p0 c0 i13 {1,S} {6,S} {7,S} {8,S} 3 H u0 p0 c0 {1,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {2,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {2,S} """ ) unlabeledRxn = Reaction(reactants=[eth], products = [eth]) labeledRxn = Reaction(reactants=[ethi], products = [ethi]) storedLabeledRxn = labeledRxn.copy() modifiedAtoms = remove_isotope(labeledRxn, inplace=True) self.assertTrue(unlabeledRxn.isIsomorphic(labeledRxn)) redo_isotope(modifiedAtoms) self.assertTrue(storedLabeledRxn.isIsomorphic(labeledRxn))
def getReactionForEntry(entry, database): """ Return a Reaction object for a given entry that uses Species instead of Molecules (so that we can compute the reaction thermo). """ reaction = Reaction(reactants=[], products=[]) for molecule in entry.item.reactants: molecule.makeHydrogensExplicit() reactant = Species(molecule=[molecule], label=molecule.toSMILES()) reactant.generateResonanceIsomers() reactant.thermo = generateThermoData(reactant, database) reaction.reactants.append(reactant) for molecule in entry.item.products: molecule.makeHydrogensExplicit() product = Species(molecule=[molecule], label=molecule.toSMILES()) product.generateResonanceIsomers() product.thermo = generateThermoData(product, database) reaction.products.append(product) reaction.kinetics = entry.data reaction.degeneracy = entry.item.degeneracy return reaction
def __init__(self, index=-1, reactants=None, products=None, specificCollider=None, kinetics=None, network_kinetics=None, reversible=True, transitionState=None, duplicate=False, degeneracy=1, pairs=None, library=None, allow_pdep_route=False, elementary_high_p=False, allow_max_rate_violation=False, entry=None, ): Reaction.__init__(self, index=index, reactants=reactants, products=products, specificCollider=specificCollider, kinetics=kinetics, network_kinetics=network_kinetics, reversible=reversible, transitionState=transitionState, duplicate=duplicate, degeneracy=degeneracy, pairs=pairs, allow_pdep_route=allow_pdep_route, elementary_high_p=elementary_high_p, allow_max_rate_violation=allow_max_rate_violation, ) self.library = library self.family = library self.entry = entry
class TestReaction(unittest.TestCase): """ Contains unit tests of the Reaction class. """ def setUp(self): """ A method that is called prior to each unit test in this class. """ ethylene = Species( label="C2H4", conformer=Conformer( E0=(44.7127, "kJ/mol"), modes=[ IdealGasTranslation(mass=(28.0313, "amu")), NonlinearRotor(inertia=([3.41526, 16.6498, 20.065], "amu*angstrom^2"), symmetry=4), HarmonicOscillator( frequencies=( [ 828.397, 970.652, 977.223, 1052.93, 1233.55, 1367.56, 1465.09, 1672.25, 3098.46, 3111.7, 3165.79, 3193.54, ], "cm^-1", ) ), ], spinMultiplicity=1, opticalIsomers=1, ), ) hydrogen = Species( label="H", conformer=Conformer( E0=(211.794, "kJ/mol"), modes=[IdealGasTranslation(mass=(1.00783, "amu"))], spinMultiplicity=2, opticalIsomers=1, ), ) ethyl = Species( label="C2H5", conformer=Conformer( E0=(111.603, "kJ/mol"), modes=[ IdealGasTranslation(mass=(29.0391, "amu")), NonlinearRotor(inertia=([4.8709, 22.2353, 23.9925], "amu*angstrom^2"), symmetry=1), HarmonicOscillator( frequencies=( [ 482.224, 791.876, 974.355, 1051.48, 1183.21, 1361.36, 1448.65, 1455.07, 1465.48, 2688.22, 2954.51, 3033.39, 3101.54, 3204.73, ], "cm^-1", ) ), HinderedRotor( inertia=(1.11481, "amu*angstrom^2"), symmetry=6, barrier=(0.244029, "kJ/mol"), semiclassical=None, ), ], spinMultiplicity=2, opticalIsomers=1, ), ) TS = TransitionState( label="TS", conformer=Conformer( E0=(266.694, "kJ/mol"), modes=[ IdealGasTranslation(mass=(29.0391, "amu")), NonlinearRotor(inertia=([6.78512, 22.1437, 22.2114], "amu*angstrom^2"), symmetry=1), HarmonicOscillator( frequencies=( [ 412.75, 415.206, 821.495, 924.44, 982.714, 1024.16, 1224.21, 1326.36, 1455.06, 1600.35, 3101.46, 3110.55, 3175.34, 3201.88, ], "cm^-1", ) ), ], spinMultiplicity=2, opticalIsomers=1, ), frequency=(-750.232, "cm^-1"), ) self.reaction = Reaction( reactants=[hydrogen, ethylene], products=[ethyl], kinetics=Arrhenius( A=(501366000.0, "cm^3/(mol*s)"), n=1.637, Ea=(4.32508, "kJ/mol"), T0=(1, "K"), Tmin=(300, "K"), Tmax=(2500, "K"), ), transitionState=TS, ) # CC(=O)O[O] acetylperoxy = Species( label="acetylperoxy", thermo=Wilhoit( Cp0=(4.0 * constants.R, "J/(mol*K)"), CpInf=(21.0 * constants.R, "J/(mol*K)"), a0=-3.95, a1=9.26, a2=-15.6, a3=8.55, B=(500.0, "K"), H0=(-6.151e04, "J/mol"), S0=(-790.2, "J/(mol*K)"), ), ) # C[C]=O acetyl = Species( label="acetyl", thermo=Wilhoit( Cp0=(4.0 * constants.R, "J/(mol*K)"), CpInf=(15.5 * constants.R, "J/(mol*K)"), a0=0.2541, a1=-0.4712, a2=-4.434, a3=2.25, B=(500.0, "K"), H0=(-1.439e05, "J/mol"), S0=(-524.6, "J/(mol*K)"), ), ) # [O][O] oxygen = Species( label="oxygen", thermo=Wilhoit( Cp0=(3.5 * constants.R, "J/(mol*K)"), CpInf=(4.5 * constants.R, "J/(mol*K)"), a0=-0.9324, a1=26.18, a2=-70.47, a3=44.12, B=(500.0, "K"), H0=(1.453e04, "J/mol"), S0=(-12.19, "J/(mol*K)"), ), ) self.reaction2 = Reaction( reactants=[acetyl, oxygen], products=[acetylperoxy], kinetics=Arrhenius( A=(2.65e12, "cm^3/(mol*s)"), n=0.0, Ea=(0.0, "kJ/mol"), T0=(1, "K"), Tmin=(300, "K"), Tmax=(2000, "K") ), ) def testIsIsomerization(self): """ Test the Reaction.isIsomerization() method. """ isomerization = Reaction(reactants=[Species()], products=[Species()]) association = Reaction(reactants=[Species(), Species()], products=[Species()]) dissociation = Reaction(reactants=[Species()], products=[Species(), Species()]) bimolecular = Reaction(reactants=[Species(), Species()], products=[Species(), Species()]) self.assertTrue(isomerization.isIsomerization()) self.assertFalse(association.isIsomerization()) self.assertFalse(dissociation.isIsomerization()) self.assertFalse(bimolecular.isIsomerization()) def testIsAssociation(self): """ Test the Reaction.isAssociation() method. """ isomerization = Reaction(reactants=[Species()], products=[Species()]) association = Reaction(reactants=[Species(), Species()], products=[Species()]) dissociation = Reaction(reactants=[Species()], products=[Species(), Species()]) bimolecular = Reaction(reactants=[Species(), Species()], products=[Species(), Species()]) self.assertFalse(isomerization.isAssociation()) self.assertTrue(association.isAssociation()) self.assertFalse(dissociation.isAssociation()) self.assertFalse(bimolecular.isAssociation()) def testIsDissociation(self): """ Test the Reaction.isDissociation() method. """ isomerization = Reaction(reactants=[Species()], products=[Species()]) association = Reaction(reactants=[Species(), Species()], products=[Species()]) dissociation = Reaction(reactants=[Species()], products=[Species(), Species()]) bimolecular = Reaction(reactants=[Species(), Species()], products=[Species(), Species()]) self.assertFalse(isomerization.isDissociation()) self.assertFalse(association.isDissociation()) self.assertTrue(dissociation.isDissociation()) self.assertFalse(bimolecular.isDissociation()) def testHasTemplate(self): """ Test the Reaction.hasTemplate() method. """ reactants = self.reaction.reactants[:] products = self.reaction.products[:] self.assertTrue(self.reaction.hasTemplate(reactants, products)) self.assertTrue(self.reaction.hasTemplate(products, reactants)) self.assertFalse(self.reaction2.hasTemplate(reactants, products)) self.assertFalse(self.reaction2.hasTemplate(products, reactants)) reactants.reverse() products.reverse() self.assertTrue(self.reaction.hasTemplate(reactants, products)) self.assertTrue(self.reaction.hasTemplate(products, reactants)) self.assertFalse(self.reaction2.hasTemplate(reactants, products)) self.assertFalse(self.reaction2.hasTemplate(products, reactants)) reactants = self.reaction2.reactants[:] products = self.reaction2.products[:] self.assertFalse(self.reaction.hasTemplate(reactants, products)) self.assertFalse(self.reaction.hasTemplate(products, reactants)) self.assertTrue(self.reaction2.hasTemplate(reactants, products)) self.assertTrue(self.reaction2.hasTemplate(products, reactants)) reactants.reverse() products.reverse() self.assertFalse(self.reaction.hasTemplate(reactants, products)) self.assertFalse(self.reaction.hasTemplate(products, reactants)) self.assertTrue(self.reaction2.hasTemplate(reactants, products)) self.assertTrue(self.reaction2.hasTemplate(products, reactants)) def testEnthalpyOfReaction(self): """ Test the Reaction.getEnthalpyOfReaction() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Hlist0 = [ float(v) for v in [ "-146007", "-145886", "-144195", "-141973", "-139633", "-137341", "-135155", "-133093", "-131150", "-129316", ] ] Hlist = self.reaction2.getEnthalpiesOfReaction(Tlist) for i in range(len(Tlist)): self.assertAlmostEqual(Hlist[i] / 1000.0, Hlist0[i] / 1000.0, 2) def testEntropyOfReaction(self): """ Test the Reaction.getEntropyOfReaction() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Slist0 = [ float(v) for v in [ "-156.793", "-156.872", "-153.504", "-150.317", "-147.707", "-145.616", "-143.93", "-142.552", "-141.407", "-140.441", ] ] Slist = self.reaction2.getEntropiesOfReaction(Tlist) for i in range(len(Tlist)): self.assertAlmostEqual(Slist[i], Slist0[i], 2) def testFreeEnergyOfReaction(self): """ Test the Reaction.getFreeEnergyOfReaction() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Glist0 = [ float(v) for v in [ "-114648", "-83137.2", "-52092.4", "-21719.3", "8073.53", "37398.1", "66346.8", "94990.6", "123383", "151565", ] ] Glist = self.reaction2.getFreeEnergiesOfReaction(Tlist) for i in range(len(Tlist)): self.assertAlmostEqual(Glist[i] / 1000.0, Glist0[i] / 1000.0, 2) def testEquilibriumConstantKa(self): """ Test the Reaction.getEquilibriumConstant() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Kalist0 = [ float(v) for v in [ "8.75951e+29", "7.1843e+10", "34272.7", "26.1877", "0.378696", "0.0235579", "0.00334673", "0.000792389", "0.000262777", "0.000110053", ] ] Kalist = self.reaction2.getEquilibriumConstants(Tlist, type="Ka") for i in range(len(Tlist)): self.assertAlmostEqual(Kalist[i] / Kalist0[i], 1.0, 4) def testEquilibriumConstantKc(self): """ Test the Reaction.getEquilibriumConstant() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Kclist0 = [ float(v) for v in [ "1.45661e+28", "2.38935e+09", "1709.76", "1.74189", "0.0314866", "0.00235045", "0.000389568", "0.000105413", "3.93273e-05", "1.83006e-05", ] ] Kclist = self.reaction2.getEquilibriumConstants(Tlist, type="Kc") for i in range(len(Tlist)): self.assertAlmostEqual(Kclist[i] / Kclist0[i], 1.0, 4) def testEquilibriumConstantKp(self): """ Test the Reaction.getEquilibriumConstant() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Kplist0 = [ float(v) for v in [ "8.75951e+24", "718430", "0.342727", "0.000261877", "3.78696e-06", "2.35579e-07", "3.34673e-08", "7.92389e-09", "2.62777e-09", "1.10053e-09", ] ] Kplist = self.reaction2.getEquilibriumConstants(Tlist, type="Kp") for i in range(len(Tlist)): self.assertAlmostEqual(Kplist[i] / Kplist0[i], 1.0, 4) def testStoichiometricCoefficient(self): """ Test the Reaction.getStoichiometricCoefficient() method. """ for reactant in self.reaction.reactants: self.assertEqual(self.reaction.getStoichiometricCoefficient(reactant), -1) for product in self.reaction.products: self.assertEqual(self.reaction.getStoichiometricCoefficient(product), 1) for reactant in self.reaction2.reactants: self.assertEqual(self.reaction.getStoichiometricCoefficient(reactant), 0) for product in self.reaction2.products: self.assertEqual(self.reaction.getStoichiometricCoefficient(product), 0) def testRateCoefficient(self): """ Test the Reaction.getRateCoefficient() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) P = 1e5 for T in Tlist: self.assertAlmostEqual( self.reaction.getRateCoefficient(T, P) / self.reaction.kinetics.getRateCoefficient(T), 1.0, 6 ) def testGenerateReverseRateCoefficient(self): """ Test the Reaction.generateReverseRateCoefficient() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) P = 1e5 reverseKinetics = self.reaction2.generateReverseRateCoefficient() for T in Tlist: kr0 = self.reaction2.getRateCoefficient(T, P) / self.reaction2.getEquilibriumConstant(T) kr = reverseKinetics.getRateCoefficient(T) self.assertAlmostEqual(kr0 / kr, 1.0, 0) def testGenerateReverseRateCoefficientArrhenius(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the Arrhenius format. """ original_kinetics = Arrhenius( A=(2.65e12, "cm^3/(mol*s)"), n=0.0, Ea=(0.0, "kJ/mol"), T0=(1, "K"), Tmin=(300, "K"), Tmax=(2000, "K") ) self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(original_kinetics.Tmin.value_si, original_kinetics.Tmax.value_si, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) @work_in_progress def testGenerateReverseRateCoefficientArrheniusEP(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the ArrheniusEP format. """ from rmgpy.kinetics import ArrheniusEP original_kinetics = ArrheniusEP( A=(2.65e12, "cm^3/(mol*s)"), n=0.0, alpha=0.5, E0=(41.84, "kJ/mol"), Tmin=(300, "K"), Tmax=(2000, "K") ) self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(original_kinetics.Tmin, original_kinetics.Tmax, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) def testGenerateReverseRateCoefficientPDepArrhenius(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the PDepArrhenius format. """ from rmgpy.kinetics import PDepArrhenius arrhenius0 = Arrhenius( A=(1.0e6, "s^-1"), n=1.0, Ea=(10.0, "kJ/mol"), T0=(300.0, "K"), Tmin=(300.0, "K"), Tmax=(2000.0, "K"), comment="""This data is completely made up""", ) arrhenius1 = Arrhenius( A=(1.0e12, "s^-1"), n=1.0, Ea=(20.0, "kJ/mol"), T0=(300.0, "K"), Tmin=(300.0, "K"), Tmax=(2000.0, "K"), comment="""This data is completely made up""", ) pressures = numpy.array([0.1, 10.0]) arrhenius = [arrhenius0, arrhenius1] Tmin = 300.0 Tmax = 2000.0 Pmin = 0.1 Pmax = 10.0 comment = """This data is completely made up""" original_kinetics = PDepArrhenius( pressures=(pressures, "bar"), arrhenius=arrhenius, Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), Pmin=(Pmin, "bar"), Pmax=(Pmax, "bar"), comment=comment, ) self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) def testGenerateReverseRateCoefficientMultiArrhenius(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the MultiArrhenius format. """ from rmgpy.kinetics import MultiArrhenius pressures = numpy.array([0.1, 10.0]) Tmin = 300.0 Tmax = 2000.0 Pmin = 0.1 Pmax = 10.0 comment = """This data is completely made up""" arrhenius = [ Arrhenius( A=(9.3e-14, "cm^3/(molecule*s)"), n=0.0, Ea=(4740 * constants.R * 0.001, "kJ/mol"), T0=(1, "K"), Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), comment=comment, ), Arrhenius( A=(1.4e-9, "cm^3/(molecule*s)"), n=0.0, Ea=(11200 * constants.R * 0.001, "kJ/mol"), T0=(1, "K"), Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), comment=comment, ), ] original_kinetics = MultiArrhenius(arrhenius=arrhenius, Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), comment=comment) self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) def testGenerateReverseRateCoefficientMultiPDepArrhenius(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the MultiPDepArrhenius format. """ from rmgpy.kinetics import PDepArrhenius, MultiPDepArrhenius Tmin = 350.0 Tmax = 1500.0 Pmin = 1e-1 Pmax = 1e1 pressures = numpy.array([1e-1, 1e1]) comment = "CH3 + C2H6 <=> CH4 + C2H5 (Baulch 2005)" arrhenius = [ PDepArrhenius( pressures=(pressures, "bar"), arrhenius=[ Arrhenius( A=(9.3e-16, "cm^3/(molecule*s)"), n=0.0, Ea=(4740 * constants.R * 0.001, "kJ/mol"), T0=(1, "K"), Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), comment=comment, ), Arrhenius( A=(9.3e-14, "cm^3/(molecule*s)"), n=0.0, Ea=(4740 * constants.R * 0.001, "kJ/mol"), T0=(1, "K"), Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), comment=comment, ), ], Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), Pmin=(Pmin, "bar"), Pmax=(Pmax, "bar"), comment=comment, ), PDepArrhenius( pressures=(pressures, "bar"), arrhenius=[ Arrhenius( A=(1.4e-11, "cm^3/(molecule*s)"), n=0.0, Ea=(11200 * constants.R * 0.001, "kJ/mol"), T0=(1, "K"), Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), comment=comment, ), Arrhenius( A=(1.4e-9, "cm^3/(molecule*s)"), n=0.0, Ea=(11200 * constants.R * 0.001, "kJ/mol"), T0=(1, "K"), Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), comment=comment, ), ], Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), Pmin=(Pmin, "bar"), Pmax=(Pmax, "bar"), comment=comment, ), ] original_kinetics = MultiPDepArrhenius( arrhenius=arrhenius, Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), Pmin=(Pmin, "bar"), Pmax=(Pmax, "bar"), comment=comment, ) self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) def testGenerateReverseRateCoefficientThirdBody(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the ThirdBody format. """ from rmgpy.kinetics import ThirdBody arrheniusLow = Arrhenius(A=(2.62e33, "cm^6/(mol^2*s)"), n=-4.76, Ea=(10.21, "kJ/mol"), T0=(1, "K")) efficiencies = {"C": 3, "C(=O)=O": 2, "CC": 3, "O": 6, "[Ar]": 0.7, "[C]=O": 1.5, "[H][H]": 2} Tmin = 300.0 Tmax = 2000.0 Pmin = 0.01 Pmax = 100.0 comment = """H + CH3 -> CH4""" thirdBody = ThirdBody( arrheniusLow=arrheniusLow, Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), Pmin=(Pmin, "bar"), Pmax=(Pmax, "bar"), efficiencies=efficiencies, comment=comment, ) original_kinetics = thirdBody self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) def testGenerateReverseRateCoefficientLindemann(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the Lindemann format. """ from rmgpy.kinetics import Lindemann arrheniusHigh = Arrhenius(A=(1.39e16, "cm^3/(mol*s)"), n=-0.534, Ea=(2.243, "kJ/mol"), T0=(1, "K")) arrheniusLow = Arrhenius(A=(2.62e33, "cm^6/(mol^2*s)"), n=-4.76, Ea=(10.21, "kJ/mol"), T0=(1, "K")) efficiencies = {"C": 3, "C(=O)=O": 2, "CC": 3, "O": 6, "[Ar]": 0.7, "[C]=O": 1.5, "[H][H]": 2} Tmin = 300.0 Tmax = 2000.0 Pmin = 0.01 Pmax = 100.0 comment = """H + CH3 -> CH4""" lindemann = Lindemann( arrheniusHigh=arrheniusHigh, arrheniusLow=arrheniusLow, Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), Pmin=(Pmin, "bar"), Pmax=(Pmax, "bar"), efficiencies=efficiencies, comment=comment, ) original_kinetics = lindemann self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) def testGenerateReverseRateCoefficientTroe(self): """ Test the Reaction.generateReverseRateCoefficient() method works for the Troe format. """ from rmgpy.kinetics import Troe arrheniusHigh = Arrhenius(A=(1.39e16, "cm^3/(mol*s)"), n=-0.534, Ea=(2.243, "kJ/mol"), T0=(1, "K")) arrheniusLow = Arrhenius(A=(2.62e33, "cm^6/(mol^2*s)"), n=-4.76, Ea=(10.21, "kJ/mol"), T0=(1, "K")) alpha = 0.783 T3 = 74 T1 = 2941 T2 = 6964 efficiencies = {"C": 3, "C(=O)=O": 2, "CC": 3, "O": 6, "[Ar]": 0.7, "[C]=O": 1.5, "[H][H]": 2} Tmin = 300.0 Tmax = 2000.0 Pmin = 0.01 Pmax = 100.0 comment = """H + CH3 -> CH4""" troe = Troe( arrheniusHigh=arrheniusHigh, arrheniusLow=arrheniusLow, alpha=alpha, T3=(T3, "K"), T1=(T1, "K"), T2=(T2, "K"), Tmin=(Tmin, "K"), Tmax=(Tmax, "K"), Pmin=(Pmin, "bar"), Pmax=(Pmax, "bar"), efficiencies=efficiencies, comment=comment, ) original_kinetics = troe self.reaction2.kinetics = original_kinetics reverseKinetics = self.reaction2.generateReverseRateCoefficient() self.reaction2.kinetics = reverseKinetics # reverse reactants, products to ensure Keq is correctly computed self.reaction2.reactants, self.reaction2.products = self.reaction2.products, self.reaction2.reactants reversereverseKinetics = self.reaction2.generateReverseRateCoefficient() # check that reverting the reverse yields the original Tlist = numpy.arange(Tmin, Tmax, 200.0, numpy.float64) P = 1e5 for T in Tlist: korig = original_kinetics.getRateCoefficient(T, P) krevrev = reversereverseKinetics.getRateCoefficient(T, P) self.assertAlmostEqual(korig / krevrev, 1.0, 0) def testTSTCalculation(self): """ A test of the transition state theory k(T) calculation function, using the reaction H + C2H4 -> C2H5. """ Tlist = 1000.0 / numpy.arange(0.4, 3.35, 0.01) klist = numpy.array([self.reaction.calculateTSTRateCoefficient(T) for T in Tlist]) arrhenius = Arrhenius().fitToData(Tlist, klist, kunits="m^3/(mol*s)") klist2 = numpy.array([arrhenius.getRateCoefficient(T) for T in Tlist]) # Check that the correct Arrhenius parameters are returned self.assertAlmostEqual(arrhenius.A.value_si, 2265.2488, delta=1e-2) self.assertAlmostEqual(arrhenius.n.value_si, 1.45419, delta=1e-4) self.assertAlmostEqual(arrhenius.Ea.value_si, 6645.24, delta=1e-2) # Check that the fit is satisfactory (defined here as always within 5%) for i in range(len(Tlist)): self.assertAlmostEqual(klist[i], klist2[i], delta=5e-2 * klist[i]) def testPickle(self): """ Test that a Reaction object can be successfully pickled and unpickled with no loss of information. """ import cPickle reaction = cPickle.loads(cPickle.dumps(self.reaction, -1)) self.assertEqual(len(self.reaction.reactants), len(reaction.reactants)) self.assertEqual(len(self.reaction.products), len(reaction.products)) for reactant0, reactant in zip(self.reaction.reactants, reaction.reactants): self.assertAlmostEqual(reactant0.conformer.E0.value_si / 1e6, reactant.conformer.E0.value_si / 1e6, 2) self.assertEqual(reactant0.conformer.E0.units, reactant.conformer.E0.units) for product0, product in zip(self.reaction.products, reaction.products): self.assertAlmostEqual(product0.conformer.E0.value_si / 1e6, product.conformer.E0.value_si / 1e6, 2) self.assertEqual(product0.conformer.E0.units, product.conformer.E0.units) self.assertAlmostEqual( self.reaction.transitionState.conformer.E0.value_si / 1e6, reaction.transitionState.conformer.E0.value_si / 1e6, 2, ) self.assertEqual(self.reaction.transitionState.conformer.E0.units, reaction.transitionState.conformer.E0.units) self.assertAlmostEqual( self.reaction.transitionState.frequency.value_si, reaction.transitionState.frequency.value_si, 2 ) self.assertEqual(self.reaction.transitionState.frequency.units, reaction.transitionState.frequency.units) self.assertAlmostEqual(self.reaction.kinetics.A.value_si, reaction.kinetics.A.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.n.value_si, reaction.kinetics.n.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.T0.value_si, reaction.kinetics.T0.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.Ea.value_si, reaction.kinetics.Ea.value_si, delta=1e-6) self.assertEqual(self.reaction.kinetics.comment, reaction.kinetics.comment) self.assertEqual(self.reaction.duplicate, reaction.duplicate) self.assertEqual(self.reaction.degeneracy, reaction.degeneracy) def testOutput(self): """ Test that a Reaction object can be successfully reconstructed from its repr() output with no loss of information. """ exec("reaction = %r" % (self.reaction)) self.assertEqual(len(self.reaction.reactants), len(reaction.reactants)) self.assertEqual(len(self.reaction.products), len(reaction.products)) for reactant0, reactant in zip(self.reaction.reactants, reaction.reactants): self.assertAlmostEqual(reactant0.conformer.E0.value_si / 1e6, reactant.conformer.E0.value_si / 1e6, 2) self.assertEqual(reactant0.conformer.E0.units, reactant.conformer.E0.units) for product0, product in zip(self.reaction.products, reaction.products): self.assertAlmostEqual(product0.conformer.E0.value_si / 1e6, product.conformer.E0.value_si / 1e6, 2) self.assertEqual(product0.conformer.E0.units, product.conformer.E0.units) self.assertAlmostEqual( self.reaction.transitionState.conformer.E0.value_si / 1e6, reaction.transitionState.conformer.E0.value_si / 1e6, 2, ) self.assertEqual(self.reaction.transitionState.conformer.E0.units, reaction.transitionState.conformer.E0.units) self.assertAlmostEqual( self.reaction.transitionState.frequency.value_si, reaction.transitionState.frequency.value_si, 2 ) self.assertEqual(self.reaction.transitionState.frequency.units, reaction.transitionState.frequency.units) self.assertAlmostEqual(self.reaction.kinetics.A.value_si, reaction.kinetics.A.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.n.value_si, reaction.kinetics.n.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.T0.value_si, reaction.kinetics.T0.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.Ea.value_si, reaction.kinetics.Ea.value_si, delta=1e-6) self.assertEqual(self.reaction.kinetics.comment, reaction.kinetics.comment) self.assertEqual(self.reaction.duplicate, reaction.duplicate) self.assertEqual(self.reaction.degeneracy, reaction.degeneracy)
def loadFAMEInput(path, moleculeDict=None): """ Load the contents of a FAME input file into the MEASURE object. FAME is an early version of MEASURE written in Fortran and used by RMG-Java. This script enables importing FAME input files into MEASURE so we can use the additional functionality that MEASURE provides. Note that it is mostly designed to load the FAME input files generated automatically by RMG-Java, and may not load hand-crafted FAME input files. If you specify a `moleculeDict`, then this script will use it to associate the species with their structures. """ def readMeaningfulLine(f): line = f.readline() while line != '': line = line.strip() if len(line) > 0 and line[0] != '#': return line else: line = f.readline() return '' moleculeDict = moleculeDict or {} logging.info('Loading file "{0}"...'.format(path)) f = open(path) job = PressureDependenceJob(network=None) # Read method method = readMeaningfulLine(f).lower() if method == 'modifiedstrongcollision': job.method = 'modified strong collision' elif method == 'reservoirstate': job.method = 'reservoir state' # Read temperatures Tcount, Tunits, Tmin, Tmax = readMeaningfulLine(f).split() job.Tmin = Quantity(float(Tmin), Tunits) job.Tmax = Quantity(float(Tmax), Tunits) job.Tcount = int(Tcount) Tlist = [] for i in range(int(Tcount)): Tlist.append(float(readMeaningfulLine(f))) job.Tlist = Quantity(Tlist, Tunits) # Read pressures Pcount, Punits, Pmin, Pmax = readMeaningfulLine(f).split() job.Pmin = Quantity(float(Pmin), Punits) job.Pmax = Quantity(float(Pmax), Punits) job.Pcount = int(Pcount) Plist = [] for i in range(int(Pcount)): Plist.append(float(readMeaningfulLine(f))) job.Plist = Quantity(Plist, Punits) # Read interpolation model model = readMeaningfulLine(f).split() if model[0].lower() == 'chebyshev': job.model = ['chebyshev', int(model[1]), int(model[2])] elif model[0].lower() == 'pdeparrhenius': job.model = ['pdeparrhenius'] # Read grain size or number of grains job.grainCount = 0 job.grainSize = Quantity(0.0, "J/mol") for i in range(2): data = readMeaningfulLine(f).split() if data[0].lower() == 'numgrains': job.grainCount = int(data[1]) elif data[0].lower() == 'grainsize': job.grainSize = Quantity(float(data[2]), data[1]) # Create the Network job.network = Network() # Read collision model data = readMeaningfulLine(f) assert data.lower() == 'singleexpdown' alpha0units, alpha0 = readMeaningfulLine(f).split() T0units, T0 = readMeaningfulLine(f).split() n = readMeaningfulLine(f) energyTransferModel = SingleExponentialDown( alpha0 = Quantity(float(alpha0), alpha0units), T0 = Quantity(float(T0), T0units), n = float(n), ) speciesDict = {} # Read bath gas parameters bathGas = Species(label='bath_gas', energyTransferModel=energyTransferModel) molWtunits, molWt = readMeaningfulLine(f).split() if molWtunits == 'u': molWtunits = 'amu' bathGas.molecularWeight = Quantity(float(molWt), molWtunits) sigmaLJunits, sigmaLJ = readMeaningfulLine(f).split() epsilonLJunits, epsilonLJ = readMeaningfulLine(f).split() assert epsilonLJunits == 'J' bathGas.lennardJones = LennardJones( sigma = Quantity(float(sigmaLJ), sigmaLJunits), epsilon = Quantity(float(epsilonLJ) / constants.kB, 'K'), ) job.network.bathGas = {bathGas: 1.0} # Read species data Nspec = int(readMeaningfulLine(f)) for i in range(Nspec): species = Species() species.conformer = Conformer() # Read species label species.label = readMeaningfulLine(f) speciesDict[species.label] = species if species.label in moleculeDict: species.molecule = [moleculeDict[species.label]] # Read species E0 E0units, E0 = readMeaningfulLine(f).split() species.conformer.E0 = Quantity(float(E0), E0units) species.conformer.E0.units = 'kJ/mol' # Read species thermo data H298units, H298 = readMeaningfulLine(f).split() S298units, S298 = readMeaningfulLine(f).split() Cpcount, Cpunits = readMeaningfulLine(f).split() Cpdata = [] for i in range(int(Cpcount)): Cpdata.append(float(readMeaningfulLine(f))) if S298units == 'J/mol*K': S298units = 'J/(mol*K)' if Cpunits == 'J/mol*K': Cpunits = 'J/(mol*K)' species.thermo = ThermoData( H298 = Quantity(float(H298), H298units), S298 = Quantity(float(S298), S298units), Tdata = Quantity([300,400,500,600,800,1000,1500], "K"), Cpdata = Quantity(Cpdata, Cpunits), ) # Read species collision parameters molWtunits, molWt = readMeaningfulLine(f).split() if molWtunits == 'u': molWtunits = 'amu' species.molecularWeight = Quantity(float(molWt), molWtunits) sigmaLJunits, sigmaLJ = readMeaningfulLine(f).split() epsilonLJunits, epsilonLJ = readMeaningfulLine(f).split() assert epsilonLJunits == 'J' species.lennardJones = LennardJones( sigma = Quantity(float(sigmaLJ), sigmaLJunits), epsilon = Quantity(float(epsilonLJ) / constants.kB, 'K'), ) # Read species vibrational frequencies freqCount, freqUnits = readMeaningfulLine(f).split() frequencies = [] for j in range(int(freqCount)): frequencies.append(float(readMeaningfulLine(f))) species.conformer.modes.append(HarmonicOscillator( frequencies = Quantity(frequencies, freqUnits), )) # Read species external rotors rotCount, rotUnits = readMeaningfulLine(f).split() if int(rotCount) > 0: raise NotImplementedError('Cannot handle external rotational modes in FAME input.') # Read species internal rotors freqCount, freqUnits = readMeaningfulLine(f).split() frequencies = [] for j in range(int(freqCount)): frequencies.append(float(readMeaningfulLine(f))) barrCount, barrUnits = readMeaningfulLine(f).split() barriers = [] for j in range(int(barrCount)): barriers.append(float(readMeaningfulLine(f))) if barrUnits == 'cm^-1': barrUnits = 'J/mol' barriers = [barr * constants.h * constants.c * constants.Na * 100. for barr in barriers] elif barrUnits in ['Hz', 's^-1']: barrUnits = 'J/mol' barriers = [barr * constants.h * constants.Na for barr in barriers] elif barrUnits != 'J/mol': raise Exception('Unexpected units "{0}" for hindered rotor barrier height.'.format(barrUnits)) inertia = [V0 / 2.0 / (nu * constants.c * 100.)**2 / constants.Na for nu, V0 in zip(frequencies, barriers)] for I, V0 in zip(inertia, barriers): species.conformer.modes.append(HinderedRotor( inertia = Quantity(I,"kg*m^2"), barrier = Quantity(V0,barrUnits), symmetry = 1, )) # Read overall symmetry number species.conformer.spinMultiplicity = int(readMeaningfulLine(f)) # Read isomer, reactant channel, and product channel data Nisom = int(readMeaningfulLine(f)) Nreac = int(readMeaningfulLine(f)) Nprod = int(readMeaningfulLine(f)) for i in range(Nisom): data = readMeaningfulLine(f).split() assert data[0] == '1' job.network.isomers.append(speciesDict[data[1]]) for i in range(Nreac): data = readMeaningfulLine(f).split() assert data[0] == '2' job.network.reactants.append([speciesDict[data[1]], speciesDict[data[2]]]) for i in range(Nprod): data = readMeaningfulLine(f).split() if data[0] == '1': job.network.products.append([speciesDict[data[1]]]) elif data[0] == '2': job.network.products.append([speciesDict[data[1]], speciesDict[data[2]]]) # Read path reactions Nrxn = int(readMeaningfulLine(f)) for i in range(Nrxn): # Read and ignore reaction equation equation = readMeaningfulLine(f) reaction = Reaction(transitionState=TransitionState(), reversible=True) job.network.pathReactions.append(reaction) reaction.transitionState.conformer = Conformer() # Read reactant and product indices data = readMeaningfulLine(f).split() reac = int(data[0]) - 1 prod = int(data[1]) - 1 if reac < Nisom: reaction.reactants = [job.network.isomers[reac]] elif reac < Nisom+Nreac: reaction.reactants = job.network.reactants[reac-Nisom] else: reaction.reactants = job.network.products[reac-Nisom-Nreac] if prod < Nisom: reaction.products = [job.network.isomers[prod]] elif prod < Nisom+Nreac: reaction.products = job.network.reactants[prod-Nisom] else: reaction.products = job.network.products[prod-Nisom-Nreac] # Read reaction E0 E0units, E0 = readMeaningfulLine(f).split() reaction.transitionState.conformer.E0 = Quantity(float(E0), E0units) reaction.transitionState.conformer.E0.units = 'kJ/mol' # Read high-pressure limit kinetics data = readMeaningfulLine(f) assert data.lower() == 'arrhenius' Aunits, A = readMeaningfulLine(f).split() if '/' in Aunits: index = Aunits.find('/') Aunits = '{0}/({1})'.format(Aunits[0:index], Aunits[index+1:]) Eaunits, Ea = readMeaningfulLine(f).split() n = readMeaningfulLine(f) reaction.kinetics = Arrhenius( A = Quantity(float(A), Aunits), Ea = Quantity(float(Ea), Eaunits), n = Quantity(float(n)), ) reaction.kinetics.Ea.units = 'kJ/mol' f.close() job.network.isomers = [Configuration(isomer) for isomer in job.network.isomers] job.network.reactants = [Configuration(*reactants) for reactants in job.network.reactants] job.network.products = [Configuration(*products) for products in job.network.products] return job
class TestReaction(unittest.TestCase): """ Contains unit tests of the Reaction class. """ def setUp(self): """ A method that is called prior to each unit test in this class. """ ethylene = Species( label = 'C2H4', conformer = Conformer( E0 = (44.7127, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (28.0313, 'amu'), ), NonlinearRotor( inertia = ( [3.41526, 16.6498, 20.065], 'amu*angstrom^2', ), symmetry = 4, ), HarmonicOscillator( frequencies = ( [828.397, 970.652, 977.223, 1052.93, 1233.55, 1367.56, 1465.09, 1672.25, 3098.46, 3111.7, 3165.79, 3193.54], 'cm^-1', ), ), ], spinMultiplicity = 1, opticalIsomers = 1, ), ) hydrogen = Species( label = 'H', conformer = Conformer( E0 = (211.794, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (1.00783, 'amu'), ), ], spinMultiplicity = 2, opticalIsomers = 1, ), ) ethyl = Species( label = 'C2H5', conformer = Conformer( E0 = (111.603, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (29.0391, 'amu'), ), NonlinearRotor( inertia = ( [4.8709, 22.2353, 23.9925], 'amu*angstrom^2', ), symmetry = 1, ), HarmonicOscillator( frequencies = ( [482.224, 791.876, 974.355, 1051.48, 1183.21, 1361.36, 1448.65, 1455.07, 1465.48, 2688.22, 2954.51, 3033.39, 3101.54, 3204.73], 'cm^-1', ), ), HinderedRotor( inertia = (1.11481, 'amu*angstrom^2'), symmetry = 6, barrier = (0.244029, 'kJ/mol'), semiclassical = None, ), ], spinMultiplicity = 2, opticalIsomers = 1, ), ) TS = TransitionState( label = 'TS', conformer = Conformer( E0 = (266.694, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (29.0391, 'amu'), ), NonlinearRotor( inertia = ( [6.78512, 22.1437, 22.2114], 'amu*angstrom^2', ), symmetry = 1, ), HarmonicOscillator( frequencies = ( [412.75, 415.206, 821.495, 924.44, 982.714, 1024.16, 1224.21, 1326.36, 1455.06, 1600.35, 3101.46, 3110.55, 3175.34, 3201.88], 'cm^-1', ), ), ], spinMultiplicity = 2, opticalIsomers = 1, ), frequency = (-750.232, 'cm^-1'), ) self.reaction = Reaction( reactants = [hydrogen, ethylene], products = [ethyl], kinetics = Arrhenius( A = (501366000.0, 'cm^3/(mol*s)'), n = 1.637, Ea = (4.32508, 'kJ/mol'), T0 = (1, 'K'), Tmin = (300, 'K'), Tmax = (2500, 'K'), ), transitionState = TS, ) # CC(=O)O[O] acetylperoxy = Species( label='acetylperoxy', thermo=Wilhoit(Cp0=(4.0*constants.R,"J/(mol*K)"), CpInf=(21.0*constants.R,"J/(mol*K)"), a0=-3.95, a1=9.26, a2=-15.6, a3=8.55, B=(500.0,"K"), H0=(-6.151e+04,"J/mol"), S0=(-790.2,"J/(mol*K)")), ) # C[C]=O acetyl = Species( label='acetyl', thermo=Wilhoit(Cp0=(4.0*constants.R,"J/(mol*K)"), CpInf=(15.5*constants.R,"J/(mol*K)"), a0=0.2541, a1=-0.4712, a2=-4.434, a3=2.25, B=(500.0,"K"), H0=(-1.439e+05,"J/mol"), S0=(-524.6,"J/(mol*K)")), ) # [O][O] oxygen = Species( label='oxygen', thermo=Wilhoit(Cp0=(3.5*constants.R,"J/(mol*K)"), CpInf=(4.5*constants.R,"J/(mol*K)"), a0=-0.9324, a1=26.18, a2=-70.47, a3=44.12, B=(500.0,"K"), H0=(1.453e+04,"J/mol"), S0=(-12.19,"J/(mol*K)")), ) self.reaction2 = Reaction( reactants=[acetyl, oxygen], products=[acetylperoxy], kinetics = Arrhenius( A = (2.65e12, 'cm^3/(mol*s)'), n = 0.0, Ea = (0.0, 'kJ/mol'), T0 = (1, 'K'), Tmin = (300, 'K'), Tmax = (2000, 'K'), ), ) def testIsIsomerization(self): """ Test the Reaction.isIsomerization() method. """ isomerization = Reaction(reactants=[Species()], products=[Species()]) association = Reaction(reactants=[Species(),Species()], products=[Species()]) dissociation = Reaction(reactants=[Species()], products=[Species(),Species()]) bimolecular = Reaction(reactants=[Species(),Species()], products=[Species(),Species()]) self.assertTrue(isomerization.isIsomerization()) self.assertFalse(association.isIsomerization()) self.assertFalse(dissociation.isIsomerization()) self.assertFalse(bimolecular.isIsomerization()) def testIsAssociation(self): """ Test the Reaction.isAssociation() method. """ isomerization = Reaction(reactants=[Species()], products=[Species()]) association = Reaction(reactants=[Species(),Species()], products=[Species()]) dissociation = Reaction(reactants=[Species()], products=[Species(),Species()]) bimolecular = Reaction(reactants=[Species(),Species()], products=[Species(),Species()]) self.assertFalse(isomerization.isAssociation()) self.assertTrue(association.isAssociation()) self.assertFalse(dissociation.isAssociation()) self.assertFalse(bimolecular.isAssociation()) def testIsDissociation(self): """ Test the Reaction.isDissociation() method. """ isomerization = Reaction(reactants=[Species()], products=[Species()]) association = Reaction(reactants=[Species(),Species()], products=[Species()]) dissociation = Reaction(reactants=[Species()], products=[Species(),Species()]) bimolecular = Reaction(reactants=[Species(),Species()], products=[Species(),Species()]) self.assertFalse(isomerization.isDissociation()) self.assertFalse(association.isDissociation()) self.assertTrue(dissociation.isDissociation()) self.assertFalse(bimolecular.isDissociation()) def testHasTemplate(self): """ Test the Reaction.hasTemplate() method. """ reactants = self.reaction.reactants[:] products = self.reaction.products[:] self.assertTrue(self.reaction.hasTemplate(reactants, products)) self.assertTrue(self.reaction.hasTemplate(products, reactants)) self.assertFalse(self.reaction2.hasTemplate(reactants, products)) self.assertFalse(self.reaction2.hasTemplate(products, reactants)) reactants.reverse() products.reverse() self.assertTrue(self.reaction.hasTemplate(reactants, products)) self.assertTrue(self.reaction.hasTemplate(products, reactants)) self.assertFalse(self.reaction2.hasTemplate(reactants, products)) self.assertFalse(self.reaction2.hasTemplate(products, reactants)) reactants = self.reaction2.reactants[:] products = self.reaction2.products[:] self.assertFalse(self.reaction.hasTemplate(reactants, products)) self.assertFalse(self.reaction.hasTemplate(products, reactants)) self.assertTrue(self.reaction2.hasTemplate(reactants, products)) self.assertTrue(self.reaction2.hasTemplate(products, reactants)) reactants.reverse() products.reverse() self.assertFalse(self.reaction.hasTemplate(reactants, products)) self.assertFalse(self.reaction.hasTemplate(products, reactants)) self.assertTrue(self.reaction2.hasTemplate(reactants, products)) self.assertTrue(self.reaction2.hasTemplate(products, reactants)) def testEnthalpyOfReaction(self): """ Test the Reaction.getEnthalpyOfReaction() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Hlist0 = [float(v) for v in ['-146007', '-145886', '-144195', '-141973', '-139633', '-137341', '-135155', '-133093', '-131150', '-129316']] Hlist = self.reaction2.getEnthalpiesOfReaction(Tlist) for i in range(len(Tlist)): self.assertAlmostEqual(Hlist[i] / 1000., Hlist0[i] / 1000., 2) def testEntropyOfReaction(self): """ Test the Reaction.getEntropyOfReaction() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Slist0 = [float(v) for v in ['-156.793', '-156.872', '-153.504', '-150.317', '-147.707', '-145.616', '-143.93', '-142.552', '-141.407', '-140.441']] Slist = self.reaction2.getEntropiesOfReaction(Tlist) for i in range(len(Tlist)): self.assertAlmostEqual(Slist[i], Slist0[i], 2) def testFreeEnergyOfReaction(self): """ Test the Reaction.getFreeEnergyOfReaction() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Glist0 = [float(v) for v in ['-114648', '-83137.2', '-52092.4', '-21719.3', '8073.53', '37398.1', '66346.8', '94990.6', '123383', '151565']] Glist = self.reaction2.getFreeEnergiesOfReaction(Tlist) for i in range(len(Tlist)): self.assertAlmostEqual(Glist[i] / 1000., Glist0[i] / 1000., 2) def testEquilibriumConstantKa(self): """ Test the Reaction.getEquilibriumConstant() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Kalist0 = [float(v) for v in ['8.75951e+29', '7.1843e+10', '34272.7', '26.1877', '0.378696', '0.0235579', '0.00334673', '0.000792389', '0.000262777', '0.000110053']] Kalist = self.reaction2.getEquilibriumConstants(Tlist, type='Ka') for i in range(len(Tlist)): self.assertAlmostEqual(Kalist[i] / Kalist0[i], 1.0, 4) def testEquilibriumConstantKc(self): """ Test the Reaction.getEquilibriumConstant() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Kclist0 = [float(v) for v in ['1.45661e+28', '2.38935e+09', '1709.76', '1.74189', '0.0314866', '0.00235045', '0.000389568', '0.000105413', '3.93273e-05', '1.83006e-05']] Kclist = self.reaction2.getEquilibriumConstants(Tlist, type='Kc') for i in range(len(Tlist)): self.assertAlmostEqual(Kclist[i] / Kclist0[i], 1.0, 4) def testEquilibriumConstantKp(self): """ Test the Reaction.getEquilibriumConstant() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) Kplist0 = [float(v) for v in ['8.75951e+24', '718430', '0.342727', '0.000261877', '3.78696e-06', '2.35579e-07', '3.34673e-08', '7.92389e-09', '2.62777e-09', '1.10053e-09']] Kplist = self.reaction2.getEquilibriumConstants(Tlist, type='Kp') for i in range(len(Tlist)): self.assertAlmostEqual(Kplist[i] / Kplist0[i], 1.0, 4) def testStoichiometricCoefficient(self): """ Test the Reaction.getStoichiometricCoefficient() method. """ for reactant in self.reaction.reactants: self.assertEqual(self.reaction.getStoichiometricCoefficient(reactant), -1) for product in self.reaction.products: self.assertEqual(self.reaction.getStoichiometricCoefficient(product), 1) for reactant in self.reaction2.reactants: self.assertEqual(self.reaction.getStoichiometricCoefficient(reactant), 0) for product in self.reaction2.products: self.assertEqual(self.reaction.getStoichiometricCoefficient(product), 0) def testRateCoefficient(self): """ Test the Reaction.getRateCoefficient() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) P = 1e5 for T in Tlist: self.assertAlmostEqual(self.reaction.getRateCoefficient(T, P) / self.reaction.kinetics.getRateCoefficient(T), 1.0, 6) def testGenerateReverseRateCoefficient(self): """ Test the Reaction.generateReverseRateCoefficient() method. """ Tlist = numpy.arange(200.0, 2001.0, 200.0, numpy.float64) P = 1e5 reverseKinetics = self.reaction2.generateReverseRateCoefficient() for T in Tlist: kr0 = self.reaction2.getRateCoefficient(T, P) / self.reaction2.getEquilibriumConstant(T) kr = reverseKinetics.getRateCoefficient(T) self.assertAlmostEqual(kr0 / kr, 1.0, 0) def testTSTCalculation(self): """ A test of the transition state theory k(T) calculation function, using the reaction H + C2H4 -> C2H5. """ Tlist = 1000.0/numpy.arange(0.4, 3.35, 0.01) klist = numpy.array([self.reaction.calculateTSTRateCoefficient(T) for T in Tlist]) arrhenius = Arrhenius().fitToData(Tlist, klist, kunits='m^3/(mol*s)') klist2 = numpy.array([arrhenius.getRateCoefficient(T) for T in Tlist]) # Check that the correct Arrhenius parameters are returned self.assertAlmostEqual(arrhenius.A.value_si, 2265.2488, delta=1e-2) self.assertAlmostEqual(arrhenius.n.value_si, 1.45419, delta=1e-4) self.assertAlmostEqual(arrhenius.Ea.value_si, 6645.24, delta=1e-2) # Check that the fit is satisfactory (defined here as always within 5%) for i in range(len(Tlist)): self.assertAlmostEqual(klist[i], klist2[i], delta=5e-2 * klist[i]) def testPickle(self): """ Test that a Reaction object can be successfully pickled and unpickled with no loss of information. """ import cPickle reaction = cPickle.loads(cPickle.dumps(self.reaction,-1)) self.assertEqual(len(self.reaction.reactants), len(reaction.reactants)) self.assertEqual(len(self.reaction.products), len(reaction.products)) for reactant0, reactant in zip(self.reaction.reactants, reaction.reactants): self.assertAlmostEqual(reactant0.conformer.E0.value_si / 1e6, reactant.conformer.E0.value_si / 1e6, 2) self.assertEqual(reactant0.conformer.E0.units, reactant.conformer.E0.units) for product0, product in zip(self.reaction.products, reaction.products): self.assertAlmostEqual(product0.conformer.E0.value_si / 1e6, product.conformer.E0.value_si / 1e6, 2) self.assertEqual(product0.conformer.E0.units, product.conformer.E0.units) self.assertAlmostEqual(self.reaction.transitionState.conformer.E0.value_si / 1e6, reaction.transitionState.conformer.E0.value_si / 1e6, 2) self.assertEqual(self.reaction.transitionState.conformer.E0.units, reaction.transitionState.conformer.E0.units) self.assertAlmostEqual(self.reaction.transitionState.frequency.value_si, reaction.transitionState.frequency.value_si, 2) self.assertEqual(self.reaction.transitionState.frequency.units, reaction.transitionState.frequency.units) self.assertAlmostEqual(self.reaction.kinetics.A.value_si, reaction.kinetics.A.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.n.value_si, reaction.kinetics.n.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.T0.value_si, reaction.kinetics.T0.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.Ea.value_si, reaction.kinetics.Ea.value_si, delta=1e-6) self.assertEqual(self.reaction.kinetics.comment, reaction.kinetics.comment) self.assertEqual(self.reaction.duplicate, reaction.duplicate) self.assertEqual(self.reaction.degeneracy, reaction.degeneracy) def testOutput(self): """ Test that a Reaction object can be successfully reconstructed from its repr() output with no loss of information. """ exec('reaction = %r' % (self.reaction)) self.assertEqual(len(self.reaction.reactants), len(reaction.reactants)) self.assertEqual(len(self.reaction.products), len(reaction.products)) for reactant0, reactant in zip(self.reaction.reactants, reaction.reactants): self.assertAlmostEqual(reactant0.conformer.E0.value_si / 1e6, reactant.conformer.E0.value_si / 1e6, 2) self.assertEqual(reactant0.conformer.E0.units, reactant.conformer.E0.units) for product0, product in zip(self.reaction.products, reaction.products): self.assertAlmostEqual(product0.conformer.E0.value_si / 1e6, product.conformer.E0.value_si / 1e6, 2) self.assertEqual(product0.conformer.E0.units, product.conformer.E0.units) self.assertAlmostEqual(self.reaction.transitionState.conformer.E0.value_si / 1e6, reaction.transitionState.conformer.E0.value_si / 1e6, 2) self.assertEqual(self.reaction.transitionState.conformer.E0.units, reaction.transitionState.conformer.E0.units) self.assertAlmostEqual(self.reaction.transitionState.frequency.value_si, reaction.transitionState.frequency.value_si, 2) self.assertEqual(self.reaction.transitionState.frequency.units, reaction.transitionState.frequency.units) self.assertAlmostEqual(self.reaction.kinetics.A.value_si, reaction.kinetics.A.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.n.value_si, reaction.kinetics.n.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.T0.value_si, reaction.kinetics.T0.value_si, delta=1e-6) self.assertAlmostEqual(self.reaction.kinetics.Ea.value_si, reaction.kinetics.Ea.value_si, delta=1e-6) self.assertEqual(self.reaction.kinetics.comment, reaction.kinetics.comment) self.assertEqual(self.reaction.duplicate, reaction.duplicate) self.assertEqual(self.reaction.degeneracy, reaction.degeneracy)
def getForwardReactionForFamilyEntry(self, entry, family, thermoDatabase): """ For a given `entry` for a reaction of the given reaction `family` (the string label of the family), return the reaction with kinetics and degeneracy for the "forward" direction as defined by the reaction family. For families that are their own reverse, the direction the kinetics is given in will be preserved. If the entry contains functional groups for the reactants, assume that it is given in the forward direction and do nothing. Returns the reaction in the direction consistent with the reaction family template, and the matching template. Note that the returned reaction will have its kinetics and degeneracy set appropriately. In order to reverse the reactions that are given in the reverse of the direction the family is defined, we need to compute the thermodynamics of the reactants and products. For this reason you must also pass the `thermoDatabase` to use to generate the thermo data. """ def generateThermoData(species, thermoDatabase): thermoData = [thermoDatabase.getThermoData(species)] thermoData.sort(key=lambda x: x.getEnthalpy(298)) return thermoData[0] def matchSpeciesToMolecules(species, molecules): if len(species) == len(molecules) == 1: return species[0].isIsomorphic(molecules[0]) elif len(species) == len(molecules) == 2: if species[0].isIsomorphic(molecules[0]) and species[1].isIsomorphic(molecules[1]): return True elif species[0].isIsomorphic(molecules[1]) and species[1].isIsomorphic(molecules[0]): return True return False reaction = None; template = None # Get the indicated reaction family try: groups = self.families[family].groups except KeyError: raise ValueError('Invalid value "{0}" for family parameter.'.format(family)) if all([(isinstance(reactant, Group) or isinstance(reactant, LogicNode)) for reactant in entry.item.reactants]): # The entry is a rate rule, containing functional groups only # By convention, these are always given in the forward direction and # have kinetics defined on a per-site basis reaction = Reaction( reactants = entry.item.reactants[:], products = [], kinetics = entry.data, degeneracy = 1, ) template = [groups.entries[label] for label in entry.label.split(';')] elif (all([isinstance(reactant, Molecule) for reactant in entry.item.reactants]) and all([isinstance(product, Molecule) for product in entry.item.products])): # The entry is a real reaction, containing molecules # These could be defined for either the forward or reverse direction # and could have a reaction-path degeneracy reaction = Reaction(reactants=[], products=[]) for molecule in entry.item.reactants: reactant = Species(molecule=[molecule]) reactant.generateResonanceIsomers() reactant.thermo = generateThermoData(reactant, thermoDatabase) reaction.reactants.append(reactant) for molecule in entry.item.products: product = Species(molecule=[molecule]) product.generateResonanceIsomers() product.thermo = generateThermoData(product, thermoDatabase) reaction.products.append(product) # Generate all possible reactions involving the reactant species generatedReactions = self.generateReactionsFromFamilies([reactant.molecule for reactant in reaction.reactants], [], only_families=[family]) # Remove from that set any reactions that don't produce the desired reactants and products forward = []; reverse = [] for rxn in generatedReactions: if matchSpeciesToMolecules(reaction.reactants, rxn.reactants) and matchSpeciesToMolecules(reaction.products, rxn.products): forward.append(rxn) if matchSpeciesToMolecules(reaction.reactants, rxn.products) and matchSpeciesToMolecules(reaction.products, rxn.reactants): reverse.append(rxn) # We should now know whether the reaction is given in the forward or # reverse direction if len(forward) == 1 and len(reverse) == 0: # The reaction is in the forward direction, so use as-is reaction = forward[0] template = reaction.template # Don't forget to overwrite the estimated kinetics from the database with the kinetics for this entry reaction.kinetics = entry.data elif len(reverse) == 1 and len(forward) == 0: # The reaction is in the reverse direction # First fit Arrhenius kinetics in that direction Tdata = 1000.0 / numpy.arange(0.5, 3.301, 0.1, numpy.float64) kdata = numpy.zeros_like(Tdata) for i in range(Tdata.shape[0]): kdata[i] = entry.data.getRateCoefficient(Tdata[i]) / reaction.getEquilibriumConstant(Tdata[i]) kunits = 'm^3/(mol*s)' if len(reverse[0].reactants) == 2 else 's^-1' kinetics = Arrhenius().fitToData(Tdata, kdata, kunits, T0=1.0) kinetics.Tmin = entry.data.Tmin kinetics.Tmax = entry.data.Tmax kinetics.Pmin = entry.data.Pmin kinetics.Pmax = entry.data.Pmax # Now flip the direction reaction = reverse[0] reaction.kinetics = kinetics template = reaction.template elif len(reverse) > 0 and len(forward) > 0: print 'FAIL: Multiple reactions found for {0!r}.'.format(entry.label) elif len(reverse) == 0 and len(forward) == 0: print 'FAIL: No reactions found for "%s".' % (entry.label) else: print 'FAIL: Unable to estimate kinetics for {0!r}.'.format(entry.label) assert reaction is not None assert template is not None return reaction, template
def testSolveCH3(self): """ Test the surface batch reactor with a nondissociative adsorption of CH3 Here we choose a kinetic model consisting of the adsorption reaction CH3 + X <=> CH3X We use a sticking coefficient for the rate expression. """ CH3 = Species( molecule=[Molecule().fromSMILES("[CH3]")], thermo=NASA(polynomials=[NASAPolynomial(coeffs=[3.91547, 0.00184155, 3.48741e-06, -3.32746e-09, 8.49953e-13, 16285.6, 0.351743], Tmin=(100, 'K'), Tmax=(1337.63, 'K')), NASAPolynomial(coeffs=[3.54146, 0.00476786, -1.82148e-06, 3.28876e-10, -2.22545e-14, 16224, 1.66032], Tmin=(1337.63, 'K'), Tmax=(5000, 'K'))], Tmin=(100, 'K'), Tmax=(5000, 'K'), E0=(135.382, 'kJ/mol'), comment="""Thermo library: primaryThermoLibrary + radical(CH3)""" ), molecularWeight=(15.0345, 'amu'), ) X = Species( molecule=[Molecule().fromAdjacencyList("1 X u0 p0")], thermo=NASA(polynomials=[NASAPolynomial(coeffs=[0, 0, 0, 0, 0, 0, 0], Tmin=(298, 'K'), Tmax=(1000, 'K')), NASAPolynomial(coeffs=[0, 0, 0, 0, 0, 0, 0], Tmin=(1000, 'K'), Tmax=(2000, 'K'))], Tmin=(298, 'K'), Tmax=(2000, 'K'), E0=(-6.19426, 'kJ/mol'), comment="""Thermo library: surfaceThermo""") ) CH3X = Species( molecule=[Molecule().fromAdjacencyList("1 H u0 p0 {2,S} \n 2 X u0 p0 {1,S}")], thermo=NASA(polynomials=[NASAPolynomial(coeffs=[-0.552219, 0.026442, -3.55617e-05, 2.60044e-08, -7.52707e-12, -4433.47, 0.692144], Tmin=(298, 'K'), Tmax=(1000, 'K')), NASAPolynomial(coeffs=[3.62557, 0.00739512, -2.43797e-06, 1.86159e-10, 3.6485e-14, -5187.22, -18.9668], Tmin=(1000, 'K'), Tmax=(2000, 'K'))], Tmin=(298, 'K'), Tmax=(2000, 'K'), E0=(-39.1285, 'kJ/mol'), comment="""Thermo library: surfaceThermo""") ) rxn1 = Reaction(reactants=[CH3, X], products=[CH3X], kinetics=StickingCoefficient(A=0.1, n=0, Ea=(0, 'kcal/mol'), T0=(1, 'K'), Tmin=(200, 'K'), Tmax=(3000, 'K'), comment="""Exact match found for rate rule (Adsorbate;VacantSite)""" ) # kinetics=SurfaceArrhenius(A=(2.7e10, 'cm^3/(mol*s)'), # n=0.5, # Ea=(5.0, 'kJ/mol'), # T0=(1.0, 'K')) ) coreSpecies = [CH3, X, CH3X] edgeSpecies = [] coreReactions = [rxn1] edgeReactions = [] T = 800. initialP = 1.0e5 rxnSystem = SurfaceReactor( T, initialP, nSims=1, initialGasMoleFractions={CH3: 1.0}, initialSurfaceCoverages={X: 1.0}, surfaceVolumeRatio=(1., 'm^-1'), surfaceSiteDensity=(2.72e-9, 'mol/cm^2'), termination=[]) # in chemkin, the sites are mostly occupied in about 1e-8 seconds. rxnSystem.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions) tlist = numpy.logspace(-13, -5, 81, dtype=numpy.float64) print "Surface site density:", rxnSystem.surfaceSiteDensity.value_si print "rxn1 rate coefficient", rxn1.getSurfaceRateCoefficient(rxnSystem.T.value_si, rxnSystem.surfaceSiteDensity.value_si ) # Integrate to get the solution at each time point t = [] y = [] reactionRates = [] speciesRates = [] t.append(rxnSystem.t) # You must make a copy of y because it is overwritten by DASSL at # each call to advance() y.append(rxnSystem.y.copy()) reactionRates.append(rxnSystem.coreReactionRates.copy()) speciesRates.append(rxnSystem.coreSpeciesRates.copy()) print "time: ", t print "moles:", y print "reaction rates:", reactionRates print "species rates:", speciesRates for t1 in tlist: rxnSystem.advance(t1) t.append(rxnSystem.t) # You must make a copy of y because it is overwritten by DASSL at # each call to advance() y.append(rxnSystem.y.copy()) reactionRates.append(rxnSystem.coreReactionRates.copy()) speciesRates.append(rxnSystem.coreSpeciesRates.copy()) # Convert the solution vectors to numpy arrays t = numpy.array(t, numpy.float64) y = numpy.array(y, numpy.float64) reactionRates = numpy.array(reactionRates, numpy.float64) speciesRates = numpy.array(speciesRates, numpy.float64) V = constants.R * rxnSystem.T.value_si * numpy.sum(y) / rxnSystem.initialP.value_si # Check that we're computing the species fluxes correctly for i in range(t.shape[0]): self.assertAlmostEqual(reactionRates[i, 0], -speciesRates[i, 0], delta=1e-6 * reactionRates[i, 0]) self.assertAlmostEqual(reactionRates[i, 0], -speciesRates[i, 1], delta=1e-6 * reactionRates[i, 0]) self.assertAlmostEqual(reactionRates[i, 0], speciesRates[i, 2], delta=1e-6 * reactionRates[i, 0]) # Check that we've reached equilibrium by the end self.assertAlmostEqual(reactionRates[-1, 0], 0.0, delta=1e-2)
def setUp(self): """ A method that is called prior to each unit test in this class. """ ethylene = Species( label = 'C2H4', conformer = Conformer( E0 = (44.7127, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (28.0313, 'amu'), ), NonlinearRotor( inertia = ( [3.41526, 16.6498, 20.065], 'amu*angstrom^2', ), symmetry = 4, ), HarmonicOscillator( frequencies = ( [828.397, 970.652, 977.223, 1052.93, 1233.55, 1367.56, 1465.09, 1672.25, 3098.46, 3111.7, 3165.79, 3193.54], 'cm^-1', ), ), ], spinMultiplicity = 1, opticalIsomers = 1, ), ) hydrogen = Species( label = 'H', conformer = Conformer( E0 = (211.794, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (1.00783, 'amu'), ), ], spinMultiplicity = 2, opticalIsomers = 1, ), ) ethyl = Species( label = 'C2H5', conformer = Conformer( E0 = (111.603, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (29.0391, 'amu'), ), NonlinearRotor( inertia = ( [4.8709, 22.2353, 23.9925], 'amu*angstrom^2', ), symmetry = 1, ), HarmonicOscillator( frequencies = ( [482.224, 791.876, 974.355, 1051.48, 1183.21, 1361.36, 1448.65, 1455.07, 1465.48, 2688.22, 2954.51, 3033.39, 3101.54, 3204.73], 'cm^-1', ), ), HinderedRotor( inertia = (1.11481, 'amu*angstrom^2'), symmetry = 6, barrier = (0.244029, 'kJ/mol'), semiclassical = None, ), ], spinMultiplicity = 2, opticalIsomers = 1, ), ) TS = TransitionState( label = 'TS', conformer = Conformer( E0 = (266.694, 'kJ/mol'), modes = [ IdealGasTranslation( mass = (29.0391, 'amu'), ), NonlinearRotor( inertia = ( [6.78512, 22.1437, 22.2114], 'amu*angstrom^2', ), symmetry = 1, ), HarmonicOscillator( frequencies = ( [412.75, 415.206, 821.495, 924.44, 982.714, 1024.16, 1224.21, 1326.36, 1455.06, 1600.35, 3101.46, 3110.55, 3175.34, 3201.88], 'cm^-1', ), ), ], spinMultiplicity = 2, opticalIsomers = 1, ), frequency = (-750.232, 'cm^-1'), ) self.reaction = Reaction( reactants = [hydrogen, ethylene], products = [ethyl], kinetics = Arrhenius( A = (501366000.0, 'cm^3/(mol*s)'), n = 1.637, Ea = (4.32508, 'kJ/mol'), T0 = (1, 'K'), Tmin = (300, 'K'), Tmax = (2500, 'K'), ), transitionState = TS, ) # CC(=O)O[O] acetylperoxy = Species( label='acetylperoxy', thermo=Wilhoit(Cp0=(4.0*constants.R,"J/(mol*K)"), CpInf=(21.0*constants.R,"J/(mol*K)"), a0=-3.95, a1=9.26, a2=-15.6, a3=8.55, B=(500.0,"K"), H0=(-6.151e+04,"J/mol"), S0=(-790.2,"J/(mol*K)")), ) # C[C]=O acetyl = Species( label='acetyl', thermo=Wilhoit(Cp0=(4.0*constants.R,"J/(mol*K)"), CpInf=(15.5*constants.R,"J/(mol*K)"), a0=0.2541, a1=-0.4712, a2=-4.434, a3=2.25, B=(500.0,"K"), H0=(-1.439e+05,"J/mol"), S0=(-524.6,"J/(mol*K)")), ) # [O][O] oxygen = Species( label='oxygen', thermo=Wilhoit(Cp0=(3.5*constants.R,"J/(mol*K)"), CpInf=(4.5*constants.R,"J/(mol*K)"), a0=-0.9324, a1=26.18, a2=-70.47, a3=44.12, B=(500.0,"K"), H0=(1.453e+04,"J/mol"), S0=(-12.19,"J/(mol*K)")), ) self.reaction2 = Reaction( reactants=[acetyl, oxygen], products=[acetylperoxy], kinetics = Arrhenius( A = (2.65e12, 'cm^3/(mol*s)'), n = 0.0, Ea = (0.0, 'kJ/mol'), T0 = (1, 'K'), Tmin = (300, 'K'), Tmax = (2000, 'K'), ), )