def test_prettify(self): """Test that the prettify function works for an Arkane job""" benzyl_path = os.path.join(os.path.dirname(os.path.dirname(rmgpy.__file__)), 'examples', 'arkane', 'species', 'Benzyl') arkane = Arkane(input_file=os.path.join(benzyl_path, 'input.py'), output_directory=benzyl_path) arkane.plot = False arkane.execute() with open(os.path.join(benzyl_path, 'output.py'), 'r') as f: lines = f.readlines() self.assertIn('conformer(\n', lines) self.assertIn(" E0 = (193.749, 'kJ/mol'),\n", lines) self.assertIn('thermo(\n', lines) self.assertIn(" Cp0 = (33.2579, 'J/(mol*K)'),\n", lines)
def test_pdep_job(self): """ A general test for a PDep job in Arkane """ self.tst1 = Arkane() self.tst1.input_file = self.input_file self.tst1.output_directory = self.directory self.tst1.verbose = logging.WARN self.tst1.plot = False self.tst1.job_list = [] self.tst1.job_list = self.tst1.load_input_file(self.tst1.input_file) self.tst1.execute() job = self.tst1.job_list[0] self.assertEquals(job.Tmin.value_si, 300.0) self.assertEquals(job.minimum_grain_count, 100) self.assertFalse(job.rmgmode) self.assertTrue(job.active_j_rotor) self.assertEquals(job.network.path_reactions[0].label, 'acetylperoxy <=> hydroperoxylvinoxy') self.assertAlmostEquals(job.network.path_reactions[0].transition_state.tunneling.E0_TS.value_si, -24267.2) self.assertAlmostEquals(job.network.path_reactions[0].transition_state.tunneling.frequency.value_si, -1679.04) self.assertEquals(len(job.network.net_reactions[0].reactants[0].conformer.modes), 6) # self.assertEquals(self.tst1.frequencyScaleFactor, 0.947) # test that a network pdf was generated files = [f for f in os.listdir(self.directory) if os.path.isfile(os.path.join(self.directory, f))] self.assertTrue(any(f == 'network.pdf' for f in files)) # Test the generated network reaction dictionary = {'hydroperoxylvinoxy': Species().from_smiles('[CH2]C(=O)OO'), 'acetylperoxy': Species().from_smiles('CC(=O)O[O]')} with open(os.path.join(self.directory, 'chem.inp'), 'r') as chem: reaction_list = read_reactions_block(chem, dictionary) rxn = reaction_list[0] self.assertIsInstance(rxn.kinetics, Chebyshev) # Accept a delta of 0.2, which could result from numerical discrepancies # See RMG-Py #1682 on GitHub for discussion self.assertAlmostEquals(rxn.kinetics.get_rate_coefficient(1000.0, 1.0), 88.88253229631246, delta=0.2) files = [f for f in os.listdir(os.path.join(self.directory, 'sensitivity', '')) if os.path.isfile(os.path.join(self.directory, 'sensitivity', f))] self.assertTrue(any('hydroperoxylvinoxy.pdf' in f for f in files)) with open(os.path.join(self.directory, 'sensitivity', 'network1.txt'), 'r') as f: lines = f.readlines() for line in lines: if '1000.0' in line: break sa_coeff = line.split()[-2] self.assertAlmostEquals(float(sa_coeff), -8.23e-6, delta=0.02e-6)
def testPDepJob(self): """ A general test for a PDep job in Arkane """ self.tst1 = Arkane() self.tst1.inputFile = self.input_file self.tst1.outputDirectory = self.directory self.tst1.verbose = logging.WARN self.tst1.plot = False self.tst1.jobList = [] self.tst1.jobList = self.tst1.loadInputFile(self.tst1.inputFile) self.tst1.execute() job = self.tst1.jobList[0] self.assertEquals(job.Tmin.value_si, 300.0) self.assertEquals(job.minimumGrainCount, 100) self.assertFalse(job.rmgmode) self.assertTrue(job.activeJRotor) self.assertEquals(job.network.pathReactions[0].label,'acetylperoxy <=> hydroperoxylvinoxy') self.assertAlmostEquals(job.network.pathReactions[0].transitionState.tunneling.E0_TS.value_si,-24267.2) self.assertAlmostEquals(job.network.pathReactions[0].transitionState.tunneling.frequency.value_si,-1679.04) self.assertEquals(len(job.network.netReactions[0].reactants[0].conformer.modes), 6) # self.assertEquals(self.tst1.frequencyScaleFactor, 0.947) # test that a network pdf was generated files = [f for f in os.listdir(self.directory) if os.path.isfile(os.path.join(self.directory, f))] self.assertTrue(any(f == 'network.pdf' for f in files)) # Test the generated network reaction dictionary = {'hydroperoxylvinoxy': Species().fromSMILES('[CH2]C(=O)OO'), 'acetylperoxy': Species().fromSMILES('CC(=O)O[O]')} with open(os.path.join(self.directory, 'chem.inp'), 'r') as chem: reaction_list = readReactionsBlock(chem, dictionary) rxn = reaction_list[0] self.assertIsInstance(rxn.kinetics, Chebyshev) self.assertAlmostEquals(rxn.kinetics.getRateCoefficient(1000.0, 1.0), 88.879056605) files = [f for f in os.listdir(os.path.join(self.directory, 'sensitivity', '')) if os.path.isfile(os.path.join(self.directory, 'sensitivity', f))] self.assertTrue(any('hydroperoxylvinoxy.pdf' in f for f in files)) with open(os.path.join(self.directory, 'sensitivity', 'network1.txt'), 'r') as f: lines = f.readlines() for line in lines: if '1000.0' in line: break sa_coeff = line.split()[-2] self.assertEquals(float(sa_coeff), -8.24e-6)
def __init__(self, reaction, directory=".", model_chemistry="M06-2X/cc-pVTZ", freq_scale_factor=0.982): """ A class to perform Arkane calculations: :param: reaction: (Reaction) The reaction of interest :param: output_directory: (str) The directory where you would like output files written to :param: model_chemistry: (str) The supported model_chemistry described by http://reactionmechanismgenerator.github.io/RMG-Py/users/arkane/input.html#model-chemistry :param: freq_scale_factor: (float) The scaling factor corresponding to the model chemistry - source:https://comp.chem.umn.edu/freqscale/version3b1.htm """ self.reaction = reaction self.directory = directory self.kinetics_job = RMGArkane() self.thermo_job = RMGArkane() self.model_chemistry = model_chemistry self.freq_scale_factor = freq_scale_factor
class ArkaneTest(unittest.TestCase): """ Contains unit tests for the sensitivity module in Arkane """ @classmethod def setUp(cls): """A function that is run ONCE before all unit tests in this class.""" cls.directory = os.path.join(settings['test_data.directory'], 'arkane', 'tst1', '') cls.input_file = os.path.join(cls.directory, 'pdep_sa.py') # clean working folder from all previous test output dirs = [ d for d in os.listdir(cls.directory) if not os.path.isfile(os.path.join(cls.directory, d)) ] for d in dirs: shutil.rmtree( os.path.join(settings['test_data.directory'], 'arkane', 'tst1', d, '')) files = [ f for f in os.listdir(cls.directory) if os.path.isfile(os.path.join(cls.directory, f)) ] for f in files: if 'pdep_sa' not in f: os.remove( os.path.join(settings['test_data.directory'], 'arkane', 'tst1', f)) def test_pdep_job(self): """ A general test for a PDep job in Arkane """ self.tst1 = Arkane() self.tst1.input_file = self.input_file self.tst1.output_directory = self.directory self.tst1.verbose = logging.WARN self.tst1.plot = False self.tst1.job_list = [] self.tst1.job_list = self.tst1.load_input_file(self.tst1.input_file) self.tst1.execute() job = self.tst1.job_list[0] self.assertEquals(job.Tmin.value_si, 300.0) self.assertEquals(job.minimum_grain_count, 100) self.assertFalse(job.rmgmode) self.assertTrue(job.active_j_rotor) self.assertEquals(job.network.path_reactions[0].label, 'acetylperoxy <=> hydroperoxylvinoxy') self.assertAlmostEquals( job.network.path_reactions[0].transition_state.tunneling.E0_TS. value_si, -24267.2) self.assertAlmostEquals( job.network.path_reactions[0].transition_state.tunneling.frequency. value_si, -1679.04) self.assertEquals( len(job.network.net_reactions[0].reactants[0].conformer.modes), 6) # self.assertEquals(self.tst1.frequencyScaleFactor, 0.947) # test that a network pdf was generated files = [ f for f in os.listdir(self.directory) if os.path.isfile(os.path.join(self.directory, f)) ] self.assertTrue(any(f == 'network.pdf' for f in files)) # Test the generated network reaction dictionary = { 'hydroperoxylvinoxy': Species().from_smiles('[CH2]C(=O)OO'), 'acetylperoxy': Species().from_smiles('CC(=O)O[O]') } with open(os.path.join(self.directory, 'chem.inp'), 'r') as chem: reaction_list = read_reactions_block(chem, dictionary) rxn = reaction_list[0] self.assertIsInstance(rxn.kinetics, Chebyshev) self.assertAlmostEquals(rxn.kinetics.get_rate_coefficient(1000.0, 1.0), 88.88253229631246) files = [ f for f in os.listdir( os.path.join(self.directory, 'sensitivity', '')) if os.path.isfile(os.path.join(self.directory, 'sensitivity', f)) ] self.assertTrue(any('hydroperoxylvinoxy.pdf' in f for f in files)) with open(os.path.join(self.directory, 'sensitivity', 'network1.txt'), 'r') as f: lines = f.readlines() for line in lines: if '1000.0' in line: break sa_coeff = line.split()[-2] self.assertEquals(float(sa_coeff), -8.23e-6) @classmethod def tearDown(cls): """A function that is run ONCE after all unit tests in this class.""" cls.directory = os.path.join(settings['test_data.directory'], 'arkane', 'tst1', '') cls.input_file = os.path.join(cls.directory, 'pdep_sa.py') # clean working folder from all previous test output dirs = [ d for d in os.listdir(cls.directory) if not os.path.isfile(os.path.join(cls.directory, d)) ] for d in dirs: shutil.rmtree( os.path.join(settings['test_data.directory'], 'arkane', 'tst1', d, '')) files = [ f for f in os.listdir(cls.directory) if os.path.isfile(os.path.join(cls.directory, f)) ] for f in files: if 'pdep_sa' not in f: os.remove( os.path.join(settings['test_data.directory'], 'arkane', 'tst1', f))
execute. Arkane will run the specified job, writing the output to ``output.py`` and a log to both the console and to ``Arkane.log``, with both files appearing in the same directory as the input file. Some additional command-line arguments are available; run the command :: $ python Arkane.py -h for more information. """ import os import logging from arkane.main import Arkane arkane = Arkane() # Parse and validate the command-line arguments arkane.parse_command_line_arguments() # Execute the job arkane.execute() try: import psutil process = psutil.Process(os.getpid()) memory_info = process.memory_info() logging.info('Memory used: %.2f MB' % (memory_info.rss / 1024.0 / 1024.0)) except ImportError: logging.info(
class ArkaneTest(unittest.TestCase): """ Contains unit tests for the sensitivity module in Arkane """ @classmethod def setUp(self): """A function that is run ONCE before all unit tests in this class.""" self.directory = os.path.join(settings['test_data.directory'], 'arkane', 'tst1', '') self.input_file = os.path.join(self.directory, 'pdep_sa.py') # clean working folder from all previous test output dirs = [d for d in os.listdir(self.directory) if not os.path.isfile(os.path.join(self.directory, d))] for d in dirs: shutil.rmtree(os.path.join(settings['test_data.directory'], 'arkane', 'tst1', d, '')) files = [f for f in os.listdir(self.directory) if os.path.isfile(os.path.join(self.directory, f))] for f in files: if not 'pdep_sa' in f: os.remove(os.path.join(settings['test_data.directory'], 'arkane', 'tst1', f)) def testPDepJob(self): """ A general test for a PDep job in Arkane """ self.tst1 = Arkane() self.tst1.inputFile = self.input_file self.tst1.outputDirectory = self.directory self.tst1.verbose = logging.WARN self.tst1.plot = False self.tst1.jobList = [] self.tst1.jobList = self.tst1.loadInputFile(self.tst1.inputFile) self.tst1.execute() job = self.tst1.jobList[0] self.assertEquals(job.Tmin.value_si, 300.0) self.assertEquals(job.minimumGrainCount, 100) self.assertFalse(job.rmgmode) self.assertTrue(job.activeJRotor) self.assertEquals(job.network.pathReactions[0].label,'acetylperoxy <=> hydroperoxylvinoxy') self.assertAlmostEquals(job.network.pathReactions[0].transitionState.tunneling.E0_TS.value_si,-24267.2) self.assertAlmostEquals(job.network.pathReactions[0].transitionState.tunneling.frequency.value_si,-1679.04) self.assertEquals(len(job.network.netReactions[0].reactants[0].conformer.modes), 6) # self.assertEquals(self.tst1.frequencyScaleFactor, 0.947) # test that a network pdf was generated files = [f for f in os.listdir(self.directory) if os.path.isfile(os.path.join(self.directory, f))] self.assertTrue(any(f == 'network.pdf' for f in files)) # Test the generated network reaction dictionary = {'hydroperoxylvinoxy': Species().fromSMILES('[CH2]C(=O)OO'), 'acetylperoxy': Species().fromSMILES('CC(=O)O[O]')} with open(os.path.join(self.directory, 'chem.inp'), 'r') as chem: reaction_list = readReactionsBlock(chem, dictionary) rxn = reaction_list[0] self.assertIsInstance(rxn.kinetics, Chebyshev) self.assertAlmostEquals(rxn.kinetics.getRateCoefficient(1000.0, 1.0), 88.879056605) files = [f for f in os.listdir(os.path.join(self.directory, 'sensitivity', '')) if os.path.isfile(os.path.join(self.directory, 'sensitivity', f))] self.assertTrue(any('hydroperoxylvinoxy.pdf' in f for f in files)) with open(os.path.join(self.directory, 'sensitivity', 'network1.txt'), 'r') as f: lines = f.readlines() for line in lines: if '1000.0' in line: break sa_coeff = line.split()[-2] self.assertEquals(float(sa_coeff), -8.24e-6) @classmethod def tearDown(self): """A function that is run ONCE after all unit tests in this class.""" self.directory = os.path.join(settings['test_data.directory'], 'arkane', 'tst1', '') self.input_file = os.path.join(self.directory, 'pdep_sa.py') # clean working folder from all previous test output dirs = [d for d in os.listdir(self.directory) if not os.path.isfile(os.path.join(self.directory, d))] for d in dirs: shutil.rmtree(os.path.join(settings['test_data.directory'], 'arkane', 'tst1', d, '')) files = [f for f in os.listdir(self.directory) if os.path.isfile(os.path.join(self.directory, f))] for f in files: if not 'pdep_sa' in f: os.remove(os.path.join(settings['test_data.directory'], 'arkane', 'tst1', f))
class StatMech(Calculator): def __init__( self, reaction, scratch=".", output_directory=".", model_chemistry="M06-2X/cc-pVTZ", freq_scale_factor=0.982): """ A class to perform Arkane calculations: :param: reaction: (Reaction) The reaction of interest :param: output_directory: (str) The directory where you would like output files written to :param: model_chemistry: (str) The supported model_chemistry described by http://reactionmechanismgenerator.github.io/RMG-Py/users/arkane/input.html#model-chemistry :param: freq_scale_factor: (float) The scaling factor corresponding to the model chemistry - source:https://comp.chem.umn.edu/freqscale/version3b1.htm """ self.reaction = reaction self.scratch = scratch self.arkane_job = RMGArkane() self.output_directory = output_directory self.arkane_job.outputDirectory = self.output_directory self.model_chemistry = model_chemistry self.freq_scale_factor = freq_scale_factor def get_atoms(self, conformer): """ A method to create an atom dictionary for an rmg molecule """ atom_dict = {} conf = conformer rmg_mol = conf.rmg_molecule for atom in rmg_mol.atoms: if atom.isCarbon(): atom_type = "C" if atom.isHydrogen(): atom_type = "H" if atom.isOxygen(): atom_type = "O" try: atom_dict[atom_type] += 1 except KeyError: atom_dict[atom_type] = 1 return atom_dict def get_bonds(self, conformer): conf = conformer rmg_mol = conf.rmg_molecule bondList = [] for atom in rmg_mol.atoms: for bond in atom.bonds.values(): bondList.append(bond) bonds = list(set(bondList)) bondDict = {} for bond in bonds: if bond.isSingle(): if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C-C' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'H'): bondType = 'H-H' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'H') or (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'C'): bondType = 'C-H' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'O'): bondType = 'O-O' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'C'): bondType = 'C-O' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'H'): bondType = 'O-H' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N-N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N-C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'O'): bondType = 'N-O' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'H'): bondType = 'N-H' elif bond.atom1.symbol == 'S' and bond.atom2.symbol == 'S': bondType = 'S-S' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'S') or (bond.atom1.symbol == 'S' and bond.atom2.symbol == 'H'): bondType = 'S-H' elif bond.isDouble: if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C=C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'O'): bondType = 'O=O' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'C'): bondType = 'C=O' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N=N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N=C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'O'): bondType = 'N=O' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'S') or (bond.atom1.symbol == 'S' and bond.atom2.symbol == 'O'): bondType = 'S=O' elif bond.isTriple: if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C#C' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N#N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N#C' try: bondDict[bondType] += 1 except KeyError: bondDict[bondType] = 1 return bondDict def write_arkane_for_reacts_and_prods(self, conformer): """ a method to write species to an arkane input file. Mol is an RMGMolecule """ conf = conformer mol = conf.rmg_molecule output = ['#!/usr/bin/env python', '# -*- coding: utf-8 -*-', '', 'atoms = {'] atom_dict = self.get_atoms(conf) for atom, count in atom_dict.iteritems(): output.append(" '{0}': {1},".format(atom, count)) output = output + ['}', ''] bond_dict = self.get_bonds(conf) if bond_dict != {}: output.append('bonds = {') for bond_type, num in bond_dict.iteritems(): output.append(" '{0}': {1},".format(bond_type, num)) output.append("}") else: output.append('bonds = {}') label = Chem.rdinchi.InchiToInchiKey( Chem.MolToInchi(Chem.MolFromSmiles(mol.toSMILES()))).strip("-N") external_symmetry = mol.getSymmetryNumber() output += ["", "linear = False", "", "externalSymmetry = {}".format(external_symmetry), "", "spinMultiplicity = {}".format(mol.multiplicity), "", "opticalIsomers = 1", ""] output += ["energy = {", " '{0}': Log('{1}.log'),".format( self.model_chemistry, label), "}", ""] output += ["geometry = Log('{0}.log')".format(label), ""] output += [ "frequencies = Log('{0}.log')".format(label), ""] output += ["rotors = ["] #TODO for carl for torsion in conf.torsions: output += [self.get_rotor_info(conf, torsion)] output += ["]"] input_string = "" for t in output: input_string += t + "\n" with open(os.path.join(self.scratch, label + ".py"), "w") as f: f.write(input_string) def get_rotor_info(self, conformer, torsion): """ Formats and returns info about torsion as it should appear in an Arkane species.py conformer :: autotst conformer object torsion :: autotst torsion object Needed for Arkane species file: scanLog :: Gaussian output log of freq calculation on optimized geometry pivots :: torsion center: j,k) top :: ID of all atoms in one top, starting from 1!), """ i,j,k,l = torsion.atom_indices tor_center = [j,k] #If given i,j,k,l torsion, center is j,k tor_center_adj = [j+1, k+1] # Adjusted since mol's IDs start from 0 while Arkane's start from 1 # MUST CONTAIN FREQ as well as opt geometry if isinstance(conformer, TS): tor_log = os.path.join( self.scratch, comformer.reaction_label + "_tor{0}{1}.log".format(j,k) ) else: tor_log = os.path.join( self.scratch, conformer.rmg_molecule.toAugmentedInChIKey().strip("-N") + '_tor{0}{1}.log'.format(j,k) ) top_IDs = [] for num, tf in enumerate(torsion.mask): if tf: top_IDs.append(num) top_IDs_adj = [ID+1 for ID in top_IDs] # Adjusted to start from 1 instead of 0 info = " HinderedRotor(scanLog=Log('{0}'), pivots={1}, top={2}, fit='fourier'),".format(tor_log, tor_center_adj, top_IDs_adj) return info def write_statmech_ts(self, rxn): conf = find_lowest_energy_conformer(rxn, self.scratch) output = ['#!/usr/bin/env python', '# -*- coding: utf-8 -*-', '', 'atoms = {'] atom_dict = self.get_atoms(rxn) for atom, count in atom_dict.iteritems(): output.append(" '{0}': {1},".format(atom, count)) output = output + ['}', ''] bond_dict = self.get_bonds(rxn) if bond_dict != {}: output.append('bonds = {') for bond_type, num in bond_dict.iteritems(): output.append(" '{0}': {1},".format(bond_type, num)) output.append("}") else: output.append('bonds = {}') conf.rmg_molecule.updateMultiplicity() external_symmetry = conf.rmg_molecule.getSymmetryNumber() output += ["", "linear = False", "", "externalSymmetry = {}".format(external_symmetry), "", "spinMultiplicity = {}".format(conf.rmg_molecule.multiplicity), "", "opticalIsomers = 1", ""] output += ["energy = {", " '{0}': Log('{1}.log'),".format( self.model_chemistry, rxn.label), "}", ""] output += ["geometry = Log('{0}.log')".format(rxn.label), ""] output += [ "frequencies = Log('{0}.log')".format(rxn.label), ""] output += ["rotors = []", ""] input_string = "" for t in output: input_string += t + "\n" with open(os.path.join(self.scratch, rxn.label + ".py"), "w") as f: f.write(input_string) def write_arkane_ts(self, rxn): top = [ "#!/usr/bin/env python", "# -*- coding: utf-8 -*-", "", 'modelChemistry = "{0}"'.format( self.model_chemistry), "frequencyScaleFactor = {0}".format( self.freq_scale_factor), "useHinderedRotors = False", "useBondCorrections = False", ""] labels = [] r_smiles = [] p_smiles = [] for react in rxn.reactants: conf = find_lowest_energy_conformer(react, self.scratch) r_smiles.append(conf.smiles) label = Chem.rdinchi.InchiToInchiKey( Chem.MolToInchi(Chem.MolFromSmiles(conf.smiles))).strip("-N") if label in labels: continue else: labels.append(label) line = "species('{0}', '{1}')".format( conf.smiles, label + ".py") top.append(line) for prod in rxn.products: conf = find_lowest_energy_conformer(prod, self.scratch) p_smiles.append(conf.smiles) label = Chem.rdinchi.InchiToInchiKey( Chem.MolToInchi(Chem.MolFromSmiles(conf.smiles))).strip("-N") if label in labels: continue else: labels.append(label) line = "species('{0}', '{1}')".format( conf.smiles, label + ".py") top.append(line) line = "transitionState('TS', '{0}')".format(rxn.label + ".py") top.append(line) line = ["", "reaction(", " label = '{0}',".format(rxn.label), " reactants = ['{0}', '{1}'],".format( r_smiles[0], r_smiles[1]), " products = ['{0}', '{1}'],".format( p_smiles[0], p_smiles[1]), " transitionState = 'TS',", " tunneling = 'Eckart',", ")", "", "statmech('TS')", "kinetics('{0}')".format(rxn.label)] top += line input_string = "" for t in top: input_string += t + "\n" with open(os.path.join(self.scratch, rxn.label + ".ark.py"), "w") as f: f.write(input_string) def write_files(self): for mol in self.reaction.reactants: self.write_arkane_for_reacts_and_prods(mol) for mol in self.reaction.products: self.write_arkane_for_reacts_and_prods(mol) self.write_statmech_ts(self.reaction) self.write_arkane_ts(self.reaction) def run(self): self.arkane_job.inputFile = os.path.join( self.scratch, self.reaction.label + ".ark.py") self.arkane_job.plot = False # try: self.arkane_job.execute() # except IOError: for job in self.arkane_job.jobList: if isinstance(job, KineticsJob): self.kinetics_job = job elif isinstance(job, ThermoJob) def set_reactants_and_products(self): for reactant in self.reaction.rmg_reaction.reactants: for r in self.kinetics_job.reaction.reactants: if reactant.toSMILES() == r.label: r.molecule = [reactant] for product in self.reaction.rmg_reaction.products: for p in self.kinetics_job.reaction.products: if product.toSMILES() == p.label: p.molecule = [product] self.reaction.rmg_reaction = self.kinetics_job.reaction return self.reaction
class StatMech(): def __init__(self, reaction, directory=".", model_chemistry="M06-2X/cc-pVTZ", freq_scale_factor=0.982): """ A class to perform Arkane calculations: :param: reaction: (Reaction) The reaction of interest :param: output_directory: (str) The directory where you would like output files written to :param: model_chemistry: (str) The supported model_chemistry described by http://reactionmechanismgenerator.github.io/RMG-Py/users/arkane/input.html#model-chemistry :param: freq_scale_factor: (float) The scaling factor corresponding to the model chemistry - source:https://comp.chem.umn.edu/freqscale/version3b1.htm """ self.reaction = reaction self.directory = directory self.kinetics_job = RMGArkane() self.thermo_job = RMGArkane() self.model_chemistry = model_chemistry self.freq_scale_factor = freq_scale_factor def get_atoms(self, conformer): """ A method to create an atom dictionary for an rmg molecule from a `Conformer` object. Parameters: - conformer (Conformer): a conformer object that you want atom info from Returns: - atom_dict (dict): a dictionary containing counts of different atom types """ atom_dict = {} for atom in conformer.rmg_molecule.atoms: if atom.isCarbon(): atom_type = "C" if atom.isHydrogen(): atom_type = "H" if atom.isOxygen(): atom_type = "O" try: atom_dict[atom_type] += 1 except KeyError: atom_dict[atom_type] = 1 return atom_dict def get_bonds(self, conformer): """ A method to create a bond dictionary for an rmg molecule from a `Conformer` object. Parameters: - conformer (Conformer): a conformer object that you want bond info from Returns: - bondDict (dict): a dictionary containing counts of different bond types """ bonds = conformer.rmg_molecule.getAllEdges() bondDict = {} for bond in bonds: if bond.isSingle(): if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C-C' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'H'): bondType = 'H-H' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'H') or (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'C'): bondType = 'C-H' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'O'): bondType = 'O-O' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'C'): bondType = 'C-O' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'H'): bondType = 'O-H' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N-N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N-C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'O'): bondType = 'N-O' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'H'): bondType = 'N-H' elif bond.atom1.symbol == 'S' and bond.atom2.symbol == 'S': bondType = 'S-S' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'S') or (bond.atom1.symbol == 'S' and bond.atom2.symbol == 'H'): bondType = 'S-H' elif bond.isDouble: if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C=C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'O'): bondType = 'O=O' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'C'): bondType = 'C=O' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N=N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N=C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'O'): bondType = 'N=O' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'S') or (bond.atom1.symbol == 'S' and bond.atom2.symbol == 'O'): bondType = 'S=O' elif bond.isTriple: if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C#C' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N#N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N#C' try: bondDict[bondType] += 1 except KeyError: bondDict[bondType] = 1 return bondDict def write_species_files(self, species): """ A method to write Arkane files for all conformers in a Species object Parameters: - species (Species): a species object that you want to write arkane files for - scratch (str): the directory where you want to write arkane files to, there should be a 'species/SMILES/' subdirectory Returns: - None """ for smiles, confs in list(species.conformers.items()): if os.path.exists( os.path.join(self.directory, "species", smiles, smiles + ".log")): logging.info( "Lowest energy conformer log file exists for {}".format( smiles)) self.write_conformer_file(conformer=confs[0]) else: logging.info( "Lowest energy conformer log file DOES NOT exist for {}". format(smiles)) def write_conformer_file(self, conformer): """ A method to write Arkane files for a single Conformer object Parameters: - conformer (Conformer): a Conformer object that you want to write an Arkane file for - scratch (str): the directory where you want to write arkane files to, there should be a 'species/SMILES/' subdirectory Returns: - None """ label = conformer.smiles if not os.path.exists( os.path.join(self.directory, "species", label, label + ".log")): logging.info("There is no lowest energy conformer file...") return False if os.path.exists( os.path.join(self.directory, "species", label, label + '.py')): logging.info( "Species input file already written... Not doing anything") return True parser = ccread( os.path.join(self.directory, "species", label, label + ".log")) symbol_dict = { 35: "Br", 17: "Cl", 9: "F", 8: "O", 7: "N", 6: "C", 1: "H", } atoms = [] for atom_num, coords in zip(parser.atomnos, parser.atomcoords[-1]): atoms.append(Atom(symbol=symbol_dict[atom_num], position=coords)) conformer.ase_molecule = Atoms(atoms) conformer.update_coords_from("ase") mol = conformer.rmg_molecule output = [ '#!/usr/bin/env python', '# -*- coding: utf-8 -*-', '', 'atoms = {' ] atom_dict = self.get_atoms(conformer=conformer) # Fix this for atom, count in atom_dict.items(): output.append(" '{0}': {1},".format(atom, count)) output = output + ['}', ''] bond_dict = self.get_bonds(conformer=conformer) if bond_dict != {}: output.append('bonds = {') for bond_type, num in bond_dict.items(): output.append(" '{0}': {1},".format(bond_type, num)) output.append("}") else: output.append('bonds = {}') external_symmetry = conformer.calculate_symmetry_number() output += [ "", "linear = {}".format(conformer.rmg_molecule.isLinear()), "", "externalSymmetry = {}".format(external_symmetry), "", "spinMultiplicity = {}".format( conformer.rmg_molecule.multiplicity), "", "opticalIsomers = 1", "" ] output += [ "energy = {", " '{0}': Log('{1}.log'),".format(self.model_chemistry, label), "}", "" ] # fix this output += ["geometry = Log('{0}.log')".format(label), ""] output += ["frequencies = Log('{0}.log')".format(label), ""] """ TODO: add rotor information @carl output += ["rotors = ["] for torsion in conf.torsions: output += [self.get_rotor_info(conf, torsion)] output += ["]"] """ input_string = "" for t in output: input_string += t + "\n" with open( os.path.join(self.directory, "species", label, label + '.py'), "w") as f: f.write(input_string) return True def get_rotor_info(self, conformer, torsion_index): """ Formats and returns info about torsion as it should appear in an Arkane species.py The following are needed for an Arkane input file: - scanLog :: Gaussian output log of freq calculation on optimized geometry - pivots :: torsion center: j,k of i,j,k,l (Note Arkane begins indexing with 1) - top :: ID of all atoms in one top (Note Arkane begins indexing with 1) Parameters: - conformer (Conformer): autotst conformer object - torsion (Torsion): autotst torsion object Returns: - info (str): a string containing all of the relevant information for a hindered rotor scan """ torsion = conformer.torsions[torsion_index] _, j, k, _ = torsion.atom_indices # Adjusted since mol's IDs start from 0 while Arkane's start from 1 tor_center_adj = [j + 1, k + 1] if isinstance(conformer, TS): tor_log = os.path.join( scratch, "ts", conformer.reaction_label, "torsions", comformer.reaction_label + "_36by10_{0}_{1}.log".format(j, k)) else: tor_log = os.path.join( scratch, "species", conformer.smiles, "torsions", conformer.smiles + '_36by10_{0}_{1}.log'.format(j, k)) if not os.path.exists(tor_log): logging.info( "Torsion log file does not exist for {}".format(torsion)) return "" top_IDs = [] for num, tf in enumerate(torsion.mask): if tf: top_IDs.append(num) # Adjusted to start from 1 instead of 0 top_IDs_adj = [ID + 1 for ID in top_IDs] info = " HinderedRotor(scanLog=Log('{0}'), pivots={1}, top={2}, fit='fourier'),".format( tor_log, tor_center_adj, top_IDs_adj) return info def write_ts_input(self, transitionstate): """ A method to write Arkane files for a single TS object Parameters: - transitionstate (TS): a TS object that you want to write an Arkane file for - scratch (str): the directory where you want to write arkane files to, there should be a 'ts/REACTION_LABEL/' subdirectory Returns: - None """ label = transitionstate.reaction_label if os.path.exists( os.path.join(self.directory, "ts", label, label + '.py')): logging.info("TS input file already written... Not doing anything") return True if not os.path.exists( os.path.join(self.directory, "ts", label, label + ".log")): logging.info("There is no lowest energy conformer file...") return False parser = ccread( os.path.join(self.directory, "ts", label, label + ".log")) symbol_dict = { 17: "Cl", 9: "F", 8: "O", 7: "N", 6: "C", 1: "H", } atoms = [] for atom_num, coords in zip(parser.atomnos, parser.atomcoords[-1]): atoms.append(Atom(symbol=symbol_dict[atom_num], position=coords)) transitionstate.ase_molecule = Atoms(atoms) transitionstate.update_coords_from("ase") output = [ '#!/usr/bin/env python', '# -*- coding: utf-8 -*-', '', 'atoms = {' ] atom_dict = self.get_atoms(conformer=transitionstate) # need to fix for atom, count in atom_dict.items(): output.append(" '{0}': {1},".format(atom, count)) output = output + ['}', ''] bond_dict = self.get_bonds(conformer=transitionstate) # need to fix if bond_dict != {}: output.append('bonds = {') for bond_type, num in bond_dict.items(): output.append(" '{0}': {1},".format(bond_type, num)) output.append("}") else: output.append('bonds = {}') transitionstate.rmg_molecule.updateMultiplicity() external_symmetry = transitionstate.calculate_symmetry_number() output += [ "", "linear = False", "", "externalSymmetry = {}".format(external_symmetry), "", "spinMultiplicity = {}".format( transitionstate.rmg_molecule.multiplicity), "", "opticalIsomers = 1", "" ] output += [ "energy = {", " '{0}': Log('{1}.log'),".format(self.model_chemistry, label), "}", "" ] # fix this output += ["geometry = Log('{0}.log')".format(label), ""] output += ["frequencies = Log('{0}.log')".format(label), ""] output += ["rotors = []", ""] # TODO: Fix this input_string = "" for t in output: input_string += t + "\n" with open(os.path.join(self.directory, "ts", label, label + '.py'), "w") as f: f.write(input_string) return True def write_kinetics_input(self): """ A method to write Arkane file to obtain kinetics for a Reaction object Parameters: - reaction (Reaction): a Reaction object that you want to write an Arkane kinetics job file for. - scratch (str): the directory where you want to write arkane files to, there should be a 'species/SMILES/' subdirectory Returns: - None """ top = [ "#!/usr/bin/env python", "# -*- coding: utf-8 -*-", "", 'modelChemistry = "{0}"'.format(self.model_chemistry), # fix this "frequencyScaleFactor = {0}".format( self.freq_scale_factor), # fix this "useHinderedRotors = False", # fix this @carl "useBondCorrections = False", "" ] labels = [] r_smiles = [] p_smiles = [] for i, react in enumerate(self.reaction.reactants): lowest_energy = 1e5 lowest_energy_conf = None if len(list(react.conformers.keys())) > 1: for smiles in list(react.conformers.keys()): path = os.path.join(self.directory, "species", smiles, smiles + ".log") if not os.path.exists(path): logging.info( "It looks like {} doesn't have any optimized geometries" .format(smiles)) continue parser = ccread(path) energy = parser.scfenergies[-1] if energy < lowest_energy: lowest_energy = energy lowest_energy_conf = react.conformers[smiles][0] else: smiles = list(react.conformers.keys())[0] path = os.path.join(self.directory, "species", smiles, smiles + ".log") if not os.path.exists(path): logging.info( "It looks like {} doesn't have any optimized geometries" .format(smiles)) continue parser = ccread(path) lowest_energy = parser.scfenergies[-1] lowest_energy_conf = list(react.conformers.values())[0][0] # r_smiles.append(lowest_energy_conf.smiles) r_smiles.append("react_{}".format(i)) label = lowest_energy_conf.smiles if label in labels: continue else: labels.append(label) line = "species('{0}', '{1}', structure=SMILES('{2}'))".format( "react_{}".format(i), os.path.join(self.directory, "species", label, label + ".py"), label) top.append(line) for i, prod in enumerate(self.reaction.products): lowest_energy = 1e5 lowest_energy_conf = None if len(list(prod.conformers.keys())) > 1: for smiles in list(prod.conformers.keys()): path = os.path.join(self.directory, "species", smiles, smiles + ".log") if not os.path.exists(path): logging.info( "It looks like {} doesn't have any optimized geometries" .format(smiles)) continue parser = ccread(path) energy = parser.scfenergies[-1] if energy < lowest_energy: lowest_energy = energy lowest_energy_conf = prod.conformers[smiles][0] else: smiles = list(prod.conformers.keys())[0] path = os.path.join(self.directory, "species", smiles, smiles + ".log") if not os.path.exists(path): logging.info( "It looks like {} doesn't have any optimized geometries" .format(smiles)) continue parser = ccread(path) lowest_energy = parser.scfenergies[-1] lowest_energy_conf = list(prod.conformers.values())[0][0] # p_smiles.append(lowest_energy_conf.smiles) p_smiles.append("prod_{}".format(i)) label = lowest_energy_conf.smiles if label in labels: continue else: labels.append(label) line = "species('{0}', '{1}', structure=SMILES('{2}'))".format( "prod_{}".format(i), os.path.join(self.directory, "species", label, label + ".py"), label) top.append(line) line = "transitionState('TS', '{0}')".format( os.path.join(self.directory, "ts", self.reaction.label, self.reaction.label + ".py")) top.append(line) line = [ "", "reaction(", " label = '{0}',".format(self.reaction.label), " reactants = {},".format(r_smiles), " products = {},".format(p_smiles), " transitionState = 'TS',", " tunneling = 'Eckart',", ")", "", "statmech('TS')", "kinetics('{0}')".format(self.reaction.label) ] top += line input_string = "" for t in top: input_string += t + "\n" with open( os.path.join(self.directory, "ts", self.reaction.label, self.reaction.label + ".kinetics.py"), "w") as f: f.write(input_string) def write_thermo_input(self, conformer): """ A method to write Arkane file to obtain thermochemistry for a Conformer object Parameters: - conformer (Conformer): a Conformer object that you want to write an Arkane thermo job file for. - scratch (str): the directory where you want to write arkane files to, there should be a 'species/SMILES/' subdirectory Returns: - None """ model_chemistry = self.model_chemistry freq_scale_factor = self.freq_scale_factor top = [ "#!/usr/bin/env python", "# -*- coding: utf-8 -*-", "", 'modelChemistry = "{0}"'.format(model_chemistry), # fix this "frequencyScaleFactor = {0}".format(freq_scale_factor), # fix this "useHinderedRotors = False", # fix this @carl "useBondCorrections = False", "" ] line = "species('species', '{1}', structure=SMILES('{2}'))".format( "species", os.path.join(conformer.smiles + ".py"), conformer.smiles) top.append(line) top.append("statmech('species')") top.append("thermo('species', 'NASA')") input_string = "" for t in top: input_string += t + "\n" with open( os.path.join(self.directory, "species", conformer.smiles, conformer.smiles + ".thermo.py"), "w") as f: f.write(input_string) def write_files(self): """ A method to write all species, transition state, and kinetics job files to obtain kinetic parameters Parameters: - None Returns: - None """ for mol in self.reaction.reactants: for smiles, confs in list(mol.conformers.items()): conf = confs[0] self.write_conformer_file(conf) for mol in self.reaction.products: for smiles, confs in list(mol.conformers.items()): conf = confs[0] self.write_conformer_file(conf) self.write_ts_input(self.reaction.ts["forward"][0]) self.write_kinetics_input() def run(self): """ A method to write run a kinetics job from all of the files written `write_files` Parameters: - None Returns: - None """ self.kinetics_job.inputFile = os.path.join( self.directory, "ts", self.reaction.label, self.reaction.label + ".kinetics.py") self.kinetics_job.plot = False self.kinetics_job.outputDirectory = os.path.join( self.directory, "ts", self.reaction.label) self.kinetics_job.execute() for job in self.kinetics_job.jobList: if isinstance(job, KineticsJob): self.kinetics_job = job elif isinstance(job, ThermoJob): self.thermo_job = job def set_results(self): """ A method to set the RMGReaction from the kinetics job to the RMGReaction of the input Reaction Parameters: - None Returns: - None """ for reactant in self.reaction.rmg_reaction.reactants: for r in self.kinetics_job.reaction.reactants: if reactant.toSMILES() == r.label: r.molecule = [reactant] for product in self.reaction.rmg_reaction.products: for p in self.kinetics_job.reaction.products: if product.toSMILES() == p.label: p.molecule = [product] self.reaction.rmg_reaction = self.kinetics_job.reaction return self.reaction
class StatMech(Calculator): def __init__(self, reaction, scratch=".", output_directory=".", model_chemistry="M06-2X/cc-pVTZ", freq_scale_factor=0.982): """ A class to perform Arkane calculations: :param: reaction: (Reaction) The reaction of interest :param: output_directory: (str) The directory where you would like output files written to :param: model_chemistry: (str) The supported model_chemistry described by http://reactionmechanismgenerator.github.io/RMG-Py/users/arkane/input.html#model-chemistry :param: freq_scale_factor: (float) The scaling factor corresponding to the model chemistry - source:https://comp.chem.umn.edu/freqscale/version3b1.htm """ self.reaction = reaction self.scratch = scratch self.arkane_job = RMGArkane() self.output_directory = output_directory self.arkane_job.outputDirectory = self.output_directory self.model_chemistry = model_chemistry self.freq_scale_factor = freq_scale_factor def get_atoms(self, species): """ A method to create an atom dictionary for an rmg molecule """ atom_dict = {} conf = find_lowest_energy_conformer(species, self.scratch) rmg_mol = conf.rmg_molecule for atom in rmg_mol.atoms: if atom.isCarbon(): atom_type = "C" if atom.isHydrogen(): atom_type = "H" if atom.isOxygen(): atom_type = "O" try: atom_dict[atom_type] += 1 except KeyError: atom_dict[atom_type] = 1 return atom_dict def get_bonds(self, species): conf = find_lowest_energy_conformer(species, self.scratch) rmg_mol = conf.rmg_molecule bondList = [] for atom in rmg_mol.atoms: for bond in atom.bonds.values(): bondList.append(bond) bonds = list(set(bondList)) bondDict = {} for bond in bonds: if bond.isSingle(): if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C-C' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'H'): bondType = 'H-H' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'H') or (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'C'): bondType = 'C-H' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'O'): bondType = 'O-O' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'C'): bondType = 'C-O' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'H'): bondType = 'O-H' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N-N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N-C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'O'): bondType = 'N-O' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'H'): bondType = 'N-H' elif bond.atom1.symbol == 'S' and bond.atom2.symbol == 'S': bondType = 'S-S' elif (bond.atom1.symbol == 'H' and bond.atom2.symbol == 'S') or (bond.atom1.symbol == 'S' and bond.atom2.symbol == 'H'): bondType = 'S-H' elif bond.isDouble: if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C=C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'O'): bondType = 'O=O' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'O') or (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'C'): bondType = 'C=O' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N=N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N=C' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'O'): bondType = 'N=O' elif (bond.atom1.symbol == 'O' and bond.atom2.symbol == 'S') or (bond.atom1.symbol == 'S' and bond.atom2.symbol == 'O'): bondType = 'S=O' elif bond.isTriple: if bond.atom1.symbol == 'C' and bond.atom2.symbol == 'C': bondType = 'C#C' elif bond.atom1.symbol == 'N' and bond.atom2.symbol == 'N': bondType = 'N#N' elif (bond.atom1.symbol == 'C' and bond.atom2.symbol == 'N') or (bond.atom1.symbol == 'N' and bond.atom2.symbol == 'C'): bondType = 'N#C' try: bondDict[bondType] += 1 except KeyError: bondDict[bondType] = 1 return bondDict def write_arkane_for_reacts_and_prods(self, species): """ a method to write species to an arkane input file. Mol is an RMGMolecule """ conf = find_lowest_energy_conformer(species, self.scratch) mol = conf.rmg_molecule output = [ '#!/usr/bin/env python', '# -*- coding: utf-8 -*-', '', 'atoms = {' ] atom_dict = self.get_atoms(species) for atom, count in atom_dict.iteritems(): output.append(" '{0}': {1},".format(atom, count)) output = output + ['}', ''] bond_dict = self.get_bonds(species) if bond_dict != {}: output.append('bonds = {') for bond_type, num in bond_dict.iteritems(): output.append(" '{0}': {1},".format(bond_type, num)) output.append("}") else: output.append('bonds = {}') label = Chem.rdinchi.InchiToInchiKey( Chem.MolToInchi(Chem.MolFromSmiles(mol.toSMILES()))).strip("-N") external_symmetry = mol.getSymmetryNumber() output += [ "", "linear = False", "", "externalSymmetry = {}".format(external_symmetry), "", "spinMultiplicity = {}".format(mol.multiplicity), "", "opticalIsomers = 1", "" ] output += [ "energy = {", " '{0}': Log('{1}.log'),".format(self.model_chemistry, label), "}", "" ] output += ["geometry = Log('{0}.log')".format(label), ""] output += ["frequencies = Log('{0}.log')".format(label), ""] output += ["rotors = []"] input_string = "" for t in output: input_string += t + "\n" with open(os.path.join(self.scratch, label + ".py"), "w") as f: f.write(input_string) def write_statmech_ts(self, rxn): conf = find_lowest_energy_conformer(rxn, self.scratch) output = [ '#!/usr/bin/env python', '# -*- coding: utf-8 -*-', '', 'atoms = {' ] atom_dict = self.get_atoms(rxn) for atom, count in atom_dict.iteritems(): output.append(" '{0}': {1},".format(atom, count)) output = output + ['}', ''] bond_dict = self.get_bonds(rxn) if bond_dict != {}: output.append('bonds = {') for bond_type, num in bond_dict.iteritems(): output.append(" '{0}': {1},".format(bond_type, num)) output.append("}") else: output.append('bonds = {}') conf.rmg_molecule.updateMultiplicity() external_symmetry = conf.rmg_molecule.getSymmetryNumber() output += [ "", "linear = False", "", "externalSymmetry = {}".format(external_symmetry), "", "spinMultiplicity = {}".format(conf.rmg_molecule.multiplicity), "", "opticalIsomers = 1", "" ] output += [ "energy = {", " '{0}': Log('{1}.log'),".format(self.model_chemistry, rxn.label), "}", "" ] output += ["geometry = Log('{0}.log')".format(rxn.label), ""] output += ["frequencies = Log('{0}.log')".format(rxn.label), ""] output += ["rotors = []", ""] input_string = "" for t in output: input_string += t + "\n" with open(os.path.join(self.scratch, rxn.label + ".py"), "w") as f: f.write(input_string) def write_arkane_ts(self, rxn): top = [ "#!/usr/bin/env python", "# -*- coding: utf-8 -*-", "", 'modelChemistry = "{0}"'.format(self.model_chemistry), "frequencyScaleFactor = {0}".format(self.freq_scale_factor), "useHinderedRotors = False", "useBondCorrections = False", "" ] labels = [] r_smiles = [] p_smiles = [] for react in rxn.reactants: conf = find_lowest_energy_conformer(react, self.scratch) r_smiles.append(conf.smiles) label = Chem.rdinchi.InchiToInchiKey( Chem.MolToInchi(Chem.MolFromSmiles(conf.smiles))).strip("-N") if label in labels: continue else: labels.append(label) line = "species('{0}', '{1}')".format(conf.smiles, label + ".py") top.append(line) for prod in rxn.products: conf = find_lowest_energy_conformer(prod, self.scratch) p_smiles.append(conf.smiles) label = Chem.rdinchi.InchiToInchiKey( Chem.MolToInchi(Chem.MolFromSmiles(conf.smiles))).strip("-N") if label in labels: continue else: labels.append(label) line = "species('{0}', '{1}')".format(conf.smiles, label + ".py") top.append(line) line = "transitionState('TS', '{0}')".format(rxn.label + ".py") top.append(line) line = [ "", "reaction(", " label = '{0}',".format(rxn.label), " reactants = ['{0}', '{1}'],".format(r_smiles[0], r_smiles[1]), " products = ['{0}', '{1}'],".format(p_smiles[0], p_smiles[1]), " transitionState = 'TS',", " tunneling = 'Eckart',", ")", "", "statmech('TS')", "kinetics('{0}')".format(rxn.label) ] top += line input_string = "" for t in top: input_string += t + "\n" with open(os.path.join(self.scratch, rxn.label + ".ark.py"), "w") as f: f.write(input_string) def write_files(self): for mol in self.reaction.reactants: print mol self.write_arkane_for_reacts_and_prods(mol) for mol in self.reaction.products: self.write_arkane_for_reacts_and_prods(mol) self.write_statmech_ts(self.reaction) self.write_arkane_ts(self.reaction) def run(self): self.arkane_job.inputFile = os.path.join( self.scratch, self.reaction.label + ".ark.py") print self.arkane_job.inputFile self.arkane_job.plot = False #try: self.arkane_job.execute() #except IOError: # print "There was an issue with Cairo..." for job in self.arkane_job.jobList: if isinstance(job, KineticsJob): self.kinetics_job = job def set_reactants_and_products(self): for reactant in self.reaction.rmg_reaction.reactants: for r in self.kinetics_job.reaction.reactants: if reactant.toSMILES() == r.label: r.molecule = [reactant] for product in self.reaction.rmg_reaction.products: for p in self.kinetics_job.reaction.products: if product.toSMILES() == p.label: p.molecule = [product] self.reaction.rmg_reaction = self.kinetics_job.reaction return self.reaction