def test_make_new_species(self): """ Test that CoreEdgeReactionModel.make_new_species method correctly stores the unique species. """ # adding 3 unique species: cerm = CoreEdgeReactionModel() spcs = [ Species().from_smiles('[OH]'), Species().from_smiles('CC'), Species().from_smiles('[CH3]') ] for spc in spcs: cerm.make_new_species(spc) self.assertEquals(len(cerm.species_dict), len(spcs)) self.assertEquals(len(cerm.index_species_dict), len(spcs)) # adding 3 unique, and 1 already existing species: cerm = CoreEdgeReactionModel() spcs = [ Species().from_smiles('[OH]'), Species().from_smiles('CC'), Species().from_smiles('[CH3]'), Species().from_smiles('CC') ] # duplicate species for spc in spcs: cerm.make_new_species(spc) self.assertEquals(len(cerm.species_dict), len(spcs) - 1) self.assertEquals(len(cerm.index_species_dict), len(spcs) - 1)
def test_append_unreactive_structure(self): """ Test that CERM.make_new_species correctly recognizes a non-representative resonance structure """ cerm = CoreEdgeReactionModel() spcs = [ Species().from_smiles('CCO'), # a control species Species().from_smiles('[N]=O'), Species().from_adjacency_list( """1 O u1 p2 c0 {2,S} 2 N u0 p2 c0 {1,S}""" ), # a non-representative structure of '[N]=O' ] for spc in spcs: cerm.make_new_species(spc) self.assertEquals(len(cerm.species_dict), 2) self.assertEquals(len(cerm.index_species_dict), 2) self.assertEquals(len(cerm.index_species_dict[1].molecule), 1) self.assertTrue(cerm.index_species_dict[1].molecule[0].reactive) self.assertEquals(len(cerm.index_species_dict[2].molecule), 1) self.assertTrue(cerm.index_species_dict[2].molecule[0].reactive)
def test_thermo_filter_down(self): """ test that thermo_filter_down with maximum_edge_species = 1 reduces the edge to one species """ cerm = CoreEdgeReactionModel() spcs = [ Species().from_smiles('[OH]'), Species().from_smiles('C'), Species().from_smiles('[CH3]'), Species().from_smiles('[CH2]'), Species().from_smiles('O') ] for spc in spcs: cerm.make_new_species(spc, label=spc.molecule[0].to_smiles()) spc.label = spc.molecule[0].to_smiles() thermo_dict = { '[OH]': NASA(polynomials=[ NASAPolynomial(coeffs=[ 3.51457, 2.92787e-05, -5.32168e-07, 1.0195e-09, -3.85947e-13, 3414.25, 2.10435 ], Tmin=(100, 'K'), Tmax=(1145.75, 'K')), NASAPolynomial(coeffs=[ 3.07194, 0.000604014, -1.39775e-08, -2.13448e-11, 2.48067e-15, 3579.39, 4.578 ], Tmin=(1145.75, 'K'), Tmax=(5000, 'K')) ], Tmin=(100, 'K'), Tmax=(5000, 'K'), E0=(28.3945, 'kJ/mol'), Cp0=(29.1007, 'J/(mol*K)'), CpInf=(37.4151, 'J/(mol*K)'), label="""OH(D)""", comment="""Thermo library: primaryThermoLibrary"""), 'C': NASA(polynomials=[ NASAPolynomial(coeffs=[ 4.20541, -0.00535551, 2.51121e-05, -2.1376e-08, 5.97513e-12, -10161.9, -0.921259 ], Tmin=(100, 'K'), Tmax=(1084.13, 'K')), NASAPolynomial(coeffs=[ 0.908298, 0.011454, -4.57171e-06, 8.29185e-10, -5.66309e-14, -9719.99, 13.9929 ], Tmin=(1084.13, 'K'), Tmax=(5000, 'K')) ], Tmin=(100, 'K'), Tmax=(5000, 'K'), E0=(-84.435, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(108.088, 'J/(mol*K)'), label="""CH4""", comment="""Thermo library: primaryThermoLibrary"""), '[CH3]': NASA(polynomials=[ NASAPolynomial(coeffs=[ 3.67359, 0.00201095, 5.73022e-06, -6.87117e-09, 2.54386e-12, 16445, 1.60456 ], Tmin=(200, 'K'), Tmax=(1000, 'K')), NASAPolynomial(coeffs=[ 2.28572, 0.0072399, -2.98714e-06, 5.95685e-10, -4.67154e-14, 16775.6, 8.48007 ], Tmin=(1000, 'K'), Tmax=(3500, 'K')) ], Tmin=(200, 'K'), Tmax=(3500, 'K'), E0=(136.42, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(83.1447, 'J/(mol*K)'), label="""CH3""", comment="""Thermo library: GRI-Mech3.0"""), '[CH2]': NASA(polynomials=[ NASAPolynomial(coeffs=[ 4.01192, -0.000154978, 3.26298e-06, -2.40422e-09, 5.69497e-13, 45867.7, 0.533201 ], Tmin=(100, 'K'), Tmax=(1104.62, 'K')), NASAPolynomial(coeffs=[ 3.14983, 0.00296674, -9.76056e-07, 1.54115e-10, -9.50338e-15, 46058.1, 4.77808 ], Tmin=(1104.62, 'K'), Tmax=(5000, 'K')) ], Tmin=(100, 'K'), Tmax=(5000, 'K'), E0=(381.37, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(58.2013, 'J/(mol*K)'), label="""CH2(T)""", comment="""Thermo library: primaryThermoLibrary"""), 'O': NASA(polynomials=[ NASAPolynomial(coeffs=[ 4.05764, -0.000787936, 2.90877e-06, -1.47519e-09, 2.12842e-13, -30281.6, -0.311364 ], Tmin=(100, 'K'), Tmax=(1130.24, 'K')), NASAPolynomial(coeffs=[ 2.84325, 0.00275109, -7.81031e-07, 1.07244e-10, -5.79392e-15, -29958.6, 5.91042 ], Tmin=(1130.24, 'K'), Tmax=(5000, 'K')) ], Tmin=(100, 'K'), Tmax=(5000, 'K'), E0=(-251.755, 'kJ/mol'), Cp0=(33.2579, 'J/(mol*K)'), CpInf=(58.2013, 'J/(mol*K)'), label="""H2O""", comment="""Thermo library: primaryThermoLibrary"""), } for spc in spcs[:3]: cerm.add_species_to_core(spc) reaction = TemplateReaction(reactants=[spcs[0], spcs[2]], products=[spcs[-1], spcs[-2]], degeneracy=1, reversible=True, family='H_Abstraction') cerm.process_new_reactions(new_reactions=[reaction], new_species=[]) # add CH2 and O to edge for spc in cerm.core.species + cerm.edge.species: spc.thermo = thermo_dict[ spc.molecule[0].to_smiles()] # assign thermo cerm.set_thermodynamic_filtering_parameters( Tmax=300.0, thermo_tol_keep_spc_in_edge=1000.0, min_core_size_for_prune=0, maximum_edge_species=1, reaction_systems=[]) difset = set([ x.molecule[0].to_smiles() for x in cerm.edge.species ]) - set([x.molecule[0].to_smiles() for x in cerm.core.species]) self.assertEquals( len(difset), 2) # no change because toleranceThermoKeepSpeciesInEdge is high cerm.thermo_filter_down(maximum_edge_species=1) difset = set([ x.molecule[0].to_smiles() for x in cerm.edge.species ]) - set([x.molecule[0].to_smiles() for x in cerm.core.species]) self.assertEquals( len(difset), 1 ) # should be one because we thermo filtered down to one edge species
def execute(self, output_file, plot, file_format='pdf', print_summary=True, species_list=None, thermo_library=None, kinetics_library=None): """Execute an ExplorerJob""" logging.info('Exploring network...') rmg = RMG() rmg.species_constraints = { 'allowed': ['input species', 'seed mechanisms', 'reaction libraries'], 'maximumRadicalElectrons': self.maximum_radical_electrons, 'explicitlyAllowedMolecules': [] } rmgpy.rmg.input.rmg = rmg reaction_model = CoreEdgeReactionModel() reaction_model.pressure_dependence = self.pdepjob reaction_model.pressure_dependence.rmgmode = True if output_file: reaction_model.pressure_dependence.output_file = os.path.dirname( output_file) kinetics_database = get_db('kinetics') thermo_database = get_db('thermo') thermo_database.libraries['thermojobs'] = thermo_library thermo_database.library_order.insert(0, 'thermojobs') kinetics_database.libraries['kineticsjobs'] = kinetics_library kinetics_database.library_order.insert( 0, ('kineticsjobs', 'Reaction Library')) self.job_rxns = [rxn for rxn in reaction_model.core.reactions] if output_file is not None: if not os.path.exists( os.path.join( reaction_model.pressure_dependence.output_file, 'pdep')): os.mkdir( os.path.join( reaction_model.pressure_dependence.output_file, 'pdep')) else: shutil.rmtree( os.path.join( reaction_model.pressure_dependence.output_file, 'pdep')) os.mkdir( os.path.join( reaction_model.pressure_dependence.output_file, 'pdep')) # get the molecular formula for the network mmol = None for spc in self.source: if mmol: mmol = mmol.merge(spc.molecule[0]) else: mmol = spc.molecule[0].copy(deep=True) form = mmol.get_formula() for spec in list(self.bath_gas.keys()) + self.source: nspec, is_new = reaction_model.make_new_species(spec, reactive=False) flags = np.array([ s.molecule[0].get_formula() == form for s in reaction_model.core.species ]) reaction_model.enlarge(nspec, react_edge=False, unimolecular_react=flags, bimolecular_react=np.zeros( (len(reaction_model.core.species), len(reaction_model.core.species)))) reaction_model.add_seed_mechanism_to_core('kineticsjobs') for lib in kinetics_database.library_order: if lib[0] != 'kineticsjobs': reaction_model.add_reaction_library_to_edge(lib[0]) for spc in reaction_model.core.species: for i, item in enumerate(self.source): if spc.is_isomorphic(item): self.source[i] = spc # react initial species if len(self.source) == 1: flags = np.array([ s.molecule[0].get_formula() == form for s in reaction_model.core.species ]) biflags = np.zeros((len(reaction_model.core.species), len(reaction_model.core.species))) elif len(self.source) == 2: flags = np.array([False for s in reaction_model.core.species]) biflags = np.array( [[False for i in range(len(reaction_model.core.species))] for j in range(len(reaction_model.core.species))]) biflags[reaction_model.core.species.index(self.source[0]), reaction_model.core.species.index(self.source[1])] = True else: raise ValueError( "Reactant channels with greater than 2 reactants not supported" ) reaction_model.enlarge(react_edge=True, unimolecular_react=flags, bimolecular_react=biflags) # find the networks we're interested in networks = [] for nwk in reaction_model.network_list: if set(nwk.source) == set(self.source): self.source = nwk.source networks.append(nwk) if len(networks) == 0: raise ValueError( 'Did not generate a network with the requested source. This usually means no unimolecular ' 'reactions were generated for the source. Note that library reactions that are not ' 'properly flagged as elementary_high_p can replace RMG generated reactions that would ' 'otherwise be part of networks.') for network in networks: network.bath_gas = self.bath_gas self.networks = networks # determine T and P combinations if self.pdepjob.Tlist: t_list = self.pdepjob.Tlist.value_si else: t_list = np.linspace(self.pdepjob.Tmin.value_si, self.pdepjob.Tmax.value_si, self.pdepjob.Tcount) if self.pdepjob.Plist: p_list = self.pdepjob.Plist.value_si else: p_list = np.linspace(self.pdepjob.Pmin.value_si, self.pdepjob.Pmax.value_si, self.pdepjob.Pcount) # generate the network forbidden_structures = get_db('forbidden') incomplete = True checked_species = [] while incomplete: incomplete = False for temperature in t_list: for pressure in p_list: for network in self.networks: # compute the characteristic rate coefficient by summing all rate coefficients # from the reactant channel for spc in reaction_model.edge.species: if spc in checked_species: continue if forbidden_structures.is_molecule_forbidden( spc.molecule[0]): reaction_model.remove_species_from_edge( reaction_model.reaction_systems, spc) reaction_model.remove_empty_pdep_networks() else: checked_species.append(spc) kchar = 0.0 for rxn in network.net_reactions: # reaction_model.core.reactions+reaction_model.edge.reactions if (set(rxn.reactants) == set(self.source) and rxn.products[0].molecule[0].get_formula() == form): kchar += rxn.kinetics.get_rate_coefficient( T=temperature, P=pressure) elif (set(rxn.products) == set(self.source) and rxn.reactants[0].molecule[0].get_formula() == form): kchar += rxn.generate_reverse_rate_coefficient( network_kinetics=True ).get_rate_coefficient(T=temperature, P=pressure) if network.get_leak_coefficient( T=temperature, P=pressure) > self.explore_tol * kchar: incomplete = True spc = network.get_maximum_leak_species( T=temperature, P=pressure) logging.info( 'adding new isomer {0} to network'.format(spc)) flags = np.array([ s.molecule[0].get_formula() == form for s in reaction_model.core.species ]) reaction_model.enlarge( (network, spc), react_edge=False, unimolecular_react=flags, bimolecular_react=np.zeros( (len(reaction_model.core.species), len(reaction_model.core.species)))) flags = np.array([ s.molecule[0].get_formula() == form for s in reaction_model.core.species ]) reaction_model.enlarge( react_edge=True, unimolecular_react=flags, bimolecular_react=np.zeros( (len(reaction_model.core.species), len(reaction_model.core.species)))) for network in self.networks: rm_rxns = [] for rxn in network.path_reactions: # remove reactions with forbidden species for r in rxn.reactants + rxn.products: if forbidden_structures.is_molecule_forbidden( r.molecule[0]): rm_rxns.append(rxn) for rxn in rm_rxns: logging.info('Removing forbidden reaction: {0}'.format(rxn)) network.path_reactions.remove(rxn) # clean up output files if output_file is not None: path = os.path.join( reaction_model.pressure_dependence.output_file, 'pdep') for name in os.listdir(path): if name.endswith('.py') and '_' in name: if name.split('_')[-1].split('.')[0] != str( len(network.isomers)): os.remove(os.path.join(path, name)) else: os.rename( os.path.join(path, name), os.path.join( path, 'network_full{}.py'.format( self.networks.index(network)))) warns = [] for rxn in self.job_rxns: if rxn not in network.path_reactions: warns.append( 'Reaction {0} in the input file was not explored during network expansion and was ' 'not included in the full network. This is likely because your explore_tol value is ' 'too high.'.format(rxn)) # reduction process for network in self.networks: if self.energy_tol != np.inf or self.flux_tol != 0.0: rxn_set = None product_set = None for temperature in t_list: if self.energy_tol != np.inf: rxns = network.get_energy_filtered_reactions( temperature, self.energy_tol) if rxn_set is not None: rxn_set &= set(rxns) else: rxn_set = set(rxns) for pressure in p_list: if self.flux_tol != 0.0: products = network.get_rate_filtered_products( temperature, pressure, self.flux_tol) products = [tuple(x) for x in products] if product_set is not None: product_set &= set(products) else: product_set = set(products) if rxn_set: logging.info('removing reactions during reduction:') for rxn in rxn_set: logging.info(rxn) rxn_set = list(rxn_set) if product_set: logging.info('removing products during reduction:') for prod in product_set: logging.info([x.label for x in prod]) product_set = list(product_set) network.remove_reactions(reaction_model, rxns=rxn_set, prods=product_set) for rxn in self.job_rxns: if rxn not in network.path_reactions: warns.append( 'Reaction {0} in the input file was not included in the reduced model.' .format(rxn)) self.networks = networks for p, network in enumerate(self.networks): self.pdepjob.network = network if len(self.networks) > 1: root, file_name = os.path.split(output_file) s1, s2 = file_name.split(".") ind = str(self.networks.index(network)) stot = os.path.join(root, s1 + "{}.".format(ind) + s2) else: stot = output_file self.pdepjob.execute(stot, plot, file_format='pdf', print_summary=True) if os.path.isfile('network.pdf'): os.rename('network.pdf', 'network' + str(p) + '.pdf') if warns: logging.info('\nOUTPUT WARNINGS:\n') for w in warns: logging.warning(w)