def find_species(self, identifier): """Find species based on provided identifier""" if identifier in self.species: # This is a Species object return identifier elif identifier in self.species_dict: # This is a species label return self.species_dict[identifier] else: try: spc = Species(molecule=[Molecule().fromSMILES(identifier)]) except ValueError: try: spc = Species(molecule=[Molecule().fromInChI(identifier)]) except ValueError: raise ValueError( 'Unable to interpret provided identifier {0} as ' 'species label, SMILES, or InChI.'.format(identifier)) spc.generate_resonance_structures() for species in self.species_dict.itervalues(): if spc.isIsomorphic(species): return species else: raise ValueError( 'Unable to find any species matching the identifier {0}.'. format(identifier))
def test_singlet_vs_closed_shell(self): adjlist_singlet = """ 1 C u0 p0 c0 {2,D} {3,S} {4,S} 2 C u0 p0 c0 {1,D} {3,S} {5,S} 3 C u0 p1 c0 {1,S} {2,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {2,S} """ adjlist_closed_shell = """ 1 C u0 p0 c0 {2,D} {3,S} {4,S} 2 C u0 p0 c0 {1,D} {3,D} 3 C u0 p0 c0 {1,S} {2,D} {5,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {3,S} """ singlet = Species(molecule=[Molecule().fromAdjacencyList(adjlist_singlet)]) singlet.generate_resonance_structures() closed_shell = Species(molecule=[Molecule().fromAdjacencyList(adjlist_closed_shell)]) closed_shell.generate_resonance_structures() singlet_aug_inchi = singlet.getAugmentedInChI() closed_shell_aug_inchi = closed_shell.getAugmentedInChI() self.assertTrue(singlet_aug_inchi != closed_shell_aug_inchi)
def compare(self, inchi, u_indices=None, p_indices=None): u_layer = U_LAYER_PREFIX + U_LAYER_SEPARATOR.join(map( str, u_indices)) if u_indices else None p_layer = P_LAYER_PREFIX + P_LAYER_SEPARATOR.join(map( str, p_indices)) if p_indices else None aug_inchi = compose_aug_inchi(inchi, u_layer, p_layer) mol = from_augmented_inchi(Molecule(), aug_inchi) ConsistencyChecker.check_multiplicity(mol.get_radical_count(), mol.multiplicity) for at in mol.atoms: ConsistencyChecker.check_partial_charge(at) spc = Species(molecule=[mol]) spc.generate_resonance_structures() ignore_prefix = r"(InChI=1+)(S*)/" aug_inchi_expected = re.split(ignore_prefix, aug_inchi)[-1] aug_inchi_computed = re.split(ignore_prefix, spc.get_augmented_inchi())[-1] self.assertEquals(aug_inchi_expected, aug_inchi_computed) return mol
def loadEntry(self, index, label, solvent, molecule=None, reference=None, referenceType='', shortDesc='', longDesc='', ): spc = molecule if molecule is not None: try: spc = Species().fromSMILES(molecule) except: logging.debug("Solvent '{0}' does not have a valid SMILES '{1}'" .format(label, molecule)) try: spc = Species().fromAdjacencyList(molecule) except: logging.error("Can't understand '{0}' in solute library '{1}'".format(molecule, self.name)) raise spc.generate_resonance_structures() self.entries[label] = Entry( index = index, label = label, item = spc, data = solvent, reference = reference, referenceType = referenceType, shortDesc = shortDesc, longDesc = longDesc.strip(), )
def loadEntry(self, index, label, solvent, molecule=None, reference=None, referenceType='', shortDesc='', longDesc='', ): spc = molecule if molecule is not None: try: spc = Species().fromSMILES(molecule) except: logging.debug("Solvent '{0}' does not have a valid SMILES '{1}'" .format(label, molecule)) try: spc = Species().fromAdjacencyList(molecule) except: logging.error("Can't understand '{0}' in solute library '{1}'".format(molecule, self.name)) raise spc.generate_resonance_structures() self.entries[label] = Entry( index = index, label = label, item = spc, data = solvent, reference = reference, referenceType = referenceType, shortDesc = shortDesc, longDesc = longDesc.strip(), )
def test_singlet_vs_closed_shell(self): adjlist_singlet = """ 1 C u0 p0 c0 {2,D} {3,S} {4,S} 2 C u0 p0 c0 {1,D} {3,S} {5,S} 3 C u0 p1 c0 {1,S} {2,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {2,S} """ adjlist_closed_shell = """ 1 C u0 p0 c0 {2,D} {3,S} {4,S} 2 C u0 p0 c0 {1,D} {3,D} 3 C u0 p0 c0 {1,S} {2,D} {5,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {3,S} """ singlet = Species( molecule=[Molecule().fromAdjacencyList(adjlist_singlet)]) singlet.generate_resonance_structures() closed_shell = Species( molecule=[Molecule().fromAdjacencyList(adjlist_closed_shell)]) closed_shell.generate_resonance_structures() singlet_aug_inchi = singlet.getAugmentedInChI() closed_shell_aug_inchi = closed_shell.getAugmentedInChI() self.assertTrue(singlet_aug_inchi != closed_shell_aug_inchi)
def test_calculate_degeneracy_for_non_reactive_molecule(self): """ tests that the calculateDegeneracy method gets the degeneracy correct for unreactive molecules and that __generateReactions work correctly with the react_non_reactive flag set to `True`. """ from rmgpy.data.rmg import getDB from rmgpy.data.kinetics.family import TemplateReaction adjlist = [ ''' multiplicity 2 1 H u1 p0 c0''', ''' multiplicity 2 1 O u1 p1 c+1 {2,D} 2 N u0 p2 c-1 {1,D}''', ''' 1 O u0 p1 c+1 {2,D} {3,S} 2 N u0 p2 c-1 {1,D} 3 H u0 p0 c0 {1,S}''' ] family = getDB('kinetics').families['R_Recombination'] r1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[0])]) r2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[1]) ]) # r2 is not the representative structure of # NO, but it is the correct structure participating in this reaction p1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[2])]) r2.generate_resonance_structures(keep_isomorphic=True) rxn = TemplateReaction(reactants=[r1, r2], products=[p1]) rxn.degeneracy = family.calculateDegeneracy(rxn) self.assertEqual(rxn.degeneracy, 1)
def test_calculate_degeneracy_for_non_reactive_molecule(self): """ tests that the calculateDegeneracy method gets the degeneracy correct for unreactive molecules and that __generateReactions work correctly with the react_non_reactive flag set to `True`. """ from rmgpy.data.rmg import getDB from rmgpy.data.kinetics.family import TemplateReaction adjlist = [''' multiplicity 2 1 H u1 p0 c0''', ''' multiplicity 2 1 O u1 p1 c+1 {2,D} 2 N u0 p2 c-1 {1,D}''', ''' 1 O u0 p1 c+1 {2,D} {3,S} 2 N u0 p2 c-1 {1,D} 3 H u0 p0 c0 {1,S}'''] family = getDB('kinetics').families['R_Recombination'] r1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[0])]) r2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[1])]) # r2 is not the representative structure of # NO, but it is the correct structure participating in this reaction p1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[2])]) r2.generate_resonance_structures(keep_isomorphic=True) rxn = TemplateReaction(reactants=[r1, r2], products=[p1]) rxn.degeneracy = family.calculateDegeneracy(rxn) self.assertEqual(rxn.degeneracy, 1)
def testResonaceIsomersRepresented(self): "Test that both resonance forms of 1-penten-3-yl are printed by __repr__" spec = Species().fromSMILES('C=C[CH]CC') spec.generate_resonance_structures() exec('spec2 = {0!r}'.format(spec)) self.assertEqual(len(spec.molecule), len(spec2.molecule)) for i, j in zip(spec.molecule, spec2.molecule): self.assertTrue(j.isIsomorphic(i), msg='i is not isomorphic with j, where i is {} and j is {}'.format(i.toSMILES(), j.toSMILES()))
def compare(self, adjlist, aug_inchi): spc = Species(molecule=[Molecule().from_adjacency_list(adjlist)]) spc.generate_resonance_structures() ignore_prefix = r"(InChI=1+)(S*)/" exp = re.split(ignore_prefix, aug_inchi)[-1] comp = re.split(ignore_prefix, spc.get_augmented_inchi())[-1] self.assertEquals(exp, comp)
def compare(self, adjlist, aug_inchi): spc = Species(molecule=[Molecule().fromAdjacencyList(adjlist)]) spc.generate_resonance_structures() ignore_prefix = r"(InChI=1+)(S*)/" exp = re.split(ignore_prefix, aug_inchi)[-1] comp = re.split(ignore_prefix, spc.getAugmentedInChI())[-1] self.assertEquals(exp, comp)
def test_check_for_existing_species_for_bi_aromatics(self): """ Test RMG check_for_existing_species can correctly check isomorphism for biaromatics. In this test, DPP is a species already stored in rmg species_dict, mol_test is a newly created molecule which has one kekulized benzene ring and one double_bond-single_bond benzene ring. """ rmg_test = RMG() rmg_test.reaction_model = CoreEdgeReactionModel() DPP = Species().from_smiles('C1=CC=C(C=C1)CCCC1C=CC=CC=1') DPP.generate_resonance_structures() formula = DPP.molecule[0].get_formula() if formula in rmg_test.reaction_model.species_dict: rmg_test.reaction_model.species_dict[formula].append(DPP) else: rmg_test.reaction_model.species_dict[formula] = [DPP] mol_test = Molecule().from_adjacency_list( """ 1 C u0 p0 c0 {2,S} {3,S} {16,S} {17,S} 2 C u0 p0 c0 {1,S} {4,S} {18,S} {19,S} 3 C u0 p0 c0 {1,S} {5,S} {20,S} {21,S} 4 C u0 p0 c0 {2,S} {6,B} {7,B} 5 C u0 p0 c0 {3,S} {8,D} {9,S} 6 C u0 p0 c0 {4,B} {10,B} {22,S} 7 C u0 p0 c0 {4,B} {12,B} {24,S} 8 C u0 p0 c0 {5,D} {14,S} {27,S} 9 C u0 p0 c0 {5,S} {15,D} {28,S} 10 C u0 p0 c0 {6,B} {11,B} {23,S} 11 C u0 p0 c0 {10,B} {12,B} {25,S} 12 C u0 p0 c0 {7,B} {11,B} {26,S} 13 C u0 p0 c0 {14,D} {15,S} {29,S} 14 C u0 p0 c0 {8,S} {13,D} {30,S} 15 C u0 p0 c0 {9,D} {13,S} {31,S} 16 H u0 p0 c0 {1,S} 17 H u0 p0 c0 {1,S} 18 H u0 p0 c0 {2,S} 19 H u0 p0 c0 {2,S} 20 H u0 p0 c0 {3,S} 21 H u0 p0 c0 {3,S} 22 H u0 p0 c0 {6,S} 23 H u0 p0 c0 {10,S} 24 H u0 p0 c0 {7,S} 25 H u0 p0 c0 {11,S} 26 H u0 p0 c0 {12,S} 27 H u0 p0 c0 {8,S} 28 H u0 p0 c0 {9,S} 29 H u0 p0 c0 {13,S} 30 H u0 p0 c0 {14,S} 31 H u0 p0 c0 {15,S} """) spec = rmg_test.reaction_model.check_for_existing_species(mol_test) self.assertIsNotNone(spec)
def test_resonace_isomers_represented(self): """Test that both resonance forms of 1-penten-3-yl are printed by __repr__""" spec = Species().from_smiles('C=C[CH]CC') spec.generate_resonance_structures() namespace = {} exec('spec2 = {0!r}'.format(spec), globals(), namespace) self.assertIn('spec2', namespace) spec2 = namespace['spec2'] self.assertEqual(len(spec.molecule), len(spec2.molecule)) for i, j in zip(spec.molecule, spec2.molecule): self.assertTrue(j.is_isomorphic(i), msg='i is not isomorphic with j, where i is {} and j is {}'.format(i.to_smiles(), j.to_smiles()))
def predict_thermo(self, smiles): if self.kernel_type == 'GA': spec = Species().fromSMILES(smiles) spec.generate_resonance_structures() thermo = self.kernel.getThermoDataFromGroups(spec) return thermo else: raise Exception('Kernel type {0} not supported yet.'.format( self.kernel_type))
def ensure_species(input_list, resonance=False, keepIsomorphic=False): """ The input list of :class:`Species` or :class:`Molecule` objects is modified in place to only have :class:`Species` objects. Returns None. """ for index, item in enumerate(input_list): if isinstance(item, Molecule): new_item = Species(molecule=[item]) elif isinstance(item, Species): new_item = item else: raise TypeError('Only Molecule or Species objects can be handled.') if resonance: new_item.generate_resonance_structures( keepIsomorphic=keepIsomorphic) input_list[index] = new_item
def check_isomorphism(mol1, mol2, filter_structures=True): """ Converts `mol1` and `mol2` which are RMG:Molecule objects into RMG:Species object and generate resonance structures. Then check Species isomorphism. Return True if one of the molecules in the Species derived from `mol1` is isomorphic to one of the molecules in the Species derived from `mol2`. `filter_structures` is being passes to Species.generate_resonance_structures(). make copies of the molecules, since isIsomorphic() changes atom orders """ mol1.reactive, mol2.reactive = True, True mol1_copy = mol1.copy(deep=True) mol2_copy = mol2.copy(deep=True) spc1 = Species(molecule=[mol1_copy]) spc1.generate_resonance_structures(keep_isomorphic=False, filter_structures=filter_structures) spc2 = Species(molecule=[mol2_copy]) spc2.generate_resonance_structures(keep_isomorphic=False, filter_structures=filter_structures) return spc1.isIsomorphic(spc2)
def test_check_for_existing_reaction_eliminates_identical_reactions_without_duplicate_flag( self): """ Test that check_for_existing_reaction eliminates reactions with different templates and duplicate=false """ cerm = CoreEdgeReactionModel() # make species' objects spcA = Species().from_smiles('[H]') spcB = Species().from_smiles('C=C[CH2]C') spcC = Species().from_smiles('C=C=CC') spcD = Species().from_smiles('[H][H]') spcA.label = '[H]' spcB.label = 'C=C[CH2]C' spcC.label = 'C=C=CC' spcD.label = '[H][H]' spcB.generate_resonance_structures() cerm.add_species_to_core(spcA) cerm.add_species_to_core(spcB) cerm.add_species_to_core(spcC) cerm.add_species_to_core(spcD) reaction_in_model = TemplateReaction(reactants=[spcA, spcB], products=[spcC, spcD], family='H_Abstraction', template=['Csd', 'H'], duplicate=False) reaction_in_model.reactants.sort() reaction_in_model.products.sort() reaction_to_add = TemplateReaction(reactants=[spcA, spcB], products=[spcC, spcD], family='H_Abstraction', template=['Cs12345', 'H'], duplicate=False) cerm.add_reaction_to_core(reaction_in_model) cerm.register_reaction(reaction_in_model) found, rxn = cerm.check_for_existing_reaction(reaction_to_add) self.assertTrue( found, 'check_for_existing_reaction failed to eliminate reactions without duplicate tag' )
def ensure_species(input_list, resonance=False, keepIsomorphic=False): """ Given an input list of molecules or species, return a list with only species objects. """ output_list = [] for item in input_list: if isinstance(item, Molecule): new_item = Species(molecule=[item]) elif isinstance(item, Species): new_item = item else: raise TypeError('Only Molecule or Species objects can be handled.') if resonance: new_item.generate_resonance_structures(keepIsomorphic=keepIsomorphic) output_list.append(new_item) return output_list
def test_deterministic_reaction_template_matching(self): """ Test RMG work flow can match reaction template for kinetics estimation deterministically. In this test, a change of molecules order in a reacting species should not change the reaction template matched. However, this is inherently impossible with the existing reaction generation algorithm. Currently, the first reaction will be the one that is kept if the reactions are identical. If different templates are a result of different transition states, all are kept. {O=C-[C]=C, [O]-C=C=C} -> H + C=C=C=O """ # react spc = Species().from_smiles("O=C[C]=C") spc.generate_resonance_structures() new_reactions = react_species((spc, )) # try to pick out the target reaction mol_H = Molecule().from_smiles("[H]") mol_C3H2O = Molecule().from_smiles("C=C=C=O") target_rxns = find_target_rxns_containing(mol_H, mol_C3H2O, new_reactions) self.assertEqual(len(target_rxns), 2) # reverse the order of molecules in spc spc.molecule = list(reversed(spc.molecule)) # react again new_reactions_reverse = [] new_reactions_reverse.extend(react_species((spc, ))) # try to pick out the target reaction target_rxns_reverse = find_target_rxns_containing( mol_H, mol_C3H2O, new_reactions_reverse) self.assertEqual(len(target_rxns_reverse), 2) # whatever order of molecules in spc, the reaction template matched should be same self.assertEqual(target_rxns[0].template, target_rxns_reverse[0].template)
def ensure_species(input_list, resonance=False, keep_isomorphic=False): """ The input list of :class:`Species` or :class:`Molecule` objects is modified in place to only have :class:`Species` objects. Returns None. """ for index, item in enumerate(input_list): if isinstance(item, Molecule): new_item = Species(molecule=[item]) elif isinstance(item, Species): new_item = item else: raise TypeError('Only Molecule or Species objects can be handled.') if resonance: if not any([mol.reactive for mol in new_item.molecule]): # if generating a reaction containing a Molecule with a reactive=False flag (e.g., for degeneracy # calculations), that was now converted into a Species, first mark as reactive=True new_item.molecule[0].reactive = True new_item.generate_resonance_structures(keep_isomorphic=keep_isomorphic) input_list[index] = new_item
def ensure_species(input_list, resonance=False, keep_isomorphic=False): """ The input list of :class:`Species` or :class:`Molecule` objects is modified in place to only have :class:`Species` objects. Returns None. """ for index, item in enumerate(input_list): if isinstance(item, Molecule): new_item = Species(molecule=[item]) elif isinstance(item, Species): new_item = item else: raise TypeError('Only Molecule or Species objects can be handled.') if resonance: if not any([mol.reactive for mol in new_item.molecule]): # if generating a reaction containing a Molecule with a reactive=False flag (e.g., for degeneracy # calculations), that was now converted into a Species, first mark as reactive=True new_item.molecule[0].reactive = True new_item.generate_resonance_structures(keep_isomorphic=keep_isomorphic) input_list[index] = new_item
def test_CCCO_triplet(self): adjlist = """ multiplicity 3 1 C u0 p0 c0 {2,D} {5,S} {6,S} 2 C u0 p0 c0 {1,D} {3,S} {7,S} 3 C u1 p0 c0 {2,S} {4,S} {8,S} 4 O u1 p2 c0 {3,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {3,S} """ mol = Molecule().fromAdjacencyList(adjlist) spc = Species(molecule=[mol]) spc.generate_resonance_structures() aug_inchi = spc.getAugmentedInChI() self.assertEqual(Species(molecule=[Molecule().fromAugmentedInChI(aug_inchi)]).isIsomorphic(spc), True)
def test_ccco_triplet(self): adjlist = """ multiplicity 3 1 C u0 p0 c0 {2,D} {5,S} {6,S} 2 C u0 p0 c0 {1,D} {3,S} {7,S} 3 C u1 p0 c0 {2,S} {4,S} {8,S} 4 O u1 p2 c0 {3,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {3,S} """ mol = Molecule().from_adjacency_list(adjlist) spc = Species(molecule=[mol]) spc.generate_resonance_structures() aug_inchi = spc.get_augmented_inchi() self.assertEqual(Species(molecule=[Molecule().from_augmented_inchi(aug_inchi)]).is_isomorphic(spc), True)
def test_check_for_existing_reaction_eliminates_identical_reactions(self): """ Test that check_for_existing_reaction catches identical reactions. """ cerm = CoreEdgeReactionModel() # make species' objects spcA = Species().from_smiles('[H]') spcB = Species().from_smiles('C=C[CH2]C') spcC = Species().from_smiles('C=C=CC') spcD = Species().from_smiles('[H][H]') spcA.label = '[H]' spcB.label = 'C=C[CH2]C' spcC.label = 'C=C=CC' spcD.label = '[H][H]' spcB.generate_resonance_structures() cerm.add_species_to_core(spcA) cerm.add_species_to_core(spcB) cerm.add_species_to_core(spcC) cerm.add_species_to_core(spcD) reaction_in_model = TemplateReaction(reactants=[spcA, spcB], products=[spcC, spcD], family='H_Abstraction', template=['Csd', 'H']) reaction_in_model.reactants.sort() reaction_in_model.products.sort() reaction_to_add = TemplateReaction(reactants=[spcA, spcB], products=[spcC, spcD], family='H_Abstraction', template=['Csd', 'H']) cerm.add_reaction_to_core(reaction_in_model) cerm.register_reaction(reaction_in_model) found, rxn = cerm.check_for_existing_reaction(reaction_to_add) self.assertTrue( found, 'check_for_existing_reaction failed to identify existing reaction')
def test_add_atom_labels_for_reaction_2(self): """Test that addAtomLabelsForReaction can identify reactions with identical references The molecule [CH]=C=C has resonance in this reaction""" from rmgpy.data.rmg import getDB s1 = Species().fromSMILES('C=C=C') s2 = Species().fromSMILES('C=C=[CH]') s3 = Species().fromSMILES('C#CC') s2.generate_resonance_structures() reactants = [s1, s2] products = [s2, s3] reaction = TemplateReaction(reactants=reactants, products=products, family='H_Abstraction') family = getDB('kinetics').families['H_Abstraction'] print reaction.reactants print reaction.products family.addAtomLabelsForReaction(reaction, output_with_resonance=False) # test that the reaction has labels found_labels = [] for species in reaction.reactants: for atom in species.molecule[0].atoms: if atom.label != '': found_labels.append(atom.label) self.assertEqual( len(found_labels), 3, 'wrong number of labels found {0}'.format(found_labels)) self.assertIn('*1', found_labels) self.assertIn('*2', found_labels) self.assertIn('*3', found_labels) # test for the products too found_labels = [] for species in reaction.products: for atom in species.molecule[0].atoms: if atom.label != '': found_labels.append(atom.label) self.assertEqual(len(found_labels), 3) self.assertIn('*1', found_labels) self.assertIn('*2', found_labels) self.assertIn('*3', found_labels)
def compare(self, inchi, u_indices=None, p_indices = None): u_layer = U_LAYER_PREFIX + U_LAYER_SEPARATOR.join(map(str, u_indices)) if u_indices else None p_layer = P_LAYER_PREFIX + P_LAYER_SEPARATOR.join(map(str, p_indices)) if p_indices else None aug_inchi = compose_aug_inchi(inchi, u_layer, p_layer) mol = fromAugmentedInChI(Molecule(), aug_inchi) ConsistencyChecker.check_multiplicity(mol.getRadicalCount(), mol.multiplicity) for at in mol.atoms: ConsistencyChecker.check_partial_charge(at) spc = Species(molecule=[mol]) spc.generate_resonance_structures() ignore_prefix = r"(InChI=1+)(S*)/" aug_inchi_expected = re.split(ignore_prefix, aug_inchi)[-1] aug_inchi_computed = re.split(ignore_prefix, spc.getAugmentedInChI())[-1] self.assertEquals(aug_inchi_expected, aug_inchi_computed) return mol
def combine_spc_list(spc_list1, spc_list2, same_source=True, resonance=True): """ Combine two species list used in ARC input files Args: spc_list1 (list): One of the list containing species info for ARC input files spc_list2 (list): The other list same_source (bool): If two lists are generated using the same set of chemkin file and species dictionary resonance (bool): Generate resonance structures when checking isomorphism Returns: spc_list (list): A list contains all of the species in spc_list1 and spc_list2 """ # If same source, then just compare the label if same_source: spc_list = spc_list1 + spc_list2 spc_list = list(set(spc_list)) # If not same source, compare the structure else: spc_list = list() for spc in spc_list1: if 'smiles' in spc.keys(): spc_list.append(Species().from_smiles(spc['smiles'])) elif 'adjlist' in spc.keys(): spc_list.append(Species().from_adjacency_list(spc['adjlist'])) for spc in spc_list2: if 'smiles' in spc.keys(): species = Species().from_smiles(spc['smiles']) elif 'adjlist' in spc.keys(): species = Species().from_adjacency_list(spc['adjlist']) if resonance: species.generate_resonance_structures() for species1 in spc_list: if species1.is_isomorphic(species): break else: spc_list.append(spc) return spc_list
def test_add_atom_labels_for_reaction_2(self): """Test that addAtomLabelsForReaction can identify reactions with identical references The molecule [CH]=C=C has resonance in this reaction""" from rmgpy.data.rmg import getDB s1 = Species().fromSMILES('C=C=C') s2 = Species().fromSMILES('C=C=[CH]') s3 = Species().fromSMILES('C#CC') s2.generate_resonance_structures() reactants = [s1,s2] products = [s2,s3] reaction = TemplateReaction(reactants =reactants, products = products, family = 'H_Abstraction') family = getDB('kinetics').families['H_Abstraction'] print reaction.reactants print reaction.products family.addAtomLabelsForReaction(reaction, output_with_resonance=False) # test that the reaction has labels found_labels = [] for species in reaction.reactants: for atom in species.molecule[0].atoms: if atom.label != '': found_labels.append(atom.label) self.assertEqual(len(found_labels), 3,'wrong number of labels found {0}'.format(found_labels)) self.assertIn('*1',found_labels) self.assertIn('*2',found_labels) self.assertIn('*3',found_labels) # test for the products too found_labels = [] for species in reaction.products: for atom in species.molecule[0].atoms: if atom.label != '': found_labels.append(atom.label) self.assertEqual(len(found_labels), 3) self.assertIn('*1',found_labels) self.assertIn('*2',found_labels) self.assertIn('*3',found_labels)
def generate_isotopomers(spc, N=1): """ Generate all isotopomers of the parameter species by adding max. N carbon isotopes to the atoms of the species. """ mol = spc.molecule[0] isotope = get_element(6, 13) mols = [] add_isotope(0, N, mol, mols, isotope) spcs = [] for isomol in mols: isotopomer = Species(molecule=[isomol], thermo=deepcopy(spc.thermo), transport_data=spc.transport_data, reactive=spc.reactive) isotopomer.generate_resonance_structures(keep_isomorphic=True) spcs.append(isotopomer) # do not retain identical species: filtered = [] while spcs: candidate = spcs.pop() unique = True for isotopomer in filtered: if isotopomer.is_isomorphic(candidate): unique = False break if unique: filtered.append(candidate) if spc.thermo: for isotopomer in filtered: correct_entropy(isotopomer, spc) return filtered
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.generate_resonance_structures() 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.generate_resonance_structures() product.thermo = generateThermoData(product, database) reaction.products.append(product) reaction.kinetics = entry.data reaction.degeneracy = entry.item.degeneracy return reaction
def test_is_isomorphic_strict(self): """Test that the strict argument to Species.is_isomorphic works""" spc1 = Species(smiles='[CH2]C1=CC=CC2=C1C=CC1=C2C=CC=C1') spc2 = Species(smiles='C=C1C=CC=C2C1=C[CH]C1=C2C=CC=C1') spc3 = Species(smiles='[CH2]C1=CC2=C(C=C1)C1=C(C=CC=C1)C=C2') self.assertFalse(spc1.is_isomorphic(spc2, strict=True)) self.assertTrue(spc1.is_isomorphic(spc2, strict=False)) self.assertFalse(spc1.is_isomorphic(spc3, strict=True)) self.assertFalse(spc1.is_isomorphic(spc3, strict=False)) spc1.generate_resonance_structures() spc2.generate_resonance_structures() spc3.generate_resonance_structures() self.assertTrue(spc1.is_isomorphic(spc2, strict=True)) self.assertTrue(spc1.is_isomorphic(spc2, strict=False)) self.assertFalse(spc1.is_isomorphic(spc3, strict=True)) self.assertFalse(spc1.is_isomorphic(spc3, strict=False))
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. """ 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 = [], specificCollider = entry.item.specificCollider, 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.generate_resonance_structures() reactant.thermo = thermoDatabase.getThermoData(reactant) reaction.reactants.append(reactant) for molecule in entry.item.products: product = Species(molecule=[molecule]) product.generate_resonance_structures() product.thermo = thermoDatabase.getThermoData(product) reaction.products.append(product) # Generate all possible reactions involving the reactant species generatedReactions = self.generate_reactions_from_families([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 (isomorphic_species_lists(reaction.reactants, rxn.reactants) and isomorphic_species_lists(reaction.products, rxn.products)): forward.append(rxn) if (isomorphic_species_lists(reaction.reactants, rxn.products) and isomorphic_species_lists(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]) try: kunits = ('s^-1', 'm^3/(mol*s)', 'm^6/(mol^2*s)')[len(reverse[0].reactants)-1] except IndexError: raise NotImplementedError('Cannot reverse reactions with {} products'.format( len(reverse[0].reactants))) 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 __init__(self, smiles=[], rmg_species=None): """ A class that holds information for Species object """ assert isinstance(smiles, list) self._conformers = None if ((len(smiles) != 0) and rmg_species): # Provide both a list of smiles and an rmg_species assert isinstance( rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") else: try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") smiles_list = [] for rmg_mol in rmg_species.molecule: smiles_list.append(rmg_mol.toSMILES()) for s in smiles_list: if s in smiles: continue if not(s in smiles): smiles.append(s) assert len(smiles) == len( smiles_list), "The list of smiles presented does not match the possible species provided" self.smiles = smiles self.rmg_species = rmg_species elif (rmg_species and (len(smiles) == 0)): # RMG species provided, but not smiles assert isinstance( rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") else: try: rmg_species.generate_resonance_structures() except: logging.info( "Could not generate resonance structures for this species... Using molecule provided") smiles = [] for rmg_mol in rmg_species.molecule: smiles.append(rmg_mol.toSMILES()) self.smiles = smiles self.rmg_species = rmg_species elif ((not rmg_species) and (len(smiles) != 0)): # smiles provided but not species species_list = [] for smile in smiles: molecule = RMGMolecule(SMILES=smile) species_list.append(molecule.generate_resonance_structures()) if len(smiles) != 1: got_one = False for s1 in species_list: for s2 in species_list: for m1 in s1: for m2 in s2: if m1.isIsomorphic(m2): got_one = True assert got_one, "SMILESs provided describe different species" smiles_list = [] for mol in species_list[0]: smiles_list.append(mol.toSMILES()) self.smiles = smiles_list self.rmg_species = species_list[0] else: self.smiles = [] self.rmg_species = rmg_species
def test_restart_thermo(self): """ Test restarting ARC through the ARC class in main.py via the input_dict argument of the API Rather than through ARC.py. Check that all files are in place and the log file content. """ restart_path = os.path.join(ARC_PATH, 'arc', 'testing', 'restart', '1_restart_thermo', 'restart.yml') input_dict = read_yaml_file(path=restart_path) project = 'arc_project_for_testing_delete_after_usage_restart_thermo' project_directory = os.path.join(ARC_PATH, 'Projects', project) input_dict['project'], input_dict[ 'project_directory'] = project, project_directory arc1 = ARC(**input_dict) arc1.execute() self.assertEqual(arc1.freq_scale_factor, 0.988) self.assertTrue( os.path.isfile( os.path.join(project_directory, 'output', 'thermo.info'))) with open(os.path.join(project_directory, 'output', 'thermo.info'), 'r') as f: thermo_dft_ccsdtf12_bac = False for line in f.readlines(): if 'thermo_DFT_CCSDTF12_BAC' in line: thermo_dft_ccsdtf12_bac = True break self.assertTrue(thermo_dft_ccsdtf12_bac) with open( os.path.join( project_directory, 'arc_project_for_testing_delete_after_usage_restart_thermo.info' ), 'r') as f: sts, n2h3, oet, lot, ap = False, False, False, False, False for line in f.readlines(): if 'Considered the following species and TSs:' in line: sts = True elif 'Species N2H3' in line: n2h3 = True elif 'Overall time since project initiation:' in line: oet = True elif 'Levels of theory used:' in line: lot = True elif 'ARC project arc_project_for_testing_delete_after_usage_restart_thermo' in line: ap = True self.assertTrue(sts) self.assertTrue(n2h3) self.assertTrue(oet) self.assertTrue(lot) self.assertTrue(ap) with open(os.path.join(project_directory, 'arc.log'), 'r') as f: aei, ver, git, spc, rtm, ldb, therm, src, ter =\ False, False, False, False, False, False, False, False, False for line in f.readlines(): if 'ARC execution initiated on' in line: aei = True elif '# Version:' in line: ver = True elif 'The current git HEAD for ARC is:' in line: git = True elif 'Considering species: CH3CO2_rad' in line: spc = True elif 'All jobs for species N2H3 successfully converged. Run time' in line: rtm = True elif 'Loading the RMG database...' in line: ldb = True elif 'Thermodynamics for H2O2' in line: therm = True elif 'Sources of thermoproperties determined by RMG for the parity plots:' in line: src = True elif 'ARC execution terminated on' in line: ter = True self.assertTrue(aei) self.assertTrue(ver) self.assertTrue(git) self.assertTrue(spc) self.assertTrue(rtm) self.assertTrue(ldb) self.assertTrue(therm) self.assertTrue(src) self.assertTrue(ter) self.assertTrue( os.path.isfile( os.path.join(project_directory, 'output', 'thermo_parity_plots.pdf'))) status = read_yaml_file( os.path.join(project_directory, 'output', 'status.yml')) self.assertEqual( status['CH3CO2_rad']['isomorphism'], 'opt passed isomorphism check; ' 'Conformers optimized and compared at b3lyp/6-31g(d,p) empiricaldispersion=gd3bj; ' ) self.assertTrue(status['CH3CO2_rad']['job_types']['sp']) with open( os.path.join(project_directory, 'output', 'Species', 'H2O2', 'arkane', 'species_dictionary.txt'), 'r') as f: lines = f.readlines() adj_list = '' for line in lines: if 'H2O2' not in line: adj_list += line if line == '\n': break mol1 = Molecule().from_adjacency_list(adj_list) self.assertEqual(mol1.to_smiles(), 'OO') thermo_library_path = os.path.join( project_directory, 'output', 'RMG libraries', 'thermo', 'arc_project_for_testing_delete_after_usage_restart_thermo.py') new_thermo_library_path = os.path.join( rmg_settings['database.directory'], 'thermo', 'libraries', 'arc_project_for_testing_delete_after_usage_restart_thermo.py') # copy the generated library to RMG-database shutil.copyfile(thermo_library_path, new_thermo_library_path) db = RMGDatabase() db.load( path=rmg_settings['database.directory'], thermo_libraries=[ 'arc_project_for_testing_delete_after_usage_restart_thermo' ], transport_libraries=[], reaction_libraries=[], seed_mechanisms=[], kinetics_families='none', kinetics_depositories=[], statmech_libraries=None, depository=False, solvation=False, testing=True, ) spc2 = Species(smiles='CC([O])=O') spc2.generate_resonance_structures() spc2.thermo = db.thermo.get_thermo_data(spc2) self.assertAlmostEqual(spc2.get_enthalpy(298), -212439.26998495663, 1) self.assertAlmostEqual(spc2.get_entropy(298), 283.3972662956835, 1) self.assertAlmostEqual(spc2.get_heat_capacity(1000), 118.751379824224, 1) self.assertTrue( 'arc_project_for_testing_delete_after_usage_restart_thermo' in spc2.thermo.comment) # delete the generated library from RMG-database os.remove(new_thermo_library_path)
def testaddReverseAttribute(self): """ tests that the addReverseAttribute method gets the reverse degeneracy correct """ from rmgpy.data.rmg import getDB from rmgpy.data.kinetics.family import TemplateReaction adjlist = [''' multiplicity 2 1 H u0 p0 c0 {7,S} 2 H u0 p0 c0 {4,S} 3 C u1 p0 c0 {5,S} {7,S} {8,S} 4 C u0 p0 c0 {2,S} {6,S} {7,D} 5 H u0 p0 c0 {3,S} 6 H u0 p0 c0 {4,S} 7 C u0 p0 c0 {1,S} {3,S} {4,D} 8 H u0 p0 c0 {3,S} ''', ''' 1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S} 2 C u0 p0 c0 i13 {1,S} {3,D} {7,S} 3 C u0 p0 c0 {2,D} {8,S} {9,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {3,S} 9 H u0 p0 c0 {3,S} ''', ''' multiplicity 2 1 H u0 p0 c0 {7,S} 2 H u0 p0 c0 {4,S} 3 C u1 p0 c0 {5,S} {7,S} {8,S} 4 C u0 p0 c0 {2,S} {6,S} {7,D} 5 H u0 p0 c0 {3,S} 6 H u0 p0 c0 {4,S} 7 C u0 p0 c0 i13 {1,S} {3,S} {4,D} 8 H u0 p0 c0 {3,S} ''', ''' 1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S} 2 C u0 p0 c0 {1,S} {3,D} {7,S} 3 C u0 p0 c0 {2,D} {8,S} {9,S} 4 H u0 p0 c0 {1,S} 5 H u0 p0 c0 {1,S} 6 H u0 p0 c0 {1,S} 7 H u0 p0 c0 {2,S} 8 H u0 p0 c0 {3,S} 9 H u0 p0 c0 {3,S} ''' ] family = getDB('kinetics').families['H_Abstraction'] r1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[0])]) r2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[1])]) p1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[2])]) p2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[3])]) r1.generate_resonance_structures(keep_isomorphic=True) p1.generate_resonance_structures(keep_isomorphic=True) rxn = TemplateReaction(reactants=[r1, r2], products=[p1, p2]) rxn.degeneracy = family.calculateDegeneracy(rxn) self.assertEqual(rxn.degeneracy, 6) family.addReverseAttribute(rxn) self.assertEqual(rxn.reverse.degeneracy, 6)
def test_restart(self): """ Test restarting ARC through the ARC class in main.py via the input_dict argument of the API Rather than through ARC.py. Check that all files are in place and tst file content. """ restart_path = os.path.join(arc_path, 'arc', 'testing', 'restart(H,H2O2,N2H3,CH3CO2).yml') project = 'arc_project_for_testing_delete_after_usage2' project_directory = os.path.join(arc_path, 'Projects', project) arc1 = ARC(project=project, ess_settings=dict(), input_dict=restart_path, project_directory=project_directory) arc1.execute() with open(os.path.join(project_directory, 'output', 'thermo.info'), 'r') as f: thermo_sft_ccsdtf12_bac = False for line in f.readlines(): if 'thermo_DFT_CCSDTF12_BAC' in line: thermo_sft_ccsdtf12_bac = True break self.assertTrue(thermo_sft_ccsdtf12_bac) with open(os.path.join(project_directory, 'arc_project_for_testing_delete_after_usage2.info'), 'r') as f: sts, n2h3, oet, lot, ap = False, False, False, False, False for line in f.readlines(): if 'Considered the following species and TSs:' in line: sts = True elif 'Species N2H3' in line: n2h3 = True elif 'Overall time since project initiation:' in line: oet = True elif 'Levels of theory used:' in line: lot = True elif 'ARC project arc_project_for_testing_delete_after_usage2' in line: ap = True self.assertTrue(sts) self.assertTrue(n2h3) self.assertTrue(oet) self.assertTrue(lot) self.assertTrue(ap) with open(os.path.join(project_directory, 'arc.log'), 'r') as f: aei, ver, git, spc, rtm, ldb, therm, src, ter = False, False, False, False, False, False, False, False, False for line in f.readlines(): if 'ARC execution initiated on' in line: aei = True elif '# Version:' in line: ver = True elif 'The current git HEAD for ARC is:' in line: git = True elif 'Considering species: CH3CO2_rad' in line: spc = True elif 'All jobs for species N2H3 successfully converged. Run time: 1:16:03' in line: rtm = True elif 'Loading the RMG database...' in line: ldb = True elif 'Thermodynamics for H2O2:' in line: therm = True elif 'Sources of thermoproperties determined by RMG for the parity plots:' in line: src = True elif 'ARC execution terminated on' in line: ter = True self.assertTrue(aei) self.assertTrue(ver) self.assertTrue(git) self.assertTrue(spc) self.assertTrue(rtm) self.assertTrue(ldb) self.assertTrue(therm) self.assertTrue(src) self.assertTrue(ter) self.assertTrue(os.path.isfile(os.path.join(project_directory, 'output', 'thermo_parity_plots.pdf'))) with open(os.path.join(project_directory, 'output', 'Species', 'H2O2', 'species_dictionary.txt'), 'r') as f: lines = f.readlines() adj_list = str(''.join([line for line in lines if (line and 'H2O2' not in line)])) mol1 = Molecule().fromAdjacencyList(adj_list) self.assertEqual(mol1.toSMILES(), str('OO')) thermo_library_path = os.path.join(project_directory, 'output', 'RMG libraries', 'thermo', 'arc_project_for_testing_delete_after_usage2.py') new_thermo_library_path = os.path.join(settings['database.directory'], 'thermo', 'libraries', 'arc_project_for_testing_delete_after_usage2.py') # copy the generated library to RMG-database shutil.copyfile(thermo_library_path, new_thermo_library_path) db = RMGDatabase() db.load( path=settings['database.directory'], thermoLibraries=[str('arc_project_for_testing_delete_after_usage2')], transportLibraries=[], reactionLibraries=[], seedMechanisms=[], kineticsFamilies='none', kineticsDepositories=[], statmechLibraries=None, depository=False, solvation=False, testing=True, ) spc2 = Species().fromSMILES(str('CC([O])=O')) spc2.generate_resonance_structures() spc2.thermo = db.thermo.getThermoData(spc2) self.assertAlmostEqual(spc2.getEnthalpy(298), -178003.44650359568, 1) self.assertAlmostEqual(spc2.getEntropy(298), 283.5983103176096, 1) self.assertAlmostEqual(spc2.getHeatCapacity(1000), 118.99753808225603, 1) self.assertTrue('arc_project_for_testing_delete_after_usage2' in spc2.thermo.comment) # delete the generated library from RMG-database os.remove(new_thermo_library_path)
def __init__(self, smiles=[], rmg_species=None): assert isinstance(smiles, list) self._conformers = None if ((len(smiles) != 0) and rmg_species): # Provide both a list of smiles and an rmg_species assert isinstance(rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) rmg_species.generate_resonance_structures() else: rmg_species.generate_resonance_structures() smiles_list = [] for rmg_mol in rmg_species.molecule: smiles_list.append(rmg_mol.toSMILES()) for s in smiles_list: if s in smiles: continue if not (s in smiles): smiles.append(s) assert len(smiles) == len( smiles_list ), "The list of smiles presented does not match the possible species provided" self.smiles = smiles self.rmg_species = rmg_species elif (rmg_species and (len(smiles) == 0)): # RMG species provided, but not smiles assert isinstance(rmg_species, (rmgpy.molecule.Molecule, rmgpy.species.Species)) if isinstance(rmg_species, rmgpy.molecule.Molecule): rmg_species = RMGSpecies(molecule=[rmg_species]) rmg_species.generate_resonance_structures() else: rmg_species.generate_resonance_structures() smiles = [] for rmg_mol in rmg_species.molecule: smiles.append(rmg_mol.toSMILES()) self.smiles = smiles self.rmg_species = rmg_species elif ((not rmg_species) and (len(smiles) != 0)): # smiles provided but not species species_list = [] for smile in smiles: molecule = RMGMolecule(SMILES=smile) s = RMGSpecies(molecule=[molecule]) s.generate_resonance_structures() species_list.append(s) for s1 in species_list: for s2 in species_list: assert s1.isIsomorphic( s2), "SMILESs provided describe different species" self.smiles = smiles self.rmg_species = species_list[0] else: self.smiles = [] self.rmg_species = rmg_species
def testResonanceIsomersGenerated(self): "Test that 1-penten-3-yl makes 2-penten-1-yl resonance isomer" spec = Species().fromSMILES('C=C[CH]CC') spec.generate_resonance_structures() self.assertEquals(len(spec.molecule), 2) self.assertEquals(spec.molecule[1].toSMILES(), "[CH2]C=CCC")
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 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=[], specificCollider=entry.item.specificCollider, 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.generate_resonance_structures() reactant.thermo = thermoDatabase.getThermoData(reactant) reaction.reactants.append(reactant) for molecule in entry.item.products: product = Species(molecule=[molecule]) product.generate_resonance_structures() product.thermo = thermoDatabase.getThermoData(product) reaction.products.append(product) # Generate all possible reactions involving the reactant species generatedReactions = self.generate_reactions_from_families( [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 test_resonance_isomers_generated(self): """Test that 1-penten-3-yl makes 2-penten-1-yl resonance isomer""" spec = Species().from_smiles('C=C[CH]CC') spec.generate_resonance_structures() self.assertEquals(len(spec.molecule), 2) self.assertEquals(spec.molecule[1].to_smiles(), "[CH2]C=CCC")
def testResonanceIsomersGenerated(self): "Test that 1-penten-3-yl makes 2-penten-1-yl resonance isomer" spec = Species().fromSMILES('C=C[CH]CC') spec.generate_resonance_structures() self.assertEquals(len(spec.molecule), 2) self.assertEquals(spec.molecule[1].toSMILES(), "[CH2]C=CCC")