예제 #1
0
    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)
예제 #2
0
파일: pdepTest.py 프로젝트: mbprend/RMG-Py
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))
예제 #3
0
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(
        'Optional package dependency "psutil" not found; memory profiling information will not be saved.'
    )
예제 #4
0
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))
예제 #5
0
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
예제 #6
0
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
예제 #7
0
파일: statmech.py 프로젝트: rwest/AutoTST
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