Example #1
0
 def testJobackOnBenzeneBonds(self):
     "Test Joback doesn't crash on Cb desription of beneze"
     adjlist = """
                 1  C u0 p0 {2,D} {6,S} {7,S}
                 2  C u0 p0 {1,D} {3,S} {8,S}
                 3  C u0 p0 {2,S} {4,D} {9,S}
                 4  C u0 p0 {3,D} {5,S} {10,S}
                 5  C u0 p0 {4,S} {6,D} {11,S}
                 6  C u0 p0 {1,S} {5,D} {12,S}
                 7  H u0 p0 {1,S}
                 8  H u0 p0 {2,S}
                 9  H u0 p0 {3,S}
                 10 H u0 p0 {4,S}
                 11 H u0 p0 {5,S}
                 12 H u0 p0 {6,S}
                 """
     m = Molecule().fromAdjacencyList(adjlist)
     species = Species(molecule=[m])
     transportData, blank, blank2 = self.transportdb.getTransportPropertiesViaGroupEstimates(species)
     self.assertIsNotNone(transportData)
Example #2
0
def save_kinetics_lib(rxn_list, path, name, lib_long_desc):
    """
    Save an RMG kinetics library of all reactions in `rxn_list` in the supplied `path`
    `rxn_list` is a list of ARCReaction objects
    `name` is the library's name (or project's name)
    `long_desc` is a multiline string with level of theory description
    """
    entries = dict()
    if rxn_list:
        for i, rxn in enumerate(rxn_list):
            if rxn.kinetics is not None:
                if len(rxn.rmg_reaction.reactants):
                    reactants = rxn.rmg_reaction.reactants
                    products = rxn.rmg_reaction.products
                elif rxn.r_species.mol_list is not None:
                    reactants = [Species(molecule=arc_spc.mol_list) for arc_spc in rxn.r_species]
                    products = [Species(molecule=arc_spc.mol_list) for arc_spc in rxn.p_species]
                elif rxn.r_species.mol is not None:
                    reactants = [Species(molecule=[arc_spc.mol]) for arc_spc in rxn.r_species]
                    products = [Species(molecule=[arc_spc.mol]) for arc_spc in rxn.p_species]
                else:
                    reactants = [Species(molecule=[arc_spc.xyz_mol]) for arc_spc in rxn.r_species]
                    products = [Species(molecule=[arc_spc.xyz_mol]) for arc_spc in rxn.p_species]
                rxn.rmg_reaction.reactants = reactants
                rxn.rmg_reaction.products = products
                entry = Entry(
                    index=i,
                    item=rxn.rmg_reaction,
                    data=rxn.kinetics,
                    label=rxn.label)
                rxn.ts_species.make_ts_report()
                entry.longDesc = rxn.ts_species.ts_report + '\n\nOptimized TS geometry:\n' + rxn.ts_species.final_xyz
                rxn.rmg_reaction.kinetics = rxn.kinetics
                rxn.rmg_reaction.kinetics.comment = str('')
                entries[i+1] = entry
            else:
                logging.warning('Reaction {0} did not contain any kinetic data and was omitted from the kinetics'
                                ' library.'.format(rxn.label))
        kinetics_library = KineticsLibrary(name=name, longDesc=lib_long_desc, autoGenerated=True)
        kinetics_library.entries = entries
        lib_path = os.path.join(path, 'kinetics', '')
        if os.path.exists(lib_path):
            shutil.rmtree(lib_path)
        try:
            os.makedirs(lib_path)
        except OSError:
            pass
        kinetics_library.save(os.path.join(lib_path, 'reactions.py'))
        kinetics_library.saveDictionary(os.path.join(lib_path, 'dictionary.txt'))
Example #3
0
    def test_joback(self):
        """Test transport property estimation via Joback groups."""
        self.testCases = [
            ['acetone', 'CC(=O)C', Length(5.36421, 'angstroms'), Energy(3.20446, 'kJ/mol'), "Epsilon & sigma estimated with Tc=500.53 K, Pc=47.11 bar (from Joback method)"],
            ['cyclopenta-1,2-diene', 'C1=C=CCC1', None, None, None],  # not sure what to expect, we just want to make sure it doesn't crash
            ['benzene', 'c1ccccc1', None, None, None],
        ]

        # values calculate from joback's estimations
        for name, smiles, sigma, epsilon, comment in self.testCases:
            species = Species().from_smiles(smiles)
            transport_data, blank, blank2 = self.database.get_transport_properties_via_group_estimates(species)
            # check Joback worked.
            # If we don't know what to expect, don't check (just make sure we didn't crash)
            if comment:
                self.assertTrue(transport_data.comment == comment)
            if sigma:
                self.assertAlmostEqual(transport_data.sigma.value_si * 1e10, sigma.value_si * 1e10, 4)
            if epsilon:
                self.assertAlmostEqual(transport_data.epsilon.value_si, epsilon.value_si, 1)
Example #4
0
def ensure_species(input_list, resonance=False, keep_isomorphic=False):
    """
    The input list of :class:`Species` or :class:`Molecule` objects is modified
    in place to only have :class:`Species` objects. Returns None.
    """
    for index, item in enumerate(input_list):
        if isinstance(item, Molecule):
            new_item = Species(molecule=[item])
        elif isinstance(item, Species):
            new_item = item
        else:
            raise TypeError('Only Molecule or Species objects can be handled.')
        if resonance:
            if not any([mol.reactive for mol in new_item.molecule]):
                # if generating a reaction containing a Molecule with a reactive=False flag (e.g., for degeneracy
                # calculations), that was now converted into a Species, first mark as reactive=True
                new_item.molecule[0].reactive = True
            new_item.generate_resonance_structures(
                keep_isomorphic=keep_isomorphic)
        input_list[index] = new_item
Example #5
0
    def testMcGowan(self):
        "Test we can calculate and set the McGowan volume for species containing H,C,O,N or S"
        self.testCases = [
            ['CCCCCCCC', 1.2358],  #n-octane, in library
            ['C(CO)O', 0.5078],  #ethylene glycol
            ['CC#N', 0.4042],  #acetonitrile
            ['CCS', 0.5539]  #ethanethiol
        ]

        for smiles, volume in self.testCases:
            species = Species(molecule=[Molecule(SMILES=smiles)])
            soluteData = self.database.getSoluteData(species)
            soluteData.setMcGowanVolume(
                species)  # even if it was found in library, recalculate
            self.assertTrue(
                soluteData.V is not None
            )  # so if it wasn't found in library, we should have calculated it
            self.assertAlmostEqual(
                soluteData.V, volume
            )  # the volume is what we expect given the atoms and bonds
Example #6
0
    def testDeflateReaction(self):
        """
        Test if the deflateReaction function works.
        """

        molA = Molecule().fromSMILES('[OH]')
        molB = Molecule().fromSMILES('CC')
        molC = Molecule().fromSMILES('[CH3]')

        reactants = [molA, molB]

        # both reactants were already part of the core:
        reactantIndices = [1, 2]
        molDict = {molA: 1, molB: 2}

        rxn = Reaction(reactants=[molA, molB],
                       products=[molC],
                       pairs=[(molA, molC), (molB, molC)])

        deflateReaction(rxn, molDict)

        for spc, t in zip(rxn.reactants, [int, int]):
            self.assertTrue(isinstance(spc, t))
        self.assertEquals(rxn.reactants, reactantIndices)
        for spc in rxn.products:
            self.assertTrue(isinstance(spc, Species))

        # one of the reactants was not yet part of the core:
        reactantIndices = [-1, 2]
        molDict = {molA: Species(molecule=[molA]), molB: 2}

        rxn = Reaction(reactants=[molA, molB],
                       products=[molC],
                       pairs=[(molA, molC), (molB, molC)])

        deflateReaction(rxn, molDict)

        for spc, t in zip(rxn.reactants, [Species, int]):
            self.assertTrue(isinstance(spc, t))
        for spc in rxn.products:
            self.assertTrue(isinstance(spc, Species))
Example #7
0
    def test_deterministic_reaction_template_matching(self):
        """
        Test RMG work flow can match reaction template for kinetics estimation 
        deterministically. 

        In this test, a change of molecules order in a reacting species should 
        not change the reaction template matched.

        However, this is inherently impossible with the existing reaction
        generation algorithm. Currently, the first reaction will be the one
        that is kept if the reactions are identical. If different templates
        are a result of different transition states, all are kept.
        
        {O=C-[C]=C, [O]-C=C=C} -> H + C=C=C=O
        """

        # react
        spc = Species().from_smiles("O=C[C]=C")
        spc.generate_resonance_structures()
        new_reactions = react_species((spc,))

        # try to pick out the target reaction 
        mol_H = Molecule().from_smiles("[H]")
        mol_C3H2O = Molecule().from_smiles("C=C=C=O")

        target_rxns = find_target_rxns_containing(mol_H, mol_C3H2O, new_reactions)
        self.assertEqual(len(target_rxns), 2)

        # reverse the order of molecules in spc
        spc.molecule = list(reversed(spc.molecule))

        # react again
        new_reactions_reverse = []
        new_reactions_reverse.extend(react_species((spc,)))

        # try to pick out the target reaction 
        target_rxns_reverse = find_target_rxns_containing(mol_H, mol_C3H2O, new_reactions_reverse)
        self.assertEqual(len(target_rxns_reverse), 2)

        # whatever order of molecules in spc, the reaction template matched should be same
        self.assertEqual(target_rxns[0].template, target_rxns_reverse[0].template)
    def setUpClass(self):
        """A function that is run ONCE before all unit tests in this class."""
        self.database = TransportDatabase()
        self.database.load(
            os.path.join(settings['database.directory'], 'transport'),
            ['GRI-Mech', 'PrimaryTransportLibrary'])

        self.speciesList = [
            Species().fromSMILES('C'),
            Species().fromSMILES('CCCC'),
            Species().fromSMILES('O'),
            Species().fromSMILES('[CH3]'),
            Species().fromSMILES('[OH]'),
            Species().fromSMILES('c1ccccc1'),
        ]
Example #9
0
    def compare(self, inchi, u_indices=None, p_indices=None):
        u_layer = U_LAYER_PREFIX + U_LAYER_SEPARATOR.join(map(str, u_indices)) if u_indices else None
        p_layer = P_LAYER_PREFIX + P_LAYER_SEPARATOR.join(map(str, p_indices)) if p_indices else None

        aug_inchi = compose_aug_inchi(inchi, u_layer, p_layer)

        mol = from_augmented_inchi(Molecule(), aug_inchi)
        ConsistencyChecker.check_multiplicity(mol.get_radical_count(), mol.multiplicity)

        for at in mol.atoms:
            ConsistencyChecker.check_partial_charge(at)

        spc = Species(molecule=[mol])
        spc.generate_resonance_structures()

        ignore_prefix = r"(InChI=1+)(S*)/"
        aug_inchi_expected = re.split(ignore_prefix, aug_inchi)[-1]
        aug_inchi_computed = re.split(ignore_prefix, spc.get_augmented_inchi())[-1]
        self.assertEquals(aug_inchi_expected, aug_inchi_computed)

        return mol
Example #10
0
    def test_cantera(self):
        """
        Test that a Cantera Species object is created correctly.
        """
        from rmgpy.thermo import NASA, NASAPolynomial
        import cantera as ct
        rmg_species = Species(label="Ar", thermo=NASA(
            polynomials=[NASAPolynomial(coeffs=[2.5, 0, 0, 0, 0, -745.375, 4.37967], Tmin=(200, 'K'), Tmax=(1000, 'K')),
                         NASAPolynomial(coeffs=[2.5, 0, 0, 0, 0, -745.375, 4.37967], Tmin=(1000, 'K'),
                                        Tmax=(6000, 'K'))], Tmin=(200, 'K'), Tmax=(6000, 'K'), comment="""
Thermo library: primaryThermoLibrary
"""), molecule=[Molecule(smiles="[Ar]")], transport_data=TransportData(shapeIndex=0, epsilon=(1134.93, 'J/mol'),
                                                                       sigma=(3.33, 'angstrom'), dipoleMoment=(2, 'De'),
                                                                       polarizability=(1, 'angstrom^3'),
                                                                       rotrelaxcollnum=15.0, comment="""GRI-Mech"""))

        rmg_ct_species = rmg_species.to_cantera(use_chemkin_identifier=True)

        ct_species = ct.Species.fromCti("""species(name=u'Ar',
        atoms='Ar:1',
        thermo=(NASA([200.00, 1000.00],
                     [ 2.50000000E+00,  0.00000000E+00,  0.00000000E+00,
                       0.00000000E+00,  0.00000000E+00, -7.45375000E+02,
                       4.37967000E+00]),
                NASA([1000.00, 6000.00],
                     [ 2.50000000E+00,  0.00000000E+00,  0.00000000E+00,
                       0.00000000E+00,  0.00000000E+00, -7.45375000E+02,
                       4.37967000E+00])),
        transport=gas_transport(geom='atom',
                                diam=3.33,
                                well_depth=136.501,
                                dipole=2.0,
                                polar=1.0,
                                rot_relax=15.0))""")
        self.assertEqual(type(rmg_ct_species), type(ct_species))
        self.assertEqual(rmg_ct_species.name, ct_species.name)
        self.assertEqual(rmg_ct_species.composition, ct_species.composition)
        self.assertEqual(rmg_ct_species.size, ct_species.size)
        self.assertEqual(type(rmg_ct_species.thermo), type(ct_species.thermo))
        self.assertEqual(type(rmg_ct_species.transport), type(ct_species.transport))
Example #11
0
    def test_get_kinetic_isotope_effect_simple(self):
        reactant_pair = [Species().from_smiles("C"), Species().from_smiles("[H]")]
        product_pair = [Species().from_smiles("[H][H]"), Species().from_smiles("[CH3]")]
        rxn_unlabeled = TemplateReaction(reactants=reactant_pair,
                                         products=product_pair,
                                         family='H_Abstraction',
                                         kinetics=Arrhenius(A=(1e5, 'cm^3/(mol*s)'), Ea=(0, 'J/mol')))
        rxn_labeled = TemplateReaction(reactants=[
            Species().from_adjacency_list("""
1 C u0 p0 c0 i13 {2,S} {3,S} {4,S} {5,S}
2 H u0 p0 c0 {1,S}
3 H u0 p0 c0 {1,S}
4 H u0 p0 c0 {1,S}
5 H u0 p0 c0 {1,S}
"""),
            Species().from_adjacency_list("""
multiplicity 2
1 H u1 p0 c0
""")],
                                       products=[
            Species().from_adjacency_list("""
1 H u0 p0 c0 {2,S}
2 H u0 p0 c0 {1,S}
"""),
            Species().from_adjacency_list("""
multiplicity 2
1 C u1 p0 c0 i13 {2,S} {3,S} {4,S}
2 H u0 p0 c0 {1,S}
3 H u0 p0 c0 {1,S}
4 H u0 p0 c0 {1,S}
""")],
                                       family='H_Abstraction',
                                       kinetics=Arrhenius(A=(1e5, 'cm^3/(mol*s)'), Ea=(0, 'J/mol')))
        rxn_cluster = [[rxn_labeled, rxn_unlabeled]]
        apply_kinetic_isotope_effect_simple(rxn_cluster, self.database.kinetics)
        expected_kie = ((1 / 1.008 + 1 / (13.01 + 1.008)) / (1 / 1.008 + 1 / (12.01 + 1.008))) ** 0.5
        self.assertAlmostEqual(rxn_cluster[0][0].kinetics.A.value, 1e5 * expected_kie, places=-1)
Example #12
0
    def test_specifying_absolute_file_paths(self):
        """Test specifying absolute file paths of statmech files"""
        h2o2_input = """#!/usr/bin/env python
# -*- coding: utf-8 -*-

bonds = {{'H-O': 2, 'O-O': 1}}

externalSymmetry = 2

spinMultiplicity = 1

opticalIsomers = 1

energy = {{'b3lyp/6-311+g(3df,2p)': Log('{energy}')}}

geometry = Log('{freq}')

frequencies = Log('{freq}')

rotors = [HinderedRotor(scanLog=Log('{scan}'), pivots=[1, 2], top=[1, 3], symmetry=1, fit='fourier')]

"""
        abs_arkane_path = os.path.abspath(os.path.dirname(__file__))  # this is the absolute path to `.../RMG-Py/arkane`
        energy_path = os.path.join('arkane', 'data', 'H2O2', 'sp_a19032.out')
        freq_path = os.path.join('arkane', 'data', 'H2O2', 'freq_a19031.out')
        scan_path = os.path.join('arkane', 'data', 'H2O2', 'scan_a19034.out')
        h2o2_input = h2o2_input.format(energy=energy_path, freq=freq_path, scan=scan_path)
        h2o2_path = os.path.join(abs_arkane_path, 'data', 'H2O2', 'H2O2.py')
        if not os.path.exists(os.path.dirname(h2o2_path)):
            os.makedirs(os.path.dirname(h2o2_path))
        with open(h2o2_path, 'w') as f:
            f.write(h2o2_input)
        h2o2 = Species(label='H2O2', smiles='OO')
        self.assertIsNone(h2o2.conformer)
        statmech_job = StatMechJob(species=h2o2, path=h2o2_path)
        statmech_job.modelChemistry = 'b3lyp/6-311+g(3df,2p)'
        statmech_job.load(pdep=False, plot=False)
        self.assertAlmostEqual(h2o2.conformer.E0.value_si, -146031.49933673252)
        os.remove(h2o2_path)
Example #13
0
def deflateReaction(rxn, molDict):
    """
    This function deflates a single reaction, and uses the provided 
    dictionary to populate reactants/products/pairs with integer indices,
    if possible.

    If the Molecule object could not be found in the dictionary, a new
    dictionary entry is created, creating a new Species object as the value
    for the entry.

    The reactants/products/pairs of both the forward and reverse reaction 
    object are populated with the value of the dictionary, either an
    integer index, or either a Species object.
    """

    for mol in itertools.chain(rxn.reactants, rxn.products):
        if not mol in molDict:
            molDict[mol] = Species(molecule=[mol])

    rxn.reactants = [molDict[mol] for mol in rxn.reactants]
    rxn.products = [molDict[mol] for mol in rxn.products]
    rxn.pairs = [(molDict[reactant], molDict[product]) for reactant, product in rxn.pairs]
def main(inputPath, outputPath):

    species = []

    with open(inputPath, 'r+b') as f:
        for line0 in f:
            line1 = line0.strip()
            if line1 and line1[0] != '!':
                # Parse line using regex
                match = re.match(r"([^\s!]+)\s+([^\s!]+)", line1.strip())

                # Get label and SMILES string
                label = match.group(1)
                smiles = match.group(2)

                # Save in species dictionary
                spec = Species().fromSMILES(smiles)
                spec.label = label
                species.append(spec)

    # Write to dictionary file
    saveSpeciesDictionary(outputPath, species)
Example #15
0
def generate_isotopomers(spc, N=1):
    """
    Generate all isotopomers of the parameter species by adding max. N carbon isotopes to the
    atoms of the species.
    """

    mol = spc.molecule[0]
    isotope = get_element(6, 13)

    mols = []
    add_isotope(0, N, mol, mols, isotope)

    spcs = []
    for isomol in mols:
        isotopomer = Species(molecule=[isomol],
                             thermo=deepcopy(spc.thermo),
                             transport_data=spc.transport_data,
                             reactive=spc.reactive)
        isotopomer.generate_resonance_structures(keep_isomorphic=True)
        spcs.append(isotopomer)

    # do not retain identical species:
    filtered = []
    while spcs:
        candidate = spcs.pop()
        unique = True
        for isotopomer in filtered:
            if isotopomer.is_isomorphic(candidate):
                unique = False
                break
        if unique:
            filtered.append(candidate)

    if spc.thermo:
        for isotopomer in filtered:
            correct_entropy(isotopomer, spc)

    return filtered
Example #16
0
 def setUp(self):
     """
     A method that is run before each unit test in this class.
     """
     self.species = Species(
         index=1,
         label='C2H4',
         thermo=ThermoData(
             Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0,
                     1500.0], 'K'),
             Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'),
             H298=(-20.0, 'kcal/mol'),
             S298=(50.0, 'cal/(mol*K)'),
             Tmin=(300.0, 'K'),
             Tmax=(2000.0, 'K'),
         ),
         conformer=Conformer(
             E0=(0.0, 'kJ/mol'),
             modes=[
                 IdealGasTranslation(mass=(28.03, 'amu')),
                 NonlinearRotor(
                     inertia=([5.6952e-47, 2.7758e-46,
                               3.3454e-46], 'kg*m^2'),
                     symmetry=1),
                 HarmonicOscillator(frequencies=([
                     834.50, 973.31, 975.37, 1067.1, 1238.5, 1379.5, 1472.3,
                     1691.3, 3121.6, 3136.7, 3192.5, 3221.0
                 ], 'cm^-1')),
             ],
             spinMultiplicity=1,
             opticalIsomers=1,
         ),
         molecule=[Molecule().fromSMILES('C=C')],
         transportData=TransportData(sigma=(1, 'angstrom'),
                                     epsilon=(100, 'K')),
         molecularWeight=(28.03, 'amu'),
         reactive=True,
     )
Example #17
0
def create_species_from_smiles(smiles_dictionary):
    """
    Creates a dictionary with user names as keys and specie objects as values

    =========================== =======================================================================
    Input                       Description
    =========================== =======================================================================
    smiles_dictionary           A dictionary with user names as keys and SMILES strings as values
    ===================================================================================================

    =========================== =======================================================================
    Output                      Description
    =========================== =======================================================================
    user_species_dictionary     A dictionary with user names as keys and species objects as values
    ===================================================================================================
    """

    user_species_dictionary = {}
    for (user_name, smiles_string) in smiles_dictionary.iteritems():
        user_species_dictionary[user_name] = Species(
            label=user_name).fromSMILES(smiles_string)

    return user_species_dictionary
Example #18
0
    def test_mark_duplicate_reactions(self):
        """Test that we can properly mark duplicate reactions for Chemkin."""
        s1 = Species().from_smiles('CC')
        s2 = Species().from_smiles('[CH3]')
        s3 = Species().from_smiles('[OH]')
        s4 = Species().from_smiles('C[CH2]')
        s5 = Species().from_smiles('O')
        s6 = Species().from_smiles('[H]')

        # Try initializing with duplicate=False
        reaction_list = [
            Reaction(reactants=[s1], products=[s2, s2], duplicate=False, kinetics=Arrhenius()),
            Reaction(reactants=[s1], products=[s2, s2], duplicate=False, kinetics=Arrhenius()),
            Reaction(reactants=[s1, s3], products=[s4, s5], duplicate=False, kinetics=Arrhenius()),
            Reaction(reactants=[s1, s3], products=[s4, s5], duplicate=False, kinetics=Chebyshev()),
            Reaction(reactants=[s1], products=[s4, s6], duplicate=False, kinetics=Arrhenius(), reversible=False),
            Reaction(reactants=[s1], products=[s4, s6], duplicate=False, kinetics=Arrhenius(), reversible=False),
            Reaction(reactants=[s5], products=[s3, s6], duplicate=False, kinetics=Arrhenius(), reversible=False),
            Reaction(reactants=[s3, s6], products=[s5], duplicate=False, kinetics=Arrhenius(), reversible=False),
        ]

        expected_flags = [True, True, False, False, True, True, False, False]

        mark_duplicate_reactions(reaction_list)
        duplicate_flags = [rxn.duplicate for rxn in reaction_list]

        self.assertEqual(duplicate_flags, expected_flags)

        # Try initializing with duplicate=True
        reaction_list = [
            Reaction(reactants=[s1], products=[s2, s2], duplicate=True, kinetics=Arrhenius()),
            Reaction(reactants=[s1], products=[s2, s2], duplicate=True, kinetics=Arrhenius()),
            Reaction(reactants=[s1, s3], products=[s4, s5], duplicate=True, kinetics=Arrhenius()),
            Reaction(reactants=[s1, s3], products=[s4, s5], duplicate=True, kinetics=Chebyshev()),
            Reaction(reactants=[s1], products=[s4, s6], duplicate=True, kinetics=Arrhenius(), reversible=False),
            Reaction(reactants=[s1], products=[s4, s6], duplicate=True, kinetics=Arrhenius(), reversible=False),
            Reaction(reactants=[s5], products=[s3, s6], duplicate=True, kinetics=Arrhenius(), reversible=False),
            Reaction(reactants=[s3, s6], products=[s5], duplicate=True, kinetics=Arrhenius(), reversible=False),
        ]

        mark_duplicate_reactions(reaction_list)
        duplicate_flags = [rxn.duplicate for rxn in reaction_list]

        self.assertEqual(duplicate_flags, expected_flags)
Example #19
0
    def load_entry(
        self,
        index,
        label,
        solvent,
        molecule=None,
        reference=None,
        referenceType='',
        shortDesc='',
        longDesc='',
    ):
        """
        Method for parsing entries in database files.
        Note that these argument names are retained for backward compatibility.
        """
        if molecule is not None:
            if not isinstance(molecule, list):
                molecule = [molecule]
            spc_list = []
            for mol in molecule:
                spc0 = Species(label=label)
                spc0.set_structure(mol)
                spc_list.append(spc0)
        else:
            spc_list = None

        self.entries[label] = Entry(
            index=index,
            label=label,
            item=spc_list,
            data=solvent,
            reference=reference,
            reference_type=referenceType,
            short_desc=shortDesc,
            long_desc=longDesc.strip(),
        )
Example #20
0
    def testCorrectionGeneration(self):
        "Test we can estimate solvation thermochemistry."
        self.testCases = [
            # solventName, soluteName, soluteSMILES, Hsolv, Gsolv
            ['water', 'acetic acid', 'C(C)(=O)O', -56500, -6700 * 4.184],
            [
                'water', 'naphthalene', 'C1=CC=CC2=CC=CC=C12', -42800,
                -2390 * 4.184
            ],
            ['1-octanol', 'octane', 'CCCCCCCC', -40080, -4180 * 4.184],
            ['1-octanol', 'tetrahydrofuran', 'C1CCOC1', -28320, -3930 * 4.184],
            ['benzene', 'toluene', 'C1(=CC=CC=C1)C', -37660, -5320 * 4.184],
            ['benzene', '1,4-dioxane', 'C1COCCO1', -39030, -5210 * 4.184]
        ]

        for solventName, soluteName, smiles, H, G in self.testCases:
            species = Species(molecule=[Molecule(SMILES=smiles)])
            soluteData = self.database.getSoluteData(species)
            solventData = self.database.getSolventData(solventName)
            solvationCorrection = self.database.getSolvationCorrection(
                soluteData, solventData)
            self.assertAlmostEqual(
                solvationCorrection.enthalpy / 10000.,
                H / 10000.,
                0,
                msg=
                "Solvation enthalpy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}"
                .format(soluteName, solventName, solvationCorrection.enthalpy,
                        H))  #0 decimal place, in 10kJ.
            self.assertAlmostEqual(
                solvationCorrection.gibbs / 10000.,
                G / 10000.,
                0,
                msg=
                "Solvation Gibbs free energy discrepancy ({2:.0f}!={3:.0f}) for {0} in {1}"
                .format(soluteName, solventName, solvationCorrection.gibbs, G))
Example #21
0
    def test_smiles_instantiation(self):
        """Test that we can create a species using the SMILES argument"""
        test = Species(smiles='C1=CC=CC=C1')

        self.assertTrue(test.is_isomorphic(self.species2))
Example #22
0
    def test_inchi_instantiation(self):
        """Test that we can create a species using the InChI argument"""
        test = Species(inchi='InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H')

        self.assertTrue(test.is_isomorphic(self.species2))
Example #23
0
    def test_is_isomorphic_to_filtered_resonance_structure(self):
        """
        Test that a Species containing a non-representative resonance structure is isomorphic
        with the "correct" Species containing only representative structures (which were not filtered out)

        When generating resonance isomers for N/O/S atoms, a large number of resonance structures per species could
        potentially be generated, yet most are filtered out and only the "correct" / "representative" structures
        are kept. This test makes sure that if a non-representative structure (i.e., a structure that was filtered out)
        is generated, RMG finds the Species it belongs to, if the last exists.
        """

        spc1_correct = Species().from_smiles(
            '[O]N=O')  # check charge separation with higher octet deviation
        spc1_nonrepresentative = Species().from_adjacency_list(
            """multiplicity 2
                                                                1 N u1 p1 c0 {2,S} {3,S}
                                                                2 O u0 p3 c-1 {1,S}
                                                                3 O u0 p2 c+1 {1,S}"""
        )
        spc2_correct = Species().from_smiles(
            '[N]=NON=O')  # check atoms with val 6
        spc2_nonrepresentative = Species().from_adjacency_list(
            """multiplicity 2
                                                                1 O u0 p2 c0 {2,S} {3,S}
                                                                2 N u1 p1 c0 {1,S} {4,S}
                                                                3 N u0 p2 c-1 {1,S} {5,S}
                                                                4 N u0 p2 c0 {2,S}
                                                                5 O u0 p2 c+1 {3,S}"""
        )
        spc3_correct = Species().from_smiles('[O]S(O)=O')  # check O4tc penalty
        spc3_nonrepresentative = Species().from_adjacency_list(
            """multiplicity 2
                                                                1 S u0 p1 c-1 {2,S} {3,S} {4,T}
                                                                2 O u0 p2 c0 {1,S} {5,S}
                                                                3 O u1 p2 c0 {1,S}
                                                                4 O u0 p1 c+1 {1,T}
                                                                5 H u0 p0 c0 {2,S}"""
        )
        spc4_correct = Species().from_smiles(
            'OS(=[N+]=[N-])O')  # check O4dc penalty
        spc4_nonrepresentative = Species().from_adjacency_list(
            """1 S u0 p0 c+1 {2,D} {3,D} {4,S}
                                                                2 N u0 p1 c0 {1,D} {5,S}
                                                                3 O u0 p1 c+1 {1,D} {6,S}
                                                                4 O u0 p2 c0 {1,S} {7,S}
                                                                5 N u0 p3 c-2 {2,S}
                                                                6 H u0 p0 c0 {3,S}
                                                                7 H u0 p0 c0 {4,S}"""
        )
        spc5_correct = Species().from_smiles('[O][S]')  # checks birad penalty
        spc5_nonrepresentative = Species().from_adjacency_list(
            """multiplicity 3
                                                                1 O u0 p2 c0 {2,D}
                                                                2 S u2 p1 c0 {1,D}"""
        )
        spc6_correct = Species().from_smiles(
            '[N-]=[N+]=S=S=O')  # checks the S#S case
        spc6_nonrepresentative = Species().from_adjacency_list(
            """1 S u0 p1 c0 {2,S} {3,T}
                                                                2 N u0 p0 c+1 {1,S} {4,T}
                                                                3 S u0 p1 c0 {1,T} {5,S}
                                                                4 N u0 p1 c0 {2,T}
                                                                5 O u0 p3 c-1 {3,S}"""
        )

        # check that the structures are not isomorphic if resonance structures are not generated:
        self.assertFalse(
            spc1_correct.is_isomorphic(spc1_nonrepresentative, strict=True))

        # check that the nonrepresentative structure is isomorphic by generating resonance structures:
        self.assertTrue(
            spc1_correct.is_isomorphic(spc1_nonrepresentative, strict=False))
        self.assertTrue(
            spc2_correct.is_isomorphic(spc2_nonrepresentative, strict=False))
        self.assertTrue(
            spc3_correct.is_isomorphic(spc3_nonrepresentative, strict=False))
        self.assertTrue(
            spc4_correct.is_isomorphic(spc4_nonrepresentative, strict=False))
        self.assertTrue(
            spc5_correct.is_isomorphic(spc5_nonrepresentative, strict=False))
        self.assertTrue(
            spc6_correct.is_isomorphic(spc6_nonrepresentative, strict=False))
Example #24
0
 def test_resonance_isomers_generated(self):
     """Test that 1-penten-3-yl makes 2-penten-1-yl resonance isomer"""
     spec = Species().from_smiles('C=C[CH]CC')
     spec.generate_resonance_structures()
     self.assertEquals(len(spec.molecule), 2)
     self.assertEquals(spec.molecule[1].to_smiles(), "[CH2]C=CCC")
Example #25
0
def species(label, structure):
    spc = Species(label=label, molecule=[structure])
    initialSpecies[label] = spc
    return spc
Example #26
0
def loadFAMEInput(path, moleculeDict=None):
    """
    Load the contents of a FAME input file into the MEASURE object. FAME
    is an early version of MEASURE written in Fortran and used by RMG-Java.
    This script enables importing FAME input files into MEASURE so we can
    use the additional functionality that MEASURE provides. Note that it
    is mostly designed to load the FAME input files generated automatically
    by RMG-Java, and may not load hand-crafted FAME input files. If you
    specify a `moleculeDict`, then this script will use it to associate
    the species with their structures.
    """
    
    def readMeaningfulLine(f):
        line = f.readline()
        while line != '':
            line = line.strip()
            if len(line) > 0 and line[0] != '#':
                return line
            else:
                line = f.readline()
        return ''

    moleculeDict = moleculeDict or {}

    logging.info('Loading file "{0}"...'.format(path))
    f = open(path)

    job = PressureDependenceJob(network=None)
    
    # Read method
    method = readMeaningfulLine(f).lower()
    if method == 'modifiedstrongcollision': 
        job.method = 'modified strong collision'
    elif method == 'reservoirstate': 
        job.method = 'reservoir state'

    # Read temperatures
    Tcount, Tunits, Tmin, Tmax = readMeaningfulLine(f).split()
    job.Tmin = Quantity(float(Tmin), Tunits) 
    job.Tmax = Quantity(float(Tmax), Tunits)
    job.Tcount = int(Tcount)
    Tlist = []
    for i in range(int(Tcount)):
        Tlist.append(float(readMeaningfulLine(f)))
    job.Tlist = Quantity(Tlist, Tunits)
    
    # Read pressures
    Pcount, Punits, Pmin, Pmax = readMeaningfulLine(f).split()
    job.Pmin = Quantity(float(Pmin), Punits) 
    job.Pmax = Quantity(float(Pmax), Punits)
    job.Pcount = int(Pcount)
    Plist = []
    for i in range(int(Pcount)):
        Plist.append(float(readMeaningfulLine(f)))
    job.Plist = Quantity(Plist, Punits)
    
    # Read interpolation model
    model = readMeaningfulLine(f).split()
    if model[0].lower() == 'chebyshev':
        job.interpolationModel = ('chebyshev', int(model[1]), int(model[2]))
    elif model[0].lower() == 'pdeparrhenius':
        job.interpolationModel = ('pdeparrhenius',)
    
    # Read grain size or number of grains
    job.minimumGrainCount = 0
    job.maximumGrainSize = None
    for i in range(2):
        data = readMeaningfulLine(f).split()
        if data[0].lower() == 'numgrains':
            job.minimumGrainCount = int(data[1])
        elif data[0].lower() == 'grainsize':
            job.maximumGrainSize = (float(data[2]), data[1])

    # A FAME file is almost certainly created during an RMG job, so use RMG mode
    job.rmgmode = True

    # Create the Network
    job.network = Network()

    # Read collision model
    data = readMeaningfulLine(f)
    assert data.lower() == 'singleexpdown'
    alpha0units, alpha0 = readMeaningfulLine(f).split()
    T0units, T0 = readMeaningfulLine(f).split()
    n = readMeaningfulLine(f)
    energyTransferModel = SingleExponentialDown(
        alpha0 = Quantity(float(alpha0), alpha0units),
        T0 = Quantity(float(T0), T0units),
        n = float(n),
    )
    
    speciesDict = {}

    # Read bath gas parameters
    bathGas = Species(label='bath_gas', energyTransferModel=energyTransferModel)
    molWtunits, molWt = readMeaningfulLine(f).split()
    if molWtunits == 'u': molWtunits = 'amu'
    bathGas.molecularWeight = Quantity(float(molWt), molWtunits)
    sigmaLJunits, sigmaLJ = readMeaningfulLine(f).split()
    epsilonLJunits, epsilonLJ = readMeaningfulLine(f).split()
    assert epsilonLJunits == 'J'
    bathGas.transportData = TransportData(
        sigma = Quantity(float(sigmaLJ), sigmaLJunits),
        epsilon = Quantity(float(epsilonLJ) / constants.kB, 'K'),
    )
    job.network.bathGas = {bathGas: 1.0}
    
    # Read species data
    Nspec = int(readMeaningfulLine(f))
    for i in range(Nspec):
        species = Species()
        species.conformer = Conformer()
        species.energyTransferModel = energyTransferModel
        
        # Read species label
        species.label = readMeaningfulLine(f)
        speciesDict[species.label] = species
        if species.label in moleculeDict:
            species.molecule = [moleculeDict[species.label]]
        
        # Read species E0
        E0units, E0 = readMeaningfulLine(f).split()
        species.conformer.E0 = Quantity(float(E0), E0units)
        species.conformer.E0.units = 'kJ/mol'
        
        # Read species thermo data
        H298units, H298 = readMeaningfulLine(f).split()
        S298units, S298 = readMeaningfulLine(f).split()
        Cpcount, Cpunits = readMeaningfulLine(f).split()
        Cpdata = []
        for i in range(int(Cpcount)):
            Cpdata.append(float(readMeaningfulLine(f)))
        if S298units == 'J/mol*K': S298units = 'J/(mol*K)'
        if Cpunits == 'J/mol*K': Cpunits = 'J/(mol*K)'
        species.thermo = ThermoData(
            H298 = Quantity(float(H298), H298units),
            S298 = Quantity(float(S298), S298units),
            Tdata = Quantity([300,400,500,600,800,1000,1500], "K"),
            Cpdata = Quantity(Cpdata, Cpunits),
            Cp0 = (Cpdata[0], Cpunits),
            CpInf = (Cpdata[-1], Cpunits),
        )
        
        # Read species collision parameters
        molWtunits, molWt = readMeaningfulLine(f).split()
        if molWtunits == 'u': molWtunits = 'amu'
        species.molecularWeight = Quantity(float(molWt), molWtunits)
        sigmaLJunits, sigmaLJ = readMeaningfulLine(f).split()
        epsilonLJunits, epsilonLJ = readMeaningfulLine(f).split()
        assert epsilonLJunits == 'J'
        species.transportData = TransportData(
            sigma = Quantity(float(sigmaLJ), sigmaLJunits),
            epsilon = Quantity(float(epsilonLJ) / constants.kB, 'K'),
        )
        
        # Read species vibrational frequencies
        freqCount, freqUnits = readMeaningfulLine(f).split()
        frequencies = []
        for j in range(int(freqCount)):
            frequencies.append(float(readMeaningfulLine(f)))
        species.conformer.modes.append(HarmonicOscillator(
            frequencies = Quantity(frequencies, freqUnits),
        ))
        
        # Read species external rotors
        rotCount, rotUnits = readMeaningfulLine(f).split()
        if int(rotCount) > 0:
            raise NotImplementedError('Cannot handle external rotational modes in FAME input.')
        
        # Read species internal rotors
        freqCount, freqUnits = readMeaningfulLine(f).split()
        frequencies = []
        for j in range(int(freqCount)):
            frequencies.append(float(readMeaningfulLine(f)))
        barrCount, barrUnits = readMeaningfulLine(f).split()
        barriers = []
        for j in range(int(barrCount)):
            barriers.append(float(readMeaningfulLine(f)))
        if barrUnits == 'cm^-1':
            barrUnits = 'J/mol'
            barriers = [barr * constants.h * constants.c * constants.Na * 100. for barr in barriers]
        elif barrUnits in ['Hz', 's^-1']:
            barrUnits = 'J/mol'
            barriers = [barr * constants.h * constants.Na for barr in barriers]
        elif barrUnits != 'J/mol':
            raise Exception('Unexpected units "{0}" for hindered rotor barrier height.'.format(barrUnits))
        inertia = [V0 / 2.0 / (nu * constants.c * 100.)**2 / constants.Na for nu, V0 in zip(frequencies, barriers)]
        for I, V0 in zip(inertia, barriers):
            species.conformer.modes.append(HinderedRotor(
                inertia = Quantity(I,"kg*m^2"), 
                barrier = Quantity(V0,barrUnits), 
                symmetry = 1,
                semiclassical = False,
            ))
            
        # Read overall symmetry number
        species.conformer.spinMultiplicity = int(readMeaningfulLine(f))
        
    # Read isomer, reactant channel, and product channel data
    Nisom = int(readMeaningfulLine(f))
    Nreac = int(readMeaningfulLine(f))
    Nprod = int(readMeaningfulLine(f))
    for i in range(Nisom):
        data = readMeaningfulLine(f).split()
        assert data[0] == '1'
        job.network.isomers.append(speciesDict[data[1]])
    for i in range(Nreac):
        data = readMeaningfulLine(f).split()
        assert data[0] == '2'
        job.network.reactants.append([speciesDict[data[1]], speciesDict[data[2]]])
    for i in range(Nprod):
        data = readMeaningfulLine(f).split()
        if data[0] == '1':
            job.network.products.append([speciesDict[data[1]]])
        elif data[0] == '2':
            job.network.products.append([speciesDict[data[1]], speciesDict[data[2]]])

    # Read path reactions
    Nrxn = int(readMeaningfulLine(f))
    for i in range(Nrxn):
        
        # Read and ignore reaction equation
        equation = readMeaningfulLine(f)
        reaction = Reaction(transitionState=TransitionState(), reversible=True)
        job.network.pathReactions.append(reaction)
        reaction.transitionState.conformer = Conformer()
        
        # Read reactant and product indices
        data = readMeaningfulLine(f).split()
        reac = int(data[0]) - 1
        prod = int(data[1]) - 1
        if reac < Nisom:
            reaction.reactants = [job.network.isomers[reac]]
        elif reac < Nisom+Nreac:
            reaction.reactants = job.network.reactants[reac-Nisom]
        else:
            reaction.reactants = job.network.products[reac-Nisom-Nreac]
        if prod < Nisom:
            reaction.products = [job.network.isomers[prod]]
        elif prod < Nisom+Nreac:
            reaction.products = job.network.reactants[prod-Nisom]
        else:
            reaction.products = job.network.products[prod-Nisom-Nreac]
        
        # Read reaction E0
        E0units, E0 = readMeaningfulLine(f).split()
        reaction.transitionState.conformer.E0 = Quantity(float(E0), E0units)
        reaction.transitionState.conformer.E0.units = 'kJ/mol'
        
        # Read high-pressure limit kinetics
        data = readMeaningfulLine(f)
        assert data.lower() == 'arrhenius'
        Aunits, A = readMeaningfulLine(f).split()
        if '/' in Aunits:
            index = Aunits.find('/')
            Aunits = '{0}/({1})'.format(Aunits[0:index], Aunits[index+1:])
        Eaunits, Ea = readMeaningfulLine(f).split()
        n = readMeaningfulLine(f)
        reaction.kinetics = Arrhenius(
            A = Quantity(float(A), Aunits),
            Ea = Quantity(float(Ea), Eaunits),
            n = Quantity(float(n)),
        )
        reaction.kinetics.Ea.units = 'kJ/mol'

    f.close()
    
    job.network.isomers = [Configuration(isomer) for isomer in job.network.isomers]
    job.network.reactants = [Configuration(*reactants) for reactants in job.network.reactants]
    job.network.products = [Configuration(*products) for products in job.network.products]

    return job
Example #27
0
    def getForwardReactionForFamilyEntry(self, entry, family, thermoDatabase):
        """
        For a given `entry` for a reaction of the given reaction `family` (the
        string label of the family), return the reaction with kinetics and
        degeneracy for the "forward" direction as defined by the reaction 
        family. For families that are their own reverse, the direction the
        kinetics is given in will be preserved. If the entry contains 
        functional groups for the reactants, assume that it is given in the 
        forward direction and do nothing. Returns the reaction in the direction
        consistent with the reaction family template, and the matching template.
        Note that the returned reaction will have its kinetics and degeneracy
        set appropriately.
        
        In order to reverse the reactions that are given in the reverse of the
        direction the family is defined, we need to compute the thermodynamics
        of the reactants and products. For this reason you must also pass
        the `thermoDatabase` to use to generate the thermo data.
        """
        def generateThermoData(species, thermoDatabase):
            thermoData = [thermoDatabase.getThermoData(species)]
            thermoData.sort(key=lambda x: x.getEnthalpy(298))
            return thermoData[0]

        def matchSpeciesToMolecules(species, molecules):
            if len(species) == len(molecules) == 1:
                return species[0].isIsomorphic(molecules[0])
            elif len(species) == len(molecules) == 2:
                if species[0].isIsomorphic(
                        molecules[0]) and species[1].isIsomorphic(
                            molecules[1]):
                    return True
                elif species[0].isIsomorphic(
                        molecules[1]) and species[1].isIsomorphic(
                            molecules[0]):
                    return True
            return False

        reaction = None
        template = None

        # Get the indicated reaction family
        try:
            groups = self.families[family].groups
        except KeyError:
            raise ValueError(
                'Invalid value "{0}" for family parameter.'.format(family))

        if all([(isinstance(reactant, Group)
                 or isinstance(reactant, LogicNode))
                for reactant in entry.item.reactants]):
            # The entry is a rate rule, containing functional groups only
            # By convention, these are always given in the forward direction and
            # have kinetics defined on a per-site basis
            reaction = Reaction(
                reactants=entry.item.reactants[:],
                products=[],
                kinetics=entry.data,
                degeneracy=1,
            )
            template = [
                groups.entries[label] for label in entry.label.split(';')
            ]

        elif (all([
                isinstance(reactant, Molecule)
                for reactant in entry.item.reactants
        ]) and all(
            [isinstance(product, Molecule)
             for product in entry.item.products])):
            # The entry is a real reaction, containing molecules
            # These could be defined for either the forward or reverse direction
            # and could have a reaction-path degeneracy

            reaction = Reaction(reactants=[], products=[])
            for molecule in entry.item.reactants:
                reactant = Species(molecule=[molecule])
                reactant.generateResonanceIsomers()
                reactant.thermo = generateThermoData(reactant, thermoDatabase)
                reaction.reactants.append(reactant)
            for molecule in entry.item.products:
                product = Species(molecule=[molecule])
                product.generateResonanceIsomers()
                product.thermo = generateThermoData(product, thermoDatabase)
                reaction.products.append(product)

            # Generate all possible reactions involving the reactant species
            generatedReactions = self.generateReactionsFromFamilies(
                [reactant.molecule for reactant in reaction.reactants], [],
                only_families=[family])

            # Remove from that set any reactions that don't produce the desired reactants and products
            forward = []
            reverse = []
            for rxn in generatedReactions:
                if matchSpeciesToMolecules(
                        reaction.reactants,
                        rxn.reactants) and matchSpeciesToMolecules(
                            reaction.products, rxn.products):
                    forward.append(rxn)
                if matchSpeciesToMolecules(
                        reaction.reactants,
                        rxn.products) and matchSpeciesToMolecules(
                            reaction.products, rxn.reactants):
                    reverse.append(rxn)

            # We should now know whether the reaction is given in the forward or
            # reverse direction
            if len(forward) == 1 and len(reverse) == 0:
                # The reaction is in the forward direction, so use as-is
                reaction = forward[0]
                template = reaction.template
                # Don't forget to overwrite the estimated kinetics from the database with the kinetics for this entry
                reaction.kinetics = entry.data
            elif len(reverse) == 1 and len(forward) == 0:
                # The reaction is in the reverse direction
                # First fit Arrhenius kinetics in that direction
                Tdata = 1000.0 / numpy.arange(0.5, 3.301, 0.1, numpy.float64)
                kdata = numpy.zeros_like(Tdata)
                for i in range(Tdata.shape[0]):
                    kdata[i] = entry.data.getRateCoefficient(
                        Tdata[i]) / reaction.getEquilibriumConstant(Tdata[i])
                kunits = 'm^3/(mol*s)' if len(
                    reverse[0].reactants) == 2 else 's^-1'
                kinetics = Arrhenius().fitToData(Tdata, kdata, kunits, T0=1.0)
                kinetics.Tmin = entry.data.Tmin
                kinetics.Tmax = entry.data.Tmax
                kinetics.Pmin = entry.data.Pmin
                kinetics.Pmax = entry.data.Pmax
                # Now flip the direction
                reaction = reverse[0]
                reaction.kinetics = kinetics
                template = reaction.template
            elif len(reverse) > 0 and len(forward) > 0:
                print 'FAIL: Multiple reactions found for {0!r}.'.format(
                    entry.label)
            elif len(reverse) == 0 and len(forward) == 0:
                print 'FAIL: No reactions found for "%s".' % (entry.label)
            else:
                print 'FAIL: Unable to estimate kinetics for {0!r}.'.format(
                    entry.label)

        assert reaction is not None
        assert template is not None
        return reaction, template
Example #28
0
    def test_restart(self):
        """
        Test restarting ARC through the ARC class in main.py via the input_dict argument of the API
        Rather than through ARC.py. Check that all files are in place and tst file content.
        """
        restart_path = os.path.join(arc_path, 'arc', 'testing', 'restart(H,H2O2,N2H3,CH3CO2).yml')
        project = 'arc_project_for_testing_delete_after_usage2'
        project_directory = os.path.join(arc_path, 'Projects', project)
        arc1 = ARC(project=project, ess_settings=dict(),
                   input_dict=restart_path, project_directory=project_directory)
        arc1.execute()

        with open(os.path.join(project_directory, 'output', 'thermo.info'), 'r') as f:
            thermo_sft_ccsdtf12_bac = False
            for line in f.readlines():
                if 'thermo_DFT_CCSDTF12_BAC' in line:
                    thermo_sft_ccsdtf12_bac = True
                    break
        self.assertTrue(thermo_sft_ccsdtf12_bac)

        with open(os.path.join(project_directory, 'arc_project_for_testing_delete_after_usage2.info'), 'r') as f:
            sts, n2h3, oet, lot, ap = False, False, False, False, False
            for line in f.readlines():
                if 'Considered the following species and TSs:' in line:
                    sts = True
                elif 'Species N2H3' in line:
                    n2h3 = True
                elif 'Overall time since project initiation:' in line:
                    oet = True
                elif 'Levels of theory used:' in line:
                    lot = True
                elif 'ARC project arc_project_for_testing_delete_after_usage2' in line:
                    ap = True
        self.assertTrue(sts)
        self.assertTrue(n2h3)
        self.assertTrue(oet)
        self.assertTrue(lot)
        self.assertTrue(ap)

        with open(os.path.join(project_directory, 'arc.log'), 'r') as f:
            aei, ver, git, spc, rtm, ldb, therm, src, ter = False, False, False, False, False, False, False, False, False
            for line in f.readlines():
                if 'ARC execution initiated on' in line:
                    aei = True
                elif '#   Version:' in line:
                    ver = True
                elif 'The current git HEAD for ARC is:' in line:
                    git = True
                elif 'Considering species: CH3CO2_rad' in line:
                    spc = True
                elif 'All jobs for species N2H3 successfully converged. Run time: 1:16:03' in line:
                    rtm = True
                elif 'Loading the RMG database...' in line:
                    ldb = True
                elif 'Thermodynamics for H2O2:' in line:
                    therm = True
                elif 'Sources of thermoproperties determined by RMG for the parity plots:' in line:
                    src = True
                elif 'ARC execution terminated on' in line:
                    ter = True
        self.assertTrue(aei)
        self.assertTrue(ver)
        self.assertTrue(git)
        self.assertTrue(spc)
        self.assertTrue(rtm)
        self.assertTrue(ldb)
        self.assertTrue(therm)
        self.assertTrue(src)
        self.assertTrue(ter)

        self.assertTrue(os.path.isfile(os.path.join(project_directory, 'output', 'thermo_parity_plots.pdf')))

        with open(os.path.join(project_directory, 'output', 'Species', 'H2O2', 'species_dictionary.txt'), 'r') as f:
            lines = f.readlines()
        adj_list = str(''.join([line for line in lines if (line and 'H2O2' not in line)]))
        mol1 = Molecule().fromAdjacencyList(adj_list)
        self.assertEqual(mol1.toSMILES(), str('OO'))

        thermo_library_path = os.path.join(project_directory, 'output', 'RMG libraries', 'thermo',
                                           'arc_project_for_testing_delete_after_usage2.py')
        new_thermo_library_path = os.path.join(settings['database.directory'], 'thermo', 'libraries',
                                               'arc_project_for_testing_delete_after_usage2.py')
        # copy the generated library to RMG-database
        shutil.copyfile(thermo_library_path, new_thermo_library_path)
        db = RMGDatabase()
        db.load(
            path=settings['database.directory'],
            thermoLibraries=[str('arc_project_for_testing_delete_after_usage2')],
            transportLibraries=[],
            reactionLibraries=[],
            seedMechanisms=[],
            kineticsFamilies='none',
            kineticsDepositories=[],
            statmechLibraries=None,
            depository=False,
            solvation=False,
            testing=True,
        )

        spc2 = Species().fromSMILES(str('CC([O])=O'))
        spc2.generate_resonance_structures()
        spc2.thermo = db.thermo.getThermoData(spc2)
        self.assertAlmostEqual(spc2.getEnthalpy(298), -178003.44650359568, 1)
        self.assertAlmostEqual(spc2.getEntropy(298), 283.5983103176096, 1)
        self.assertAlmostEqual(spc2.getHeatCapacity(1000), 118.99753808225603, 1)
        self.assertTrue('arc_project_for_testing_delete_after_usage2' in spc2.thermo.comment)

        # delete the generated library from RMG-database
        os.remove(new_thermo_library_path)
Example #29
0
    def saveOld(self, path):
        """
        Save an old-style reaction library to `path`. This creates files named
        ``species.txt``, ``reactions.txt``, and ``pdepreactions.txt`` in the
        given directory; these contain the species dictionary, high-pressure
        limit reactions and kinetics, and pressure-dependent reactions and
        kinetics, respectively.
        """
        try:
            os.makedirs(path)
        except OSError:
            pass

        def writeArrhenius(f, arrhenius):
            f.write(
                ' {0:<12.3E} {1:>7.3f} {2:>11.2f}    {3}{4:g} {5:g} {6:g}\n'.
                format(
                    arrhenius.A.value_si,
                    arrhenius.n.value_si,
                    arrhenius.Ea.value_si / 4.184,
                    '*' if arrhenius.A.isUncertaintyMultiplicative() else '',
                    arrhenius.A.uncertainty,
                    arrhenius.n.uncertainty,
                    arrhenius.Ea.uncertainty / 4.184,
                ))

        # Gather all of the species used in this kinetics library
        speciesDict = self.getSpecies()
        # Also include colliders in the above
        for entry in self.entries.values():
            if isinstance(entry.data, ThirdBody):
                for molecule in entry.data.efficiencies:
                    formula = molecule.getFormula()
                    if formula in ['He', 'Ar', 'N2', 'Ne']:
                        pass
                    else:
                        found = False
                        for species in speciesDict.values():
                            for mol in species.molecule:
                                if mol.isIsomorphic(molecule):
                                    found = True
                                    break
                        if not found:
                            speciesDict[formula] = Species(label=formula,
                                                           molecule=[molecule])

        entries = self.entries.values()
        entries.sort(key=lambda x: x.index)

        # Save the species dictionary
        speciesList = speciesDict.values()
        speciesList.sort(key=lambda x: x.label)
        f = open(os.path.join(path, 'species.txt'), 'w')
        for species in speciesList:
            f.write(species.molecule[0].toAdjacencyList(label=species.label,
                                                        removeH=False) + "\n")
        f.close()

        # Save the high-pressure limit reactions
        # Currently only Arrhenius kinetics are allowed
        f = open(os.path.join(path, 'reactions.txt'), 'w')
        f.write('Unit:\n')
        f.write('A: mol/m3/s\n')
        f.write('E: cal/mol\n\n')
        f.write('Reactions:\n')
        for entry in entries:
            kinetics = entry.data
            rateList = []
            if isinstance(kinetics, MultiArrhenius):
                entry.item.duplicate = True
                rateList = kinetics.arrhenius[:]
            else:
                if not kinetics.isPressureDependent():
                    rateList.append(kinetics)
            for rate in rateList:
                # Write reaction equation
                f.write('{0:<59}'.format(entry.item))
                # Write kinetics
                if isinstance(rate, Arrhenius):
                    writeArrhenius(f, rate)
                else:
                    raise DatabaseError(
                        'Unexpected kinetics type "{0}" encountered while saving old kinetics library (reactions.txt).'
                        .format(rate.__class__))
                # Mark as duplicate if needed
                if entry.item.duplicate:
                    f.write(' DUPLICATE\n')
        f.close()

        # Save the pressure-dependent reactions
        # Currently only ThirdBody, Lindemann, Troe, and PDepArrhenius kinetics are allowed
        f = open(os.path.join(path, 'pdepreactions.txt'), 'w')
        f.write('Unit:\n')
        f.write('A: mol/m3/s\n')
        f.write('E: cal/mol\n\n')
        f.write('Reactions:\n')
        for entry in entries:
            kinetics = entry.data
            if not kinetics.isPressureDependent():
                continue
            rateList = []
            if isinstance(kinetics, MultiPDepArrhenius):
                entry.item.duplicate = True
                rateList = kinetics.arrhenius[:]
            else:
                rateList.append(kinetics)
            for rate in rateList:
                # Write reaction equation
                equation = str(entry.item)
                if entry.item.reversible:
                    index = equation.find('<=>')
                else:
                    index = equation.find('=>')
                if isinstance(rate,
                              ThirdBody) and not isinstance(rate, Lindemann):
                    equation = '{0}+ M {1} + M'.format(equation[0:index],
                                                       equation[index:])
                elif isinstance(rate, PDepArrhenius):
                    pass
                else:
                    equation = '{0}(+M) {1} (+M)'.format(
                        equation[0:index], equation[index:])
                f.write('{0:<59}'.format(equation))
                # Write kinetics
                if isinstance(rate, (ThirdBody, Lindemann, Troe)):
                    if isinstance(rate, Lindemann):
                        # Lindemann (and Troe) fall-off have the High-P as default, and Low-P labeled LOW
                        writeArrhenius(f, rate.arrheniusHigh)
                    else:
                        # Non-falloff ThirdBody reactions are always in the Low-P limit
                        writeArrhenius(f, rate.arrheniusLow)
                    if len(rate.efficiencies) > 0:
                        eff_line = ''
                        for molecule, efficiency in rate.efficiencies.iteritems(
                        ):
                            for spec in speciesDict.values():
                                if molecule in spec.molecule:
                                    mol_label = spec.label
                                    break
                            else:
                                mol_label = molecule.getFormula().upper()
                            eff_line += '{0}/{1:g}/  '.format(
                                mol_label, efficiency)
                        f.write(eff_line.strip() + '\n')
                    if isinstance(rate, Lindemann):
                        f.write('     LOW  /  {0:10.3e} {1:9.3f} {2:10.2f}/\n'.
                                format(
                                    rate.arrheniusLow.A.value_si,
                                    rate.arrheniusLow.n.value_si,
                                    rate.arrheniusLow.Ea.value_si / 4.184,
                                ))
                    if isinstance(rate, Troe):
                        if rate.T2 is not None:
                            f.write(
                                '     TROE /  {0:10.4f} {1:10.2g} {2:10.2g} {3:10.2g}/\n'
                                .format(
                                    rate.alpha,
                                    rate.T3.value_si,
                                    rate.T1.value_si,
                                    rate.T2.value_si,
                                ))
                        else:
                            f.write(
                                '     TROE /  {0:10.4f} {1:10.2g} {2:10.2g}/\n'
                                .format(
                                    rate.alpha,
                                    rate.T3.value_si,
                                    rate.T1.value_si,
                                ))

                elif isinstance(rate, PDepArrhenius):
                    writeArrhenius(f, rate.arrhenius[-1])
                    for pressure, arrhenius in zip(rate.pressures.value_si,
                                                   rate.arrhenius):
                        f.write(
                            '     PLOG /  {0:10g} {1:10.3e} {2:9.3f} {3:10.2f} /\n'
                            .format(
                                pressure / 1e5,
                                arrhenius.A.value_si,
                                arrhenius.n.value_si,
                                arrhenius.Ea.value_si / 4.184,
                            ))
                else:
                    raise DatabaseError(
                        'Unexpected kinetics type "{0}" encountered while saving old kinetics library (reactions.txt).'
                        .format(rate.__class__))
                # Mark as duplicate if needed
                if entry.item.duplicate:
                    f.write(' DUPLICATE\n')
                f.write('\n')
        f.close()
Example #30
0
    def loadEntry(
        self,
        index,
        reactant1,
        product1,
        kinetics,
        reactant2=None,
        reactant3=None,
        product2=None,
        product3=None,
        degeneracy=1,
        label='',
        duplicate=False,
        reversible=True,
        reference=None,
        referenceType='',
        shortDesc='',
        longDesc='',
    ):

        reactants = [
            Species(label=reactant1.strip().splitlines()[0].strip(),
                    molecule=[Molecule().fromAdjacencyList(reactant1)])
        ]
        if reactant2 is not None:
            reactants.append(
                Species(label=reactant2.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(reactant2)]))
        if reactant3 is not None:
            reactants.append(
                Species(label=reactant3.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(reactant3)]))

        products = [
            Species(label=product1.strip().splitlines()[0].strip(),
                    molecule=[Molecule().fromAdjacencyList(product1)])
        ]
        if product2 is not None:
            products.append(
                Species(label=product2.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(product2)]))
        if product3 is not None:
            products.append(
                Species(label=product3.strip().splitlines()[0].strip(),
                        molecule=[Molecule().fromAdjacencyList(product3)]))

        comment = "Reaction and kinetics from {0}.".format(self.label)
        if shortDesc.strip():
            comment += "{0!s}\n".format(shortDesc.strip())
        if longDesc.strip():
            comment += str(re.sub('\s*\n\s*', '\n', longDesc))
        kinetics.comment = comment.strip()

        # Perform mass balance check on the reaction
        rxn = Reaction(reactants=reactants,
                       products=products,
                       degeneracy=degeneracy,
                       duplicate=duplicate,
                       reversible=reversible)
        if not rxn.isBalanced():
            raise DatabaseError(
                'Reaction {0} in kinetics library {1} was not balanced! Please reformulate.'
                .format(rxn, self.label))

        assert index not in self.entries, "Reaction with index {0} already present!".format(
            index)
        self.entries[index] = Entry(
            index=index,
            label=label,
            item=rxn,
            data=kinetics,
            reference=reference,
            referenceType=referenceType,
            shortDesc=shortDesc,
            longDesc=longDesc.strip(),
        )

        # Convert SMILES to Molecule objects in collision efficiencies
        if isinstance(kinetics, PDepKineticsModel):
            efficiencies = {}
            for smiles, eff in kinetics.efficiencies.items():
                if isinstance(smiles, str):
                    efficiencies[Molecule().fromSMILES(smiles)] = eff
            kinetics.efficiencies = efficiencies