Example #1
0
def correctDegeneracyOfReverseReactions(reactionList, reactants):
    """
    This method corrects the degeneracy of reactions found when the backwards
    template is used. Given the following parameters:

        reactionList - list of reactions with their degeneracies already counted
        reactants - list/tuple of species used in the generateReactions method

    This method modifies reactionList in place and returns nothing
    
    This does not adjust for identical reactants, you need to use `reduceSameReactantDegeneracy`
    to adjust for that.
    """
    from rmgpy.reaction import _isomorphicSpeciesList
    from rmgpy.reaction import ReactionError

    for rxn in reactionList:
        if _isomorphicSpeciesList(rxn.reactants, reactants):
            # was forward reaction so ignore
            continue
        elif _isomorphicSpeciesList(rxn.products, reactants):
            # was reverse reaction so should find degeneracy
            family = getDB('kinetics').families[rxn.family]
            if not family.ownReverse:
                rxn.degeneracy = family.calculateDegeneracy(
                    rxn, ignoreSameReactants=True)
        else:
            # wrong reaction was sent here
            raise ReactionError(
                'Reaction in reactionList did not match reactants. Reaction: {}, Reactants: {}'
                .format(rxn, reactants))
Example #2
0
    def generateStatMech(self):
        """
        Generate molecular degree of freedom data for the species. You must
        have already provided a thermodynamics model using e.g.
        :meth:`generateThermoData()`.
        """
        logging.debug("Generating statmech for species {}".format(self.label))
        from rmgpy.data.rmg import getDB
        try:
            statmechDB = getDB('statmech')        
            if not statmechDB: raise Exception
        except Exception:
            logging.debug('Could not obtain the stat. mech database. Not generating stat. mech...')
            raise

        molecule = self.molecule[0]
        conformer = statmechDB.getStatmechData(molecule, self.getThermoData())

        if self.conformer is None:
            self.conformer = Conformer()

        if self.conformer.E0 is None:
            self.setE0WithThermo()

        self.conformer.modes = conformer.modes
        self.conformer.spinMultiplicity = conformer.spinMultiplicity
        if self.conformer.E0 is None or not self.hasStatMech():
            from rmgpy.exceptions import StatmechError
            logging.error('The conformer in question is {}'.format(self.conformer))
            raise StatmechError('Species {0} does not have stat mech after generateStatMech called'.format(self.label))
Example #3
0
def reactMolecules(moleculeTuples):
    """
    Performs a reaction between
    the resonance isomers.

    The parameter contains a list of tuples with each tuple:
    (Molecule, index of the core species it belongs to)
    """

    families = getDB('kinetics').families

    molecules, reactantIndices = zip(*moleculeTuples)

    reactionList = []
    for _, family in families.iteritems():
        rxns = family.generateReactions(molecules)
        reactionList.extend(rxns)

    for reactant in molecules:
        reactant.clearLabeledAtoms()

    for rxn in reactionList:
        deflate(rxn, molecules, reactantIndices)

    return reactionList
Example #4
0
    def test_calculate_degeneracy_for_non_reactive_molecule(self):
        """
        tests that the calculateDegeneracy method gets the degeneracy correct for unreactive molecules
        and that __generateReactions work correctly with the react_non_reactive flag set to `True`.
        """
        from rmgpy.data.rmg import getDB
        from rmgpy.data.kinetics.family import TemplateReaction

        adjlist = [
            '''
        multiplicity 2
        1 H u1 p0 c0''', '''
        multiplicity 2
        1 O u1 p1 c+1 {2,D}
        2 N u0 p2 c-1 {1,D}''', '''
        1 O u0 p1 c+1 {2,D} {3,S}
        2 N u0 p2 c-1 {1,D}
        3 H u0 p0 c0 {1,S}'''
        ]

        family = getDB('kinetics').families['R_Recombination']
        r1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[0])])
        r2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[1])
                               ])  # r2 is not the representative structure of
        # NO, but it is the correct structure participating in this reaction
        p1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[2])])
        r2.generate_resonance_structures(keep_isomorphic=True)

        rxn = TemplateReaction(reactants=[r1, r2], products=[p1])
        rxn.degeneracy = family.calculateDegeneracy(rxn)
        self.assertEqual(rxn.degeneracy, 1)
    def generateStatMech(self):
        """
        Generate molecular degree of freedom data for the species. You must
        have already provided a thermodynamics model using e.g.
        :meth:`generateThermoData()`.
        """
        logging.debug("Generating statmech for species {}".format(self.label))
        from rmgpy.data.rmg import getDB
        try:
            statmechDB = getDB('statmech')        
            if not statmechDB: raise Exception
        except Exception:
            logging.debug('Could not obtain the stat. mech database. Not generating stat. mech...')
            raise

        molecule = self.molecule[0]
        conformer = statmechDB.getStatmechData(molecule, self.getThermoData())

        if self.conformer is None:
            self.conformer = Conformer()

        if self.conformer.E0 is None:
            self.setE0WithThermo()

        self.conformer.modes = conformer.modes
        self.conformer.spinMultiplicity = conformer.spinMultiplicity
        if self.conformer.E0 is None or not self.hasStatMech():
            from rmgpy.exceptions import StatmechError
            logging.error('The conformer in question is {}'.format(self.conformer))
            raise StatmechError('Species {0} does not have stat mech after generateStatMech called'.format(self.label))
def ensure_reaction_direction(isotopomerRxns):
    """
    given a list of reactions with varying isotope labels but identical structure,
    obtained from the `cluster` method, this method remakes the kinetics so that
    they all face the same direction.
    """

    # find isotopeless reaction as standard
    reference = isotopomerRxns[0]
    family = getDB('kinetics').families[reference.family]
    if family.ownReverse:
        for rxn in isotopomerRxns:
            if not compare_isotopomers(rxn, reference, eitherDirection=False):
                # the reaction is in the oposite direction
                logging.info('isotope: identified flipped reaction direction in reaction number {} of reaction {}. Altering the direction.'.format(rxn.index, str(rxn)))
                # obtain reverse attribute with template and degeneracy
                family.addReverseAttribute(rxn)
                if frozenset(rxn.reverse.template) != frozenset(reference.template):
                    logging.warning("Reaction {} did not find proper reverse template, might cause degeneracy error.".format(str(rxn)))
                # reverse reactants and products of original reaction
                rxn.reactants, rxn.products = rxn.products, rxn.reactants
                rxn.pairs = [(p,r) for r,p in rxn.pairs]
                # set degeneracy to isotopeless reaction
                rxn.degeneracy = reference.degeneracy
                # make this reaction have kinetics of isotopeless reaction
                newKinetics = deepcopy(reference.kinetics)
                rxn.kinetics = newKinetics
                rxn.template = reference.template
                # set degeneracy to new reaction
                rxn.degeneracy = rxn.reverse.degeneracy
                #delete reverse attribute
                rxn.reverse = None
Example #7
0
def reactMolecules(moleculeTuples):
    """
    Performs a reaction between
    the resonance isomers.

    The parameter contains a list of tuples with each tuple:
    (Molecule, index of the core species it belongs to)
    """

    families = getDB('kinetics').families
    
    molecules, reactantIndices = zip(*moleculeTuples)
    
    reactionList = []
    for _, family in families.iteritems():
        rxns = family.generateReactions(molecules)
        reactionList.extend(rxns)

    for reactant in molecules:
        reactant.clearLabeledAtoms()

    for rxn in reactionList:
        deflate(rxn, molecules, reactantIndices)

    return reactionList
Example #8
0
def react_all(core_spc_list, numOldCoreSpecies, unimolecularReact, bimolecularReact, trimolecularReact=None, procnum=1):
    """
    Reacts the core species list via uni-, bi-, and trimolecular
    reactions and splits reaction families per task for improved load balancing in parallel runs.
    """
    # Select reactive species that can undergo unimolecular reactions:
    spc_tuples = [(core_spc_list[i],)
                  for i in xrange(numOldCoreSpecies) if (unimolecularReact[i] and core_spc_list[i].reactive)]

    for i in xrange(numOldCoreSpecies):
        for j in xrange(i, numOldCoreSpecies):
            # Find reactions involving the species that are bimolecular.
            # This includes a species reacting with itself (if its own concentration is high enough).
            if bimolecularReact[i, j]:
                if core_spc_list[i].reactive and core_spc_list[j].reactive:
                    spc_tuples.append((core_spc_list[i], core_spc_list[j]))

    if trimolecularReact is not None:
        for i in xrange(numOldCoreSpecies):
            for j in xrange(i, numOldCoreSpecies):
                for k in xrange(j, numOldCoreSpecies):
                    # Find reactions involving the species that are trimolecular.
                    if trimolecularReact[i, j, k]:
                        if core_spc_list[i].reactive and core_spc_list[j].reactive and core_spc_list[k].reactive:
                            spc_tuples.append((core_spc_list[i], core_spc_list[j], core_spc_list[k]))

    if procnum == 1:
        # React all families like normal (provide empty argument for only_families)
        spc_fam_tuples = zip(spc_tuples)
    else:
        # Identify and split families that are prone to generate many reactions into sublists.
        family_list = getDB('kinetics').families.keys()
        major_families = [
            'H_Abstraction', 'R_Recombination', 'Intra_Disproportionation', 'Intra_RH_Add_Endocyclic',
            'Singlet_Carbene_Intra_Disproportionation', 'Intra_ene_reaction', 'Disproportionation',
            '1,4_Linear_birad_scission', 'R_Addition_MultipleBond', '2+2_cycloaddition_Cd', 'Diels_alder_addition',
            'Intra_RH_Add_Exocyclic', 'Intra_Retro_Diels_alder_bicyclic', 'Intra_2+2_cycloaddition_Cd',
            'Birad_recombination', 'Intra_Diels_alder_monocyclic', '1,4_Cyclic_birad_scission', '1,2_Insertion_carbene',
        ]

        split_list = []
        leftovers = []
        for fam in family_list:
            if fam in major_families:
                split_list.append([fam])
            else:
                leftovers.append(fam)
        split_list.append(leftovers)

        # Only employ family splitting for reactants that have a larger number than min_atoms
        min_atoms = 10
        spc_fam_tuples = []
        for i, spc_tuple in enumerate(spc_tuples):
            if any([len(spc.molecule[0].atoms) > min_atoms for spc in spc_tuple]):
                for item in split_list:
                    spc_fam_tuples.append((spc_tuple, item))
            else:
                spc_fam_tuples.append((spc_tuple, ))

    return list(react(spc_fam_tuples, procnum))
Example #9
0
def ensure_reaction_direction(isotopomerRxns):
    """
    given a list of reactions with varying isotope labels but identical structure,
    obtained from the `cluster` method, this method remakes the kinetics so that
    they all face the same direction.
    """

    # find isotopeless reaction as standard
    reference = isotopomerRxns[0]
    family = getDB('kinetics').families[reference.family]
    if family.ownReverse:
        for rxn in isotopomerRxns:
            if not compare_isotopomers(rxn, reference, eitherDirection=False):
                # the reaction is in the oposite direction
                logging.info('isotope: identified flipped reaction direction in reaction number {} of reaction {}. Altering the direction.'.format(rxn.index, str(rxn)))
                # obtain reverse attribute with template and degeneracy
                family.addReverseAttribute(rxn)
                if frozenset(rxn.reverse.template) != frozenset(reference.template):
                    logging.warning("Reaction {} did not find proper reverse template, might cause degeneracy error.".format(str(rxn)))
                # reverse reactants and products of original reaction
                rxn.reactants, rxn.products = rxn.products, rxn.reactants
                rxn.pairs = [(p,r) for r,p in rxn.pairs]
                # set degeneracy to isotopeless reaction
                rxn.degeneracy = reference.degeneracy
                # make this reaction have kinetics of isotopeless reaction
                newKinetics = deepcopy(reference.kinetics)
                rxn.kinetics = newKinetics
                rxn.template = reference.template
                # set degeneracy to new reaction
                rxn.degeneracy = rxn.reverse.degeneracy
                #delete reverse attribute
                rxn.reverse = None
    def test_calculate_degeneracy_for_non_reactive_molecule(self):
        """
        tests that the calculateDegeneracy method gets the degeneracy correct for unreactive molecules
        and that __generateReactions work correctly with the react_non_reactive flag set to `True`.
        """
        from rmgpy.data.rmg import getDB
        from rmgpy.data.kinetics.family import TemplateReaction

        adjlist = ['''
        multiplicity 2
        1 H u1 p0 c0''',
                   '''
        multiplicity 2
        1 O u1 p1 c+1 {2,D}
        2 N u0 p2 c-1 {1,D}''',
                   '''
        1 O u0 p1 c+1 {2,D} {3,S}
        2 N u0 p2 c-1 {1,D}
        3 H u0 p0 c0 {1,S}''']

        family = getDB('kinetics').families['R_Recombination']
        r1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[0])])
        r2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[1])])  # r2 is not the representative structure of
        # NO, but it is the correct structure participating in this reaction
        p1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[2])])
        r2.generate_resonance_structures(keep_isomorphic=True)

        rxn = TemplateReaction(reactants=[r1, r2], products=[p1])
        rxn.degeneracy = family.calculateDegeneracy(rxn)
        self.assertEqual(rxn.degeneracy, 1)
Example #11
0
def database(
             thermoLibraries = None,
             transportLibraries = None,
             reactionLibraries = None,
             frequenciesLibraries = None,
             kineticsFamilies = 'default',
             kineticsDepositories = 'default',
             kineticsEstimator = 'rate rules',
             ):
    if isinstance(thermoLibraries, str):
        thermoLibraries = [thermoLibraries]
    if isinstance(transportLibraries, str):
        transportLibraries = [transportLibraries]
    if isinstance(reactionLibraries, str):
        reactionLibraries = [reactionLibraries]
    if isinstance(frequenciesLibraries, str):
        frequenciesLibraries = [frequenciesLibraries]
    
    databaseDirectory = settings['database.directory']
    thermoLibraries = thermoLibraries or []
    transportLibraries = transportLibraries
    reactionLibraries = reactionLibraries or []
    kineticsEstimator = kineticsEstimator
    
    if kineticsDepositories == 'default':
        kineticsDepositories = ['training']
    elif kineticsDepositories == 'all':
        kineticsDepositories = None
    else:
        if not isinstance(kineticsDepositories,list):
            raise InputError("kineticsDepositories should be either 'default', 'all', or a list of names eg. ['training','PrIMe'].")
        kineticsDepositories = kineticsDepositories

    if kineticsFamilies in ('default', 'all', 'none'):
        kineticsFamilies = kineticsFamilies
    else:
        if not isinstance(kineticsFamilies,list):
            raise InputError("kineticsFamilies should be either 'default', 'all', 'none', or a list of names eg. ['H_Abstraction','R_Recombination'] or ['!Intra_Disproportionation'].")
        kineticsFamilies = kineticsFamilies

    database = getDB() or RMGDatabase()

    database.load(
            path = databaseDirectory,
            thermoLibraries = thermoLibraries,
            transportLibraries = transportLibraries,
            reactionLibraries = reactionLibraries,
            seedMechanisms = [],
            kineticsFamilies = kineticsFamilies,
            kineticsDepositories = kineticsDepositories,
            depository = False, # Don't bother loading the depository information, as we don't use it
        )
    
    for family in database.kinetics.families.values(): #load training
        family.addKineticsRulesFromTrainingSet(thermoDatabase=database.thermo)

    for family in database.kinetics.families.values():
        family.fillKineticsRulesByAveragingUp(verbose=True)
def database(
             thermoLibraries = None,
             transportLibraries = None,
             reactionLibraries = None,
             frequenciesLibraries = None,
             kineticsFamilies = 'default',
             kineticsDepositories = 'default',
             kineticsEstimator = 'rate rules',
             ):
    if isinstance(thermoLibraries, str):
        thermoLibraries = [thermoLibraries]
    if isinstance(transportLibraries, str):
        transportLibraries = [transportLibraries]
    if isinstance(reactionLibraries, str):
        reactionLibraries = [reactionLibraries]
    if isinstance(frequenciesLibraries, str):
        frequenciesLibraries = [frequenciesLibraries]
    
    databaseDirectory = settings['database.directory']
    thermoLibraries = thermoLibraries or []
    transportLibraries = transportLibraries
    reactionLibraries = reactionLibraries or []
    kineticsEstimator = kineticsEstimator
    
    if kineticsDepositories == 'default':
        kineticsDepositories = ['training']
    elif kineticsDepositories == 'all':
        kineticsDepositories = None
    else:
        if not isinstance(kineticsDepositories,list):
            raise InputError("kineticsDepositories should be either 'default', 'all', or a list of names eg. ['training','PrIMe'].")
        kineticsDepositories = kineticsDepositories

    if kineticsFamilies in ('default', 'all', 'none'):
        kineticsFamilies = kineticsFamilies
    else:
        if not isinstance(kineticsFamilies,list):
            raise InputError("kineticsFamilies should be either 'default', 'all', 'none', or a list of names eg. ['H_Abstraction','R_Recombination'] or ['!Intra_Disproportionation'].")
        kineticsFamilies = kineticsFamilies

    database = getDB() or RMGDatabase()

    database.load(
            path = databaseDirectory,
            thermoLibraries = thermoLibraries,
            transportLibraries = transportLibraries,
            reactionLibraries = reactionLibraries,
            seedMechanisms = [],
            kineticsFamilies = kineticsFamilies,
            kineticsDepositories = kineticsDepositories,
            depository = False, # Don't bother loading the depository information, as we don't use it
        )
    
    for family in database.kinetics.families.values(): #load training
        family.addKineticsRulesFromTrainingSet(thermoDatabase=database.thermo)

    for family in database.kinetics.families.values():
        family.fillKineticsRulesByAveragingUp(verbose=True)
Example #13
0
def reaction(label, reactants, products, transitionState=None, kinetics=None, tunneling=''):
    global reactionDict, speciesDict, transitionStateDict
    #label = 'reaction'+transitionState
    if label in reactionDict:
        label = label+transitionState
        if label in reactionDict:
            raise ValueError('Multiple occurrences of reaction with label {0!r}.'.format(label))
    logging.info('Loading reaction {0}...'.format(label))
    reactants = sorted([speciesDict[spec] for spec in reactants])
    products = sorted([speciesDict[spec] for spec in products])
    if transitionState:
        transitionState = transitionStateDict[transitionState]
    if tunneling.lower() == 'wigner':
        transitionState.tunneling = Wigner(frequency=None)
    elif tunneling.lower() == 'eckart':
        transitionState.tunneling = Eckart(frequency=None, E0_reac=None, E0_TS=None, E0_prod=None)
    elif transitionState and (tunneling == '' or tunneling is None):
        transitionState.tunneling = None
    elif transitionState and not isinstance(tunneling, TunnelingModel):
        raise ValueError('Unknown tunneling model {0!r}.'.format(tunneling))
    rxn = Reaction(label=label, reactants=reactants, products=products, transitionState=transitionState, kinetics=kinetics)
    
    if rxn.transitionState is None and rxn.kinetics is None:
        logging.info('estimating rate of reaction {0} using RMG-database')
        if not all([m.molecule != [] for m in rxn.reactants+rxn.products]):
            raise ValueError('chemical structures of reactants and products not available for RMG estimation of reaction {0}'.format(label))
        for spc in rxn.reactants+rxn.products:
            print spc.label
            print spc.molecule
        db = getDB('kinetics')
        rxns = db.generate_reactions_from_libraries(reactants=rxn.reactants,products=rxn.products)
        rxns = [r for r in rxns if r.elementary_high_p]
        
        if rxns != []:
            for r in rxns:
                if isinstance(rxn.kinetics, PDepKineticsModel):
                    boo = rxn.generate_high_p_limit_kinetics()
                if boo:
                    rxn = r
                    break
                
        if rxns == [] or not boo:
            logging.info('No library reactions tagged with elementary_high_p found for reaction {0}, generating reactions from RMG families'.format(label))
            rxn = list(db.generate_reactions_from_families(reactants=rxn.reactants,products=rxn.products))
            model = CoreEdgeReactionModel()
            model.verboseComments = True
            for r in rxn:
                model.applyKineticsToReaction(r)
    
    if isinstance(rxn,Reaction):
        reactionDict[label] = rxn
    else:
        for i in xrange(len(rxn)):
            reactionDict[label+str(i)] = rxn[i]
    
    return rxn
Example #14
0
def loadNecessaryDatabases():
    """
    loads transport and statmech databases
    """
    from rmgpy.data.statmech import StatmechDatabase
    from rmgpy.data.transport import TransportDatabase

    #only load if they are not there already.
    try:
        getDB('transport')
        getDB('statmech')
    except DatabaseError:
        logging.info("Databases not found. Making databases")
        db = RMGDatabase()
        db.statmech = StatmechDatabase()
        db.statmech.load(os.path.join(settings['database.directory'],'statmech'))

        db.transport = TransportDatabase()
        db.transport.load(os.path.join(settings['database.directory'],'transport'))
def loadNecessaryDatabases():
    """
    loads transport and statmech databases
    """
    from rmgpy.data.statmech import StatmechDatabase
    from rmgpy.data.transport import TransportDatabase

    #only load if they are not there already.
    try:
        getDB('transport')
        getDB('statmech')
    except DatabaseError:
        logging.info("Databases not found. Making databases")
        db = RMGDatabase()
        db.statmech = StatmechDatabase()
        db.statmech.load(os.path.join(settings['database.directory'],'statmech'))

        db.transport = TransportDatabase()
        db.transport.load(os.path.join(settings['database.directory'],'transport'))
def reaction(label, reactants, products, transitionState=None, kinetics=None, tunneling=''):
    global reactionDict, speciesDict, transitionStateDict
    if label in reactionDict:
        label = label + transitionState
        if label in reactionDict:
            raise ValueError('Multiple occurrences of reaction with label {0!r}.'.format(label))
    logging.info('Loading reaction {0}...'.format(label))
    reactants = sorted([speciesDict[spec] for spec in reactants])
    products = sorted([speciesDict[spec] for spec in products])
    if transitionState:
        transitionState = transitionStateDict[transitionState]
    if tunneling.lower() == 'wigner':
        transitionState.tunneling = Wigner(frequency=None)
    elif tunneling.lower() == 'eckart':
        transitionState.tunneling = Eckart(frequency=None, E0_reac=None, E0_TS=None, E0_prod=None)
    elif transitionState and (tunneling == '' or tunneling is None):
        transitionState.tunneling = None
    elif transitionState and not isinstance(tunneling, TunnelingModel):
        raise ValueError('Unknown tunneling model {0!r}.'.format(tunneling))
    rxn = Reaction(label=label, reactants=reactants, products=products, transitionState=transitionState, kinetics=kinetics)
    
    if rxn.transitionState is None and rxn.kinetics is None:
        logging.info('estimating rate of reaction {0} using RMG-database')
        if not all([m.molecule != [] for m in rxn.reactants+rxn.products]):
            raise ValueError('chemical structures of reactants and products not available for RMG estimation of reaction {0}'.format(label))
        for spc in rxn.reactants+rxn.products:
            print spc.label
            print spc.molecule
        db = getDB('kinetics')
        rxns = db.generate_reactions_from_libraries(reactants=rxn.reactants,products=rxn.products)
        rxns = [r for r in rxns if r.elementary_high_p]
        
        if rxns != []:
            for r in rxns:
                if isinstance(rxn.kinetics, PDepKineticsModel):
                    boo = rxn.generate_high_p_limit_kinetics()
                if boo:
                    rxn = r
                    break
                
        if rxns == [] or not boo:
            logging.info('No library reactions tagged with elementary_high_p found for reaction {0}, generating reactions from RMG families'.format(label))
            rxn = list(db.generate_reactions_from_families(reactants=rxn.reactants,products=rxn.products))
            model = CoreEdgeReactionModel()
            model.verboseComments = True
            for r in rxn:
                model.applyKineticsToReaction(r)
    
    if isinstance(rxn,Reaction):
        reactionDict[label] = rxn
    else:
        for i in xrange(len(rxn)):
            reactionDict[label+str(i)] = rxn[i]
    
    return rxn
Example #17
0
def react_species(species_tuple, only_families=None):
    """
    Given a tuple of Species objects, generates all possible reactions
    from the loaded reaction families and combines degenerate reactions.
    """

    species_tuple = tuple([spc.copy(deep=True) for spc in species_tuple])

    reactions = getDB('kinetics').generate_reactions_from_families(species_tuple, only_families=only_families)

    return reactions
Example #18
0
    def test_add_atom_labels_for_reaction_3(self):
        """Test that addAtomLabelsForReaction can identify reactions with resonance and isotopes"""
        from rmgpy.data.rmg import getDB
        mr0 = Molecule().fromAdjacencyList(
            '1    C u0 p0 c0 i13 {3,D} {4,S} {5,S}\n2 *1 C u0 p0 c0 {3,D} {6,S} {7,S}\n3    C u0 p0 c0 {1,D} {2,D}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {2,S}\n7 *4 H u0 p0 c0 {2,S}\n'
        )
        mr1a = Molecule().fromAdjacencyList(
            'multiplicity 2\n1    C u0 p0 c0 i13 {2,D} {4,S} {5,S}\n2    C u0 p0 c0 {1,D} {3,D}\n3 *1 C u1 p0 c0 {2,D} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n'
        )
        mr1b = Molecule().fromAdjacencyList(
            'multiplicity 2\n1    C u1 p0 c0 i13 {2,S} {4,S} {5,S}\n2    C u0 p0 c0 {1,S} {3,T}\n3 *1 C u0 p0 c0 {2,T} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n'
        )
        mp1a = Molecule().fromAdjacencyList(
            'multiplicity 2\n1    C u0 p0 c0 {2,D} {4,S} {5,S}\n2    C u0 p0 c0 {1,D} {3,D}\n3 *1 C u1 p0 c0 i13 {2,D} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n'
        )
        mp1b = Molecule().fromAdjacencyList(
            'multiplicity 2\n1    C u1 p0 c0 {2,S} {4,S} {5,S}\n2    C u0 p0 c0 {1,S} {3,T}\n3 *1 C u0 p0 c0 i13 {2,T} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n'
        )
        s1 = Species(molecule=[mr0])
        s2 = Species(molecule=[mr1a, mr1b])
        s3 = Species(molecule=[mp1a, mp1b])
        reactants = [s1, s2]
        products = [s1, s3]
        reaction = TemplateReaction(reactants=reactants,
                                    products=products,
                                    family='H_Abstraction')
        family = getDB('kinetics').families['H_Abstraction']
        print reaction.reactants
        print reaction.products
        family.addAtomLabelsForReaction(reaction, output_with_resonance=False)

        # test that the reaction has labels
        found_labels = []
        for species in reaction.reactants:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(
            len(found_labels), 3,
            'wrong number of labels found {0}'.format(found_labels))
        self.assertIn('*1', found_labels)
        self.assertIn('*2', found_labels)
        self.assertIn('*3', found_labels)

        # test for the products too
        found_labels = []
        for species in reaction.products:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1', found_labels)
        self.assertIn('*2', found_labels)
        self.assertIn('*3', found_labels)
    def generateTransportData(self):
        """
        Generate the transportData parameters for the species.
        """
        from rmgpy.data.rmg import getDB
        try:
            transportDB = getDB('transport')        
            if not transportDB: raise Exception
        except Exception:
            logging.debug('Could not obtain the transport database. Not generating transport...')
            raise

        #count = sum([1 for atom in self.molecule[0].vertices if atom.isNonHydrogen()])
        self.transportData = transportDB.getTransportProperties(self)[0]
Example #20
0
    def generateTransportData(self):
        """
        Generate the transportData parameters for the species.
        """
        from rmgpy.data.rmg import getDB
        try:
            transportDB = getDB('transport')        
            if not transportDB: raise Exception
        except Exception:
            logging.debug('Could not obtain the transport database. Not generating transport...')
            raise

        #count = sum([1 for atom in self.molecule[0].vertices if atom.isNonHydrogen()])
        self.transportData = transportDB.getTransportProperties(self)[0]
Example #21
0
def correctDegeneracyOfReverseReaction(reaction):
    """
    This method corrects the degeneracy of reactions found when the backwards
    template is used. Given the following parameters:

        reaction - list of reactions with their degeneracies already counted

    This method modifies reaction in place and returns nothing
    
    This does not adjust for identical reactants, you need to use `reduceSameReactantDegeneracy`
    to adjust for that.
    """
    family = getDB('kinetics').families[reaction.family]
    if not family.ownReverse:
        reaction.degeneracy = family.calculateDegeneracy(reaction)
Example #22
0
def react_species(species_tuple, only_families=None):
    """
    Given a tuple of Species objects, generates all possible reactions
    from the loaded reaction families and combines degenerate reactions.

    Args:
        species_tuple (tuple): tuple of 1-3 Species objects to react together
        only_families (list, optional): list of reaction families to consider

    Returns:
        list of generated reactions
    """
    reactions = getDB('kinetics').generate_reactions_from_families(
        species_tuple, only_families=only_families)

    return reactions
Example #23
0
def reactSpecies(speciesTuple):
    """
    Given a tuple of Species objects, generates all possible reactions
    from the loaded reaction families and combines degenerate reactions.

    The generated reactions are deflated.
    """
    speciesTuple = tuple([spc.copy(deep=True) for spc in speciesTuple])

    reactions = getDB('kinetics').generate_reactions_from_families(speciesTuple)

    deflate(reactions,
            [spec for spec in speciesTuple],
            [spec.index for spec in speciesTuple])

    return reactions
Example #24
0
def reactSpecies(speciesTuple):
    """
    Given a tuple of Species objects, generates all possible reactions
    from the loaded reaction families and combines degenerate reactions.

    The generated reactions are deflated.
    """
    speciesTuple = tuple([spc.copy(deep=True) for spc in speciesTuple])

    reactions = getDB('kinetics').generate_reactions_from_families(
        speciesTuple)

    deflate(reactions, [spec for spec in speciesTuple],
            [spec.index for spec in speciesTuple])

    return reactions
Example #25
0
def reactSpecies(speciesTuple):
    """
    given one species tuple, will find the reactions and remove degeneracy
    from them.
    """
    # Check if the reactants are the same
    sameReactants = False
    if len(speciesTuple) == 2 and speciesTuple[0].isIsomorphic(
            speciesTuple[1]):
        sameReactants = True

    speciesTuple = tuple([spc.copy(deep=True) for spc in speciesTuple])

    _labelListOfSpecies(speciesTuple)

    combos = getMoleculeTuples(speciesTuple)

    reactions = map(reactMolecules, combos)
    reactions = list(itertools.chain.from_iterable(reactions))
    # remove reverse reaction
    reactions = findDegeneracies(reactions, sameReactants)
    # add reverse attribute to families with ownReverse
    toDelete = []
    for i, rxn in enumerate(reactions):
        family = getDB('kinetics').families[rxn.family]
        if family.ownReverse:
            successful = family.addReverseAttribute(rxn)
            if not successful:
                toDelete.append(i)
    # delete reactions which we could not find a reverse reaction for
    for i in reversed(toDelete):
        del reactions[i]
    # get a molecule list with species indexes
    zippedList = []
    for spec in speciesTuple:
        for mol in spec.molecule:
            zippedList.append((mol, spec.index))

    molecules, reactantIndices = zip(*zippedList)

    deflate(reactions, [spec for spec in speciesTuple],
            [spec.index for spec in speciesTuple])

    return reactions
Example #26
0
def generateThermoData(spc, thermoClass=NASA, solventName=''):
    """
    Generates thermo data, first checking Libraries, then using either QM or Database.
    
    The database generates the thermo data for each structure (resonance isomer),
    picks that with lowest H298 value.
    
    It then calls :meth:`processThermoData`, to convert (via Wilhoit) to NASA
    and set the E0.
    
    Result stored in `spc.thermo` and returned.
    """
    
    try:
        thermodb = getDB('thermo')
        if not thermodb: raise Exception
    except Exception, e:
        logging.debug('Could not obtain the thermo database. Not generating thermo...')
        return None
Example #27
0
def generateThermoData(spc, thermoClass=NASA, solventName=''):
    """
    Generates thermo data, first checking Libraries, then using either QM or Database.
    
    The database generates the thermo data for each structure (resonance isomer),
    picks that with lowest H298 value.
    
    It then calls :meth:`processThermoData`, to convert (via Wilhoit) to NASA
    and set the E0.
    
    Result stored in `spc.thermo` and returned.
    """
    
    try:
        thermodb = getDB('thermo')
        if not thermodb: raise Exception
    except Exception, e:
        logging.debug('Could not obtain the thermo database. Not generating thermo...')
        return None
    def test_add_atom_labels_for_reaction_3(self):
        """Test that addAtomLabelsForReaction can identify reactions with resonance and isotopes"""
        from rmgpy.data.rmg import getDB
        mr0 = Molecule().fromAdjacencyList('1    C u0 p0 c0 i13 {3,D} {4,S} {5,S}\n2 *1 C u0 p0 c0 {3,D} {6,S} {7,S}\n3    C u0 p0 c0 {1,D} {2,D}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {2,S}\n7 *4 H u0 p0 c0 {2,S}\n')
        mr1a = Molecule().fromAdjacencyList('multiplicity 2\n1    C u0 p0 c0 i13 {2,D} {4,S} {5,S}\n2    C u0 p0 c0 {1,D} {3,D}\n3 *1 C u1 p0 c0 {2,D} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n')
        mr1b = Molecule().fromAdjacencyList('multiplicity 2\n1    C u1 p0 c0 i13 {2,S} {4,S} {5,S}\n2    C u0 p0 c0 {1,S} {3,T}\n3 *1 C u0 p0 c0 {2,T} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n')
        mp1a = Molecule().fromAdjacencyList('multiplicity 2\n1    C u0 p0 c0 {2,D} {4,S} {5,S}\n2    C u0 p0 c0 {1,D} {3,D}\n3 *1 C u1 p0 c0 i13 {2,D} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n')
        mp1b = Molecule().fromAdjacencyList('multiplicity 2\n1    C u1 p0 c0 {2,S} {4,S} {5,S}\n2    C u0 p0 c0 {1,S} {3,T}\n3 *1 C u0 p0 c0 i13 {2,T} {6,S}\n4    H u0 p0 c0 {1,S}\n5    H u0 p0 c0 {1,S}\n6    H u0 p0 c0 {3,S}\n')
        s1 = Species(molecule = [mr0])
        s2 = Species(molecule = [mr1a,mr1b])
        s3 = Species(molecule = [mp1a,mp1b])
        reactants = [s1,s2]
        products = [s1,s3]
        reaction = TemplateReaction(reactants =reactants,
                                    products = products,
                                    family = 'H_Abstraction')
        family = getDB('kinetics').families['H_Abstraction']
        print reaction.reactants
        print reaction.products
        family.addAtomLabelsForReaction(reaction, output_with_resonance=False)

        # test that the reaction has labels
        found_labels = []
        for species in reaction.reactants:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3,'wrong number of labels found {0}'.format(found_labels))
        self.assertIn('*1',found_labels)
        self.assertIn('*2',found_labels)
        self.assertIn('*3',found_labels)

        # test for the products too
        found_labels = []
        for species in reaction.products:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1',found_labels)
        self.assertIn('*2',found_labels)
        self.assertIn('*3',found_labels)
Example #29
0
def generateThermoData(spc, thermoClass=NASA, solventName=''):
    """
    Generates thermo data, first checking Libraries, then using either QM or Database.
    
    The database generates the thermo data for each structure (resonance isomer),
    picks that with lowest H298 value.
    
    It then calls :meth:`processThermoData`, to convert (via Wilhoit) to NASA
    and set the E0.
    
    Result stored in `spc.thermo` and returned.
    """

    try:
        thermodb = getDB('thermo')
        if not thermodb: raise Exception
    except Exception:
        logging.debug(
            'Could not obtain the thermo database. Not generating thermo...')
        return None

    thermo0 = thermodb.getThermoData(spc)

    # 1. maybe only submit cyclic core
    # 2. to help radical prediction, HBI should also
    #    look up centrailThermoDB for its saturated version
    #    currently it only looks up libraries or estimates via GAV
    from rmgpy.rmg.input import getInput

    try:
        thermoCentralDatabase = getInput('thermoCentralDatabase')
    except Exception:
        logging.debug('thermoCentralDatabase could not be found.')
        thermoCentralDatabase = None

    if thermoCentralDatabase and thermoCentralDatabase.client \
        and thermoCentralDatabase.satisfyRegistrationRequirements(spc, thermo0, thermodb):

        thermoCentralDatabase.registerInCentralThermoDB(spc)

    return processThermoData(spc, thermo0, thermoClass, solventName)
Example #30
0
    def test_add_atom_labels_for_reaction(self):
        """Test that addAtomLabelsForReaction can identify reactions with resonance
        The molecule [CH]=C=C has resonance in this reaction"""
        from rmgpy.data.rmg import getDB
        reactants = [
            Molecule().fromSMILES('C=C=C'),
            Molecule().fromSMILES('[CH]=C=C'),
        ]
        products = [
            Molecule().fromSMILES('C#C[CH2]'),
            Molecule().fromSMILES('C#CC'),
        ]
        reaction = TemplateReaction(reactants=reactants,
                                    products=products,
                                    family='H_Abstraction')
        reaction.ensure_species(reactant_resonance=True,
                                product_resonance=True)
        family = getDB('kinetics').families['H_Abstraction']
        family.addAtomLabelsForReaction(reaction, output_with_resonance=False)

        # test that the reaction has labels
        found_labels = []
        for species in reaction.reactants:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1', found_labels)
        self.assertIn('*2', found_labels)
        self.assertIn('*3', found_labels)

        # test for the products too
        found_labels = []
        for species in reaction.products:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1', found_labels)
        self.assertIn('*2', found_labels)
        self.assertIn('*3', found_labels)
Example #31
0
    def test_add_atom_labels_for_reaction_2(self):
        """Test that addAtomLabelsForReaction can identify reactions with identical references
        The molecule [CH]=C=C has resonance in this reaction"""
        from rmgpy.data.rmg import getDB
        s1 = Species().fromSMILES('C=C=C')
        s2 = Species().fromSMILES('C=C=[CH]')
        s3 = Species().fromSMILES('C#CC')
        s2.generate_resonance_structures()
        reactants = [s1, s2]
        products = [s2, s3]
        reaction = TemplateReaction(reactants=reactants,
                                    products=products,
                                    family='H_Abstraction')
        family = getDB('kinetics').families['H_Abstraction']
        print reaction.reactants
        print reaction.products
        family.addAtomLabelsForReaction(reaction, output_with_resonance=False)

        # test that the reaction has labels
        found_labels = []
        for species in reaction.reactants:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(
            len(found_labels), 3,
            'wrong number of labels found {0}'.format(found_labels))
        self.assertIn('*1', found_labels)
        self.assertIn('*2', found_labels)
        self.assertIn('*3', found_labels)

        # test for the products too
        found_labels = []
        for species in reaction.products:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1', found_labels)
        self.assertIn('*2', found_labels)
        self.assertIn('*3', found_labels)
def generateThermoData(spc, thermoClass=NASA, solventName=''):
    """
    Generates thermo data, first checking Libraries, then using either QM or Database.
    
    The database generates the thermo data for each structure (resonance isomer),
    picks that with lowest H298 value.
    
    It then calls :meth:`processThermoData`, to convert (via Wilhoit) to NASA
    and set the E0.
    
    Result stored in `spc.thermo` and returned.
    """
    
    try:
        thermodb = getDB('thermo')
        if not thermodb: raise Exception
    except Exception:
        logging.debug('Could not obtain the thermo database. Not generating thermo...')
        return None
    
    thermo0 = thermodb.getThermoData(spc) 

    # 1. maybe only submit cyclic core
    # 2. to help radical prediction, HBI should also
    #    look up centrailThermoDB for its saturated version
    #    currently it only looks up libraries or estimates via GAV 
    from rmgpy.rmg.input import getInput
    
    try:
        thermoCentralDatabase = getInput('thermoCentralDatabase')
    except Exception:
        logging.debug('thermoCentralDatabase could not be found.')
        thermoCentralDatabase = None
    
    if thermoCentralDatabase and thermoCentralDatabase.client \
        and thermoCentralDatabase.satisfyRegistrationRequirements(spc, thermo0, thermodb):
        
        thermoCentralDatabase.registerInCentralThermoDB(spc)
        
    return processThermoData(spc, thermo0, thermoClass, solventName)
    def test_add_atom_labels_for_reaction(self):
        """Test that addAtomLabelsForReaction can identify reactions with resonance
        The molecule [CH]=C=C has resonance in this reaction"""
        from rmgpy.data.rmg import getDB
        reactants = [
            Molecule().fromSMILES('C=C=C'),
            Molecule().fromSMILES('[CH]=C=C'),
        ]
        products = [
            Molecule().fromSMILES('C#C[CH2]'),
            Molecule().fromSMILES('C#CC'),
        ]
        reaction = TemplateReaction(reactants =reactants,
                                    products = products,
                                    family = 'H_Abstraction')
        reaction.ensure_species(reactant_resonance=True, product_resonance=True)
        family = getDB('kinetics').families['H_Abstraction']
        family.addAtomLabelsForReaction(reaction, output_with_resonance=False)

        # test that the reaction has labels
        found_labels = []
        for species in reaction.reactants:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1',found_labels)
        self.assertIn('*2',found_labels)
        self.assertIn('*3',found_labels)

        # test for the products too
        found_labels = []
        for species in reaction.products:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1',found_labels)
        self.assertIn('*2',found_labels)
        self.assertIn('*3',found_labels)
    def test_add_atom_labels_for_reaction_2(self):
        """Test that addAtomLabelsForReaction can identify reactions with identical references
        The molecule [CH]=C=C has resonance in this reaction"""
        from rmgpy.data.rmg import getDB
        s1 = Species().fromSMILES('C=C=C')
        s2 = Species().fromSMILES('C=C=[CH]')
        s3 = Species().fromSMILES('C#CC')
        s2.generate_resonance_structures()
        reactants = [s1,s2]
        products = [s2,s3]
        reaction = TemplateReaction(reactants =reactants,
                                    products = products,
                                    family = 'H_Abstraction')
        family = getDB('kinetics').families['H_Abstraction']
        print reaction.reactants
        print reaction.products
        family.addAtomLabelsForReaction(reaction, output_with_resonance=False)

        # test that the reaction has labels
        found_labels = []
        for species in reaction.reactants:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3,'wrong number of labels found {0}'.format(found_labels))
        self.assertIn('*1',found_labels)
        self.assertIn('*2',found_labels)
        self.assertIn('*3',found_labels)

        # test for the products too
        found_labels = []
        for species in reaction.products:
            for atom in species.molecule[0].atoms:
                if atom.label != '':
                    found_labels.append(atom.label)
        self.assertEqual(len(found_labels), 3)
        self.assertIn('*1',found_labels)
        self.assertIn('*2',found_labels)
        self.assertIn('*3',found_labels)
Example #35
0
def reactSpecies(speciesTuple):
    """
    given one species tuple, will find the reactions and remove degeneracy
    from them.
    """
    speciesTuple = tuple([spc.copy(deep=True) for spc in speciesTuple])

    _labelListOfSpecies(speciesTuple)

    combos = getMoleculeTuples(speciesTuple)

    reactions = map(reactMolecules,combos)
    reactions = list(itertools.chain.from_iterable(reactions))
    # remove reverse reaction
    reactions = findDegeneracies(reactions)
    # add reverse attribute to families with ownReverse
    for rxn in reactions:
        family = getDB('kinetics').families[rxn.family]
        if family.ownReverse:
            family.addReverseAttribute(rxn)
    # fix the degneracy of (not ownReverse) reactions found in the backwards
    # direction
    correctDegeneracyOfReverseReactions(reactions, list(speciesTuple))
    reduceSameReactantDegeneracy(reactions)
    # get a molecule list with species indexes
    zippedList = []
    for spec in speciesTuple:
        for mol in spec.molecule:
            zippedList.append((mol,spec.index))

    molecules, reactantIndices = zip(*zippedList)

    deflate(reactions,
            [spec for spec in speciesTuple],
            [spec.index for spec in speciesTuple])

    return reactions
Example #36
0
def react_molecules_wrapper(reactants):

    return getDB('kinetics').react_molecules(reactants,
                                             only_families=None,
                                             prod_resonance=False)
Example #37
0
def find_degenerate_reactions(rxn_list, same_reactants=None, template=None, kinetics_database=None, kinetics_family=None):
    """
    Given a list of Reaction objects, this method combines degenerate
    reactions and increments the reaction degeneracy value. For multiple
    transition states, this method keeps them as duplicate reactions.

    If a template is specified, then the reaction list will be filtered
    to leave only reactions which match the specified template, then the
    degeneracy will be calculated as usual.

    A KineticsDatabase or KineticsFamily instance can also be provided to
    calculate the degeneracy for reactions generated in the reverse direction.
    If not provided, then it will be retrieved from the global database.

    This algorithm used to exist in family.__generateReactions, but was moved
    here so it could operate across reaction families.

    This method returns an updated list with degenerate reactions removed.

    Args:
        rxn_list (list):                                reactions to be analyzed
        same_reactants (bool, optional):                indicate whether the reactants are identical
        template (list, optional):                      specify a specific template to filter by
        kinetics_database (KineticsDatabase, optional): provide a KineticsDatabase instance for calculating degeneracy
        kinetics_family (KineticsFamily, optional):     provide a KineticsFamily instance for calculating degeneracy

    Returns:
        Reaction list with degenerate reactions combined with proper degeneracy values
    """
    # If a specific reaction template is requested, filter by that template
    if template is not None:
        selected_rxns = []
        template = frozenset(template)
        for rxn in rxn_list:
            if template == frozenset(rxn.template):
                selected_rxns.append(rxn)
        if not selected_rxns:
            # Only log a warning here. If a non-empty output is expected, then the caller should raise an exception
            logging.warning('No reactions matched the specified template, {0}'.format(template))
            return []
    else:
        selected_rxns = rxn_list

    # We want to sort all the reactions into sublists composed of isomorphic reactions
    # with degenerate transition states
    sorted_rxns = []
    for rxn0 in selected_rxns:
        # find resonance structures for rxn0
        rxn0.ensure_species()
        if len(sorted_rxns) == 0:
            # This is the first reaction, so create a new sublist
            sorted_rxns.append([rxn0])
        else:
            # Loop through each sublist, which represents a unique reaction
            for sub_list in sorted_rxns:
                # Try to determine if the current rxn0 is identical or isomorphic to any reactions in the sublist
                isomorphic = False
                identical = False
                sameTemplate = True
                for rxn in sub_list:
                    isomorphic = rxn0.isIsomorphic(rxn, checkIdentical=False, checkTemplateRxnProducts=True)
                    if isomorphic:
                        identical = rxn0.isIsomorphic(rxn, checkIdentical=True, checkTemplateRxnProducts=True)
                        if identical:
                            # An exact copy of rxn0 is already in our list, so we can move on
                            break
                        sameTemplate = frozenset(rxn.template) == frozenset(rxn0.template)
                    else:
                        # This sublist contains a different product
                        break

                # Process the reaction depending on the results of the comparisons
                if identical:
                    # This reaction does not contribute to degeneracy
                    break
                elif isomorphic:
                    if sameTemplate:
                        # We found the right sublist, and there is no identical reaction
                        # We should add rxn0 to the sublist as a degenerate rxn, and move on to the next rxn
                        sub_list.append(rxn0)
                        break
                    else:
                        # We found an isomorphic sublist, but the reaction templates are different
                        # We need to mark this as a duplicate and continue searching the remaining sublists
                        rxn0.duplicate = True
                        sub_list[0].duplicate = True
                        continue
                else:
                    # This is not an isomorphic sublist, so we need to continue searching the remaining sublists
                    # Note: This else statement is not technically necessary but is included for clarity
                    continue
            else:
                # We did not break, which means that there was no isomorphic sublist, so create a new one
                sorted_rxns.append([rxn0])

    rxn_list = []
    for sub_list in sorted_rxns:
        # Collapse our sorted reaction list by taking one reaction from each sublist
        rxn = sub_list[0]
        # The degeneracy of each reaction is the number of reactions that were in the sublist
        rxn.degeneracy = sum([reaction0.degeneracy for reaction0 in sub_list])
        rxn_list.append(rxn)

    for rxn in rxn_list:
        if rxn.is_forward:
            reduce_same_reactant_degeneracy(rxn, same_reactants)
        else:
            # fix the degeneracy of (not ownReverse) reactions found in the backwards direction
            try:
                family = kinetics_family or kinetics_database.families[rxn.family]
            except AttributeError:
                from rmgpy.data.rmg import getDB
                family = getDB('kinetics').families[rxn.family]
            if not family.ownReverse:
                rxn.degeneracy = family.calculateDegeneracy(rxn)

    return rxn_list
def find_degenerate_reactions(rxnList,
                              same_reactants=None,
                              kinetics_database=None,
                              kinetics_family=None):
    """
    given a list of Reaction object with Molecule objects, this method
    removes degenerate reactions and increments the degeneracy of the
    reaction object. For multiple transition states, this method adds
    them as separate duplicate reactions. This method modifies
    rxnList in place and does not return anything.

    This algorithm used to exist in family.__generateReactions, but was moved
    here because it didn't have any family dependence.
    """

    # We want to sort all the reactions into sublists composed of isomorphic reactions
    # with degenerate transition states
    rxnSorted = []
    for rxn0 in rxnList:
        # find resonance structures for rxn0
        ensure_species_in_reaction(rxn0)
        if len(rxnSorted) == 0:
            # This is the first reaction, so create a new sublist
            rxnSorted.append([rxn0])
        else:
            # Loop through each sublist, which represents a unique reaction
            for rxnList1 in rxnSorted:
                # Try to determine if the current rxn0 is identical or isomorphic to any reactions in the sublist
                isomorphic = False
                identical = False
                sameTemplate = False
                for rxn in rxnList1:
                    isomorphic = rxn0.isIsomorphic(
                        rxn,
                        checkIdentical=False,
                        checkTemplateRxnProducts=True)
                    if not isomorphic:
                        identical = False
                    else:
                        identical = rxn0.isIsomorphic(
                            rxn,
                            checkIdentical=True,
                            checkTemplateRxnProducts=True)
                    sameTemplate = frozenset(rxn.template) == frozenset(
                        rxn0.template)
                    if not isomorphic:
                        # a different product was found, go to next list
                        break
                    elif not sameTemplate:
                        # a different transition state was found, mark as duplicate and
                        # go to the next sublist
                        rxn.duplicate = True
                        rxn0.duplicate = True
                        break
                    elif identical:
                        # An exact copy of rxn0 is already in our list, so we can move on to the next rxn
                        break
                    else:  # sameTemplate and isomorphic but not identical
                        # This is the right sublist for rxn0, but continue to see if there is an identical rxn
                        continue
                else:
                    # We did not break, so this is the right sublist, but there is no identical reaction
                    # This means that we should add rxn0 to the sublist as a degenerate rxn
                    rxnList1.append(rxn0)
                if isomorphic and sameTemplate:
                    # We already found the right sublist, so we can move on to the next rxn
                    break
            else:
                # We did not break, which means that there was no isomorphic sublist, so create a new one
                rxnSorted.append([rxn0])

    rxnList = []
    for rxnList1 in rxnSorted:
        # Collapse our sorted reaction list by taking one reaction from each sublist
        rxn = rxnList1[0]
        # The degeneracy of each reaction is the number of reactions that were in the sublist
        rxn.degeneracy = sum([reaction0.degeneracy for reaction0 in rxnList1])
        rxnList.append(rxn)

    for rxn in rxnList:
        if rxn.isForward:
            reduce_same_reactant_degeneracy(rxn, same_reactants)
        else:
            # fix the degeneracy of (not ownReverse) reactions found in the backwards direction
            try:
                family = kinetics_family or kinetics_database.families[
                    rxn.family]
            except AttributeError:
                from rmgpy.data.rmg import getDB
                family = getDB('kinetics').families[rxn.family]
            if not family.ownReverse:
                rxn.degeneracy = family.calculateDegeneracy(rxn)

    return rxnList
    def testaddReverseAttribute(self):
        """
        tests that the addReverseAttribute method gets the reverse degeneracy correct
        """
        from rmgpy.data.rmg import getDB
        from rmgpy.data.kinetics.family import TemplateReaction
        adjlist = ['''
        multiplicity 2
        1 H u0 p0 c0 {7,S}
        2 H u0 p0 c0 {4,S}
        3 C u1 p0 c0 {5,S} {7,S} {8,S}
        4 C u0 p0 c0 {2,S} {6,S} {7,D}
        5 H u0 p0 c0 {3,S}
        6 H u0 p0 c0 {4,S}
        7 C u0 p0 c0 {1,S} {3,S} {4,D}
        8 H u0 p0 c0 {3,S}
        ''',
          '''
        1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S}
        2 C u0 p0 c0 i13 {1,S} {3,D} {7,S}
        3 C u0 p0 c0 {2,D} {8,S} {9,S}
        4 H u0 p0 c0 {1,S}
        5 H u0 p0 c0 {1,S}
        6 H u0 p0 c0 {1,S}
        7 H u0 p0 c0 {2,S}
        8 H u0 p0 c0 {3,S}
        9 H u0 p0 c0 {3,S}
        ''',
                '''
        multiplicity 2
        1 H u0 p0 c0 {7,S}
        2 H u0 p0 c0 {4,S}
        3 C u1 p0 c0 {5,S} {7,S} {8,S}
        4 C u0 p0 c0 {2,S} {6,S} {7,D}
        5 H u0 p0 c0 {3,S}
        6 H u0 p0 c0 {4,S}
        7 C u0 p0 c0 i13 {1,S} {3,S} {4,D}
        8 H u0 p0 c0 {3,S}
        ''',
          '''
        1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S}
        2 C u0 p0 c0 {1,S} {3,D} {7,S}
        3 C u0 p0 c0 {2,D} {8,S} {9,S}
        4 H u0 p0 c0 {1,S}
        5 H u0 p0 c0 {1,S}
        6 H u0 p0 c0 {1,S}
        7 H u0 p0 c0 {2,S}
        8 H u0 p0 c0 {3,S}
        9 H u0 p0 c0 {3,S}
        '''
          ]
        family = getDB('kinetics').families['H_Abstraction']
        r1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[0])])
        r2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[1])])
        p1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[2])])
        p2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[3])])
        r1.generate_resonance_structures(keep_isomorphic=True)
        p1.generate_resonance_structures(keep_isomorphic=True)

        rxn = TemplateReaction(reactants=[r1, r2], products=[p1, p2])
        
        rxn.degeneracy = family.calculateDegeneracy(rxn)
        self.assertEqual(rxn.degeneracy, 6)
        
        family.addReverseAttribute(rxn)
        
        self.assertEqual(rxn.reverse.degeneracy, 6)
Example #40
0
def processThermoData(spc, thermo0, thermoClass=NASA, solventName = ''):
    """
    Converts via Wilhoit into required `thermoClass` and sets `E0`.
    
    Resulting thermo is returned.
    """
    # TODO moving this as a global import leads to circular imports.
    from rmgpy.rmg.model import Species

    thermo = None

    # Always convert to Wilhoit so we can compute E0
    if isinstance(thermo0, Wilhoit):
        wilhoit = thermo0
    elif isinstance(thermo0, ThermoData):
        wilhoit = thermo0.toWilhoit(B=1000.)
    else:
        wilhoit = thermo0.toWilhoit()

    # Add on solvation correction
    solvationdatabase = getDB('solvation')
    if not solventName or solvationdatabase is None:
        logging.debug('Solvent database or solventName not found. Solvent effect was not utilized')
        solventData = None
    else:
        solventData = solvationdatabase.getSolventData(solventName)
    if solventData and not "Liquid thermo library" in thermo0.comment:
        solvationdatabase = getDB('solvation')
        #logging.info("Making solvent correction for {0}".format(Species.solventName))
        soluteData = solvationdatabase.getSoluteData(spc)
        solvation_correction = solvationdatabase.getSolvationCorrection(soluteData, solventData)
        # correction is added to the entropy and enthalpy
        wilhoit.S0.value_si = (wilhoit.S0.value_si + solvation_correction.entropy)
        wilhoit.H0.value_si = (wilhoit.H0.value_si + solvation_correction.enthalpy)
        
    # Compute E0 by extrapolation to 0 K
    if spc.conformer is None:
        spc.conformer = Conformer()
    spc.conformer.E0 = wilhoit.E0
    
    # Convert to desired thermo class
    if thermoClass is Wilhoit:
        thermo = wilhoit
    elif thermoClass is NASA:
        if solventData:
            #if liquid phase simulation keep the nasa polynomial if it comes from a liquid phase thermoLibrary. Otherwise convert wilhoit to NASA
            if "Liquid thermo library" in thermo0.comment and isinstance(thermo0, NASA):
                thermo = thermo0
                if thermo.E0 is None:
                    thermo.E0 = wilhoit.E0
            else:
                thermo = wilhoit.toNASA(Tmin=100.0, Tmax=5000.0, Tint=1000.0)
        else: 
            #gas phase with species matching thermo library keep the NASA from library or convert if group additivity
            if "Thermo library" in thermo0.comment and isinstance(thermo0,NASA):
                thermo=thermo0
                if thermo.E0 is None:
                    thermo.E0 = wilhoit.E0
            else:
                thermo = wilhoit.toNASA(Tmin=100.0, Tmax=5000.0, Tint=1000.0)
    else:
        raise Exception('thermoClass neither NASA nor Wilhoit.  Cannot process thermo data.')
    
    if thermo.__class__ != thermo0.__class__:
        # Compute RMS error of overall transformation
        Tlist = numpy.array([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], numpy.float64)
        err = 0.0
        for T in Tlist:
            err += (thermo.getHeatCapacity(T) - thermo0.getHeatCapacity(T))**2
        err = math.sqrt(err/len(Tlist))/constants.R
        # logging.log(logging.WARNING if err > 0.2 else 0, 'Average RMS error in heat capacity fit to {0} = {1:g}*R'.format(spc, err))

    return thermo
Example #41
0
def species(label, *args, **kwargs):
    """Load a species from an input file"""
    global speciesDict, jobList
    if label in speciesDict:
        raise ValueError(
            'Multiple occurrences of species with label {0!r}.'.format(label))
    logging.info('Loading species {0}...'.format(label))

    spec = Species(label=label)
    speciesDict[label] = spec

    path = None
    if len(args) == 1:
        # The argument is a path to a conformer input file
        path = args[0]
        job = StatMechJob(species=spec, path=path)
        logging.debug('Added species {0} to a stat mech job.'.format(label))
        jobList.append(job)
    elif len(args) > 1:
        raise InputError('species {0} can only have two non-keyword argument '
                         'which should be the species label and the '
                         'path to a quantum file.'.format(spec.label))

    if len(kwargs) > 0:
        # The species parameters are given explicitly
        structure = None
        E0 = None
        modes = []
        spinMultiplicity = 0
        opticalIsomers = 1
        molecularWeight = None
        collisionModel = None
        energyTransferModel = None
        thermo = None
        reactive = True
        for key, value in kwargs.items():
            if key == 'structure':
                structure = value
            elif key == 'E0':
                E0 = value
            elif key == 'modes':
                modes = value
            elif key == 'spinMultiplicity':
                spinMultiplicity = value
            elif key == 'opticalIsomers':
                opticalIsomers = value
            elif key == 'molecularWeight':
                molecularWeight = value
            elif key == 'collisionModel':
                collisionModel = value
            elif key == 'energyTransferModel':
                energyTransferModel = value
            elif key == 'thermo':
                thermo = value
            elif key == 'reactive':
                reactive = value
            else:
                raise TypeError(
                    'species() got an unexpected keyword argument {0!r}.'.
                    format(key))

        if structure:
            spec.molecule = [structure]
        spec.conformer = Conformer(E0=E0,
                                   modes=modes,
                                   spinMultiplicity=spinMultiplicity,
                                   opticalIsomers=opticalIsomers)
        if molecularWeight is not None:
            spec.molecularWeight = molecularWeight
        elif spec.molecularWeight is None and is_pdep(jobList):
            # If a structure was given, simply calling spec.molecularWeight will calculate the molecular weight
            # If one of the jobs is pdep and no molecular weight is given or calculated, raise an error
            raise ValueError(
                "No molecularWeight was entered for species {0}. Since a structure wasn't given"
                " as well, the molecularWeight, which is important for pressure dependent jobs,"
                " cannot be reconstructed.".format(spec.label))
        spec.transportData = collisionModel
        spec.energyTransferModel = energyTransferModel
        spec.thermo = thermo
        spec.reactive = reactive

        if spec.reactive and path is None and spec.thermo is None and spec.conformer.E0 is None:
            if not spec.molecule:
                raise InputError(
                    'Neither thermo, E0, species file path, nor structure specified, cannot estimate'
                    ' thermo properties of species {0}'.format(spec.label))
            try:
                db = getDB('thermo')
                if db is None:
                    raise DatabaseError('Thermo database is None.')
            except DatabaseError:
                logging.warn(
                    "The database isn't loaded, cannot estimate thermo for {0}. "
                    "If it is a bath gas, set reactive = False to avoid generating thermo."
                    .format(spec.label))
            else:
                logging.info(
                    'No E0 or thermo found, estimating thermo and E0 of species {0} using'
                    ' RMG-Database...'.format(spec.label))
                spec.thermo = db.getThermoData(spec)
                if spec.thermo.E0 is None:
                    th = spec.thermo.toWilhoit()
                    spec.conformer.E0 = th.E0
                    spec.thermo.E0 = th.E0
                else:
                    spec.conformer.E0 = spec.thermo.E0

        if spec.reactive and spec.thermo and not spec.hasStatMech(
        ) and structure is not None:
            # generate stat mech info if it wasn't provided before
            spec.generateStatMech()

        if not energyTransferModel:
            # default to RMG's method of generating energyTransferModel
            spec.generateEnergyTransferModel()

    return spec
    def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesList=None, thermoLibrary=None, kineticsLibrary=None):
        
        logging.info('Exploring network...')
        
        rmg = RMG()
        
        rmg.speciesConstraints = {'allowed' : ['input species', 'seed mechanisms', 'reaction libraries'], 'maximumRadicalElectrons' : self.maximumRadicalElectrons, 'explicitlyAllowedMolecules': []}

        rmgpy.rmg.input.rmg = rmg
        
        reaction_model = CoreEdgeReactionModel()
        
        reaction_model.pressureDependence = self.pdepjob
        
        reaction_model.pressureDependence.rmgmode = True
        
        if outputFile:
            reaction_model.pressureDependence.outputFile = os.path.dirname(outputFile)
        
        kineticsDatabase = getDB('kinetics')
        thermoDatabase = getDB('thermo')
        
        thermoDatabase.libraries['thermojobs'] = thermoLibrary
        thermoDatabase.libraryOrder.insert(0,'thermojobs')
        
        kineticsDatabase.libraries['kineticsjobs'] = kineticsLibrary
        kineticsDatabase.libraryOrder.insert(0,('kineticsjobs','Reaction Library'))
        
        jobRxns = [rxn for rxn in reaction_model.core.reactions]
        
        self.jobRxns = jobRxns
        
        if outputFile is not None:
            if not os.path.exists(os.path.join(reaction_model.pressureDependence.outputFile,'pdep')):
                os.mkdir(os.path.join(reaction_model.pressureDependence.outputFile,'pdep'))
            else:
                shutil.rmtree(os.path.join(reaction_model.pressureDependence.outputFile,'pdep'))
                os.mkdir(os.path.join(reaction_model.pressureDependence.outputFile,'pdep'))
            
        # get the molecular formula for the network
        mmol = None
        for spc in self.source:
            if mmol:
                mmol = mmol.merge(spc.molecule[0])
            else:
                mmol = spc.molecule[0].copy(deep=True)

        form = mmol.getFormula()
        
        for spec in self.bathGas.keys()+self.source:
            nspec,isNew = reaction_model.makeNewSpecies(spec,reactive=False)
            flags = np.array([s.molecule[0].getFormula()==form for s in reaction_model.core.species])
            reaction_model.enlarge(nspec,reactEdge=False,unimolecularReact=flags,
                    bimolecularReact=np.zeros((len(reaction_model.core.species),len(reaction_model.core.species))))
        
        reaction_model.addSeedMechanismToCore('kineticsjobs')
        
        for lib in kineticsDatabase.libraryOrder:
            if lib[0] != 'kineticsjobs':
                reaction_model.addReactionLibraryToEdge(lib[0])
        
        for spc in reaction_model.core.species:
            for i,item in enumerate(self.source):
                if spc.isIsomorphic(item):
                    self.source[i] = spc
        
        # react initial species
        if len(self.source) == 1:
            flags = np.array([s.molecule[0].getFormula()==form for s in reaction_model.core.species])
            biflags = np.zeros((len(reaction_model.core.species),len(reaction_model.core.species)))
        elif len(self.source) == 2:
            flags = np.array([False for s in reaction_model.core.species])
            biflags = np.array([[False for i in xrange(len(reaction_model.core.species))] for j in xrange(len(reaction_model.core.species))])
            biflags[reaction_model.core.species.index(self.source[0]),reaction_model.core.species.index(self.source[1])] = True
        else:
            raise ValueError("Reactant channels with greater than 2 reactants not supported")

        reaction_model.enlarge(reactEdge=True,unimolecularReact=flags,
                      bimolecularReact=biflags)

        # find the networks we're interested in
        networks = []
        for nwk in reaction_model.networkList:
            if set(nwk.source) == set(self.source):
                self.source = nwk.source
                networks.append(nwk)

        if len(networks) == 0:
            raise ValueError('Did not generate a network with the requested source. This usually means no unimolecular'
                             'reactions were generated for the source. Note that library reactions that are not'
                             ' properly flagged as elementary_high_p can replace RMG generated reactions that would'
                             ' otherwise be part of networks.')
        for network in networks:
            network.bathGas = self.bathGas

        self.networks = networks

        # determine T and P combinations
        
        if self.pdepjob.Tlist:
            Tlist = self.pdepjob.Tlist.value_si
        else:
            Tlist = np.linspace(self.pdepjob.Tmin.value_si,self.pdepjob.Tmax.value_si,self.pdepjob.Tcount)
            
        if self.pdepjob.Plist:
            Plist = self.pdepjob.Plist.value_si
        else:
            Plist = np.linspace(self.pdepjob.Pmin.value_si,self.pdepjob.Pmax.value_si,self.pdepjob.Pcount)
            
        # generate the network
        
        forbiddenStructures = getDB('forbidden')
        incomplete = True
        
        while incomplete:
            incomplete = False
            for T in Tlist:
                for P in Plist:
                    for network in self.networks:
                        kchar = 0.0 #compute the characteristic rate coefficient by summing all rate coefficients from the reactant channel
                        for rxn in network.netReactions:#reaction_model.core.reactions+reaction_model.edge.reactions:
                            if set(rxn.reactants) == set(self.source) and rxn.products[0].molecule[0].getFormula() == form:
                                kchar += rxn.kinetics.getRateCoefficient(T=T,P=P)
                            elif set(rxn.products) == set(self.source) and rxn.reactants[0].molecule[0].getFormula() == form:
                                kchar += rxn.generateReverseRateCoefficient(network_kinetics=True).getRateCoefficient(T=T,P=P)

                        if network.getLeakCoefficient(T=T,P=P) > self.explore_tol*kchar:
                            incomplete = True
                            spc = network.getMaximumLeakSpecies(T=T,P=P)
                            if forbiddenStructures.isMoleculeForbidden(spc.molecule[0]):
                                reaction_model.removeSpeciesFromEdge(reaction_model.reactionSystems,spc)
                                reaction_model.removeEmptyPdepNetworks()
                                logging.error(spc.label)
                            else:
                                logging.info('adding new isomer {0} to network'.format(spc))
                                flags = np.array([s.molecule[0].getFormula()==form for s in reaction_model.core.species])
                                reaction_model.enlarge((network,spc),reactEdge=False,unimolecularReact=flags,
                                                  bimolecularReact=np.zeros((len(reaction_model.core.species),len(reaction_model.core.species))))

                                flags = np.array([s.molecule[0].getFormula()==form for s in reaction_model.core.species])
                                reaction_model.enlarge(reactEdge=True,unimolecularReact=flags,
                                                  bimolecularReact=np.zeros((len(reaction_model.core.species),len(reaction_model.core.species))))
        for network in self.networks:
            rmRxns = []
            for rxn in network.pathReactions:  # remove reactions with forbidden species
                for r in rxn.reactants+rxn.products:
                    if forbiddenStructures.isMoleculeForbidden(r.molecule[0]):
                        rmRxns.append(rxn)

            for rxn in rmRxns:
                logging.info('Removing forbidden reaction: {0}'.format(rxn))
                network.pathReactions.remove(rxn)

            # clean up output files
            if outputFile is not None:
                path = os.path.join(reaction_model.pressureDependence.outputFile,'pdep')
                for name in os.listdir(path):
                    if name.endswith('.py') and '_' in name:
                        if name.split('_')[-1].split('.')[0] != str(len(network.isomers)):
                            os.remove(os.path.join(path,name))
                        else:
                            os.rename(os.path.join(path,name),os.path.join(path,'network_full{}.py'.format(self.networks.index(network))))

        warns = []

        for rxn in jobRxns:
            if rxn not in network.pathReactions:
                warns.append('Reaction {0} in the input file was not explored during network expansion and was not included in the full network.  This is likely because your explore_tol value is too high.'.format(rxn))

        # reduction process
        for network in self.networks:
            if self.energy_tol != np.inf or self.flux_tol != 0.0:

                rxnSet = None

                for T in Tlist:
                    if self.energy_tol != np.inf:
                        rxns = network.get_energy_filtered_reactions(T,self.energy_tol)
                        if rxnSet is not None:
                            rxnSet &= set(rxns)
                        else:
                            rxnSet = set(rxns)

                    for P in Plist:
                        if self.flux_tol != 0.0:
                            rxns = network.get_rate_filtered_reactions(T,P,self.flux_tol)
                            if rxnSet is not None:
                                rxnSet &= set(rxns)
                            else:
                                rxnSet = set(rxns)

                logging.info('removing reactions during reduction:')
                for rxn in rxnSet:
                    logging.info(rxn)

                network.remove_reactions(reaction_model,list(rxnSet))

                for rxn in jobRxns:
                    if rxn not in network.pathReactions:
                        warns.append('Reaction {0} in the input file was not included in the reduced model.'.format(rxn))

        self.networks = networks
        for p,network in enumerate(self.networks):
            self.pdepjob.network = network

            if len(self.networks) > 1:
                s1,s2 = outputFile.split(".")
                ind = str(self.networks.index(network))
                stot = s1+"{}.".format(ind)+s2
            else:
                stot = outputFile

            self.pdepjob.execute(stot, plot, format='pdf', print_summary=True)
            if os.path.isfile('network.pdf'):
                os.rename('network.pdf','network'+str(p)+'.pdf')

            if warns != []:
                logging.info('\nOUTPUT WARNINGS:\n')
                for w in warns:
                    logging.warning(w)
Example #43
0
def find_degenerate_reactions(rxnList, same_reactants=None, kinetics_database=None, kinetics_family=None):
    """
    given a list of Reaction object with Molecule objects, this method
    removes degenerate reactions and increments the degeneracy of the
    reaction object. For multiple transition states, this method adds
    them as separate duplicate reactions. This method modifies
    rxnList in place and does not return anything.

    This algorithm used to exist in family.__generateReactions, but was moved
    here because it didn't have any family dependence.
    """

    # We want to sort all the reactions into sublists composed of isomorphic reactions
    # with degenerate transition states
    rxnSorted = []
    for rxn0 in rxnList:
        # find resonance structures for rxn0
        rxn0.ensure_species()
        if len(rxnSorted) == 0:
            # This is the first reaction, so create a new sublist
            rxnSorted.append([rxn0])
        else:
            # Loop through each sublist, which represents a unique reaction
            for rxnList1 in rxnSorted:
                # Try to determine if the current rxn0 is identical or isomorphic to any reactions in the sublist
                isomorphic = False
                identical = False
                sameTemplate = False
                for rxn in rxnList1:
                    isomorphic = rxn0.isIsomorphic(rxn, checkIdentical=False, checkTemplateRxnProducts=True)
                    if not isomorphic:
                        identical = False
                    else:
                        identical = rxn0.isIsomorphic(rxn, checkIdentical=True, checkTemplateRxnProducts=True)
                    sameTemplate = frozenset(rxn.template) == frozenset(rxn0.template)
                    if not isomorphic:
                        # a different product was found, go to next list
                        break
                    elif not sameTemplate:
                        # a different transition state was found, mark as duplicate and
                        # go to the next sublist
                        rxn.duplicate = True
                        rxn0.duplicate = True
                        break
                    elif identical:
                        # An exact copy of rxn0 is already in our list, so we can move on to the next rxn
                        break
                    else: # sameTemplate and isomorphic but not identical
                        # This is the right sublist for rxn0, but continue to see if there is an identical rxn
                        continue
                else:
                    # We did not break, so this is the right sublist, but there is no identical reaction
                    # This means that we should add rxn0 to the sublist as a degenerate rxn
                    rxnList1.append(rxn0)
                if isomorphic and sameTemplate:
                    # We already found the right sublist, so we can move on to the next rxn
                    break
            else:
                # We did not break, which means that there was no isomorphic sublist, so create a new one
                rxnSorted.append([rxn0])

    rxnList = []
    for rxnList1 in rxnSorted:
        # Collapse our sorted reaction list by taking one reaction from each sublist
        rxn = rxnList1[0]
        # The degeneracy of each reaction is the number of reactions that were in the sublist
        rxn.degeneracy = sum([reaction0.degeneracy for reaction0 in rxnList1])
        rxnList.append(rxn)

    for rxn in rxnList:
        if rxn.isForward:
            reduce_same_reactant_degeneracy(rxn, same_reactants)
        else:
            # fix the degeneracy of (not ownReverse) reactions found in the backwards direction
            try:
                family = kinetics_family or kinetics_database.families[rxn.family]
            except AttributeError:
                from rmgpy.data.rmg import getDB
                family = getDB('kinetics').families[rxn.family]
            if not family.ownReverse:
                rxn.degeneracy = family.calculateDegeneracy(rxn)

    return rxnList
Example #44
0
def react_all(core_spc_list,
              numOldCoreSpecies,
              unimolecularReact,
              bimolecularReact,
              trimolecularReact=None,
              procnum=1):
    """
    Reacts the core species list via uni-, bi-, and trimolecular reactions.

    For parallel processing, reaction families are split per task for improved
    load balancing. This is currently hard-coded using reaction family labels.

    Args:
        core_spc_list (list): list of all core species
        numOldCoreSpecies (int): current number of core species in the model
        unimolecularReact (np.ndarray): reaction filter flags indicating which species to react unimolecularly
        bimolecularReact (np.ndarray): reaction filter flags indicating which species to react bimolecularly
        trimolecularReact (np.ndarray, optional): reaction filter flags indicating which species to react trimolecularly
        procnum (int, optional): number of processors used for reaction generation

    Returns:
        a list of lists of reactions generated from each species tuple
        a list of species tuples corresponding to each list of reactions
    """
    # Select reactive species that can undergo unimolecular reactions:
    spc_tuples = [(core_spc_list[i], ) for i in xrange(numOldCoreSpecies)
                  if (unimolecularReact[i] and core_spc_list[i].reactive)]

    for i in xrange(numOldCoreSpecies):
        for j in xrange(i, numOldCoreSpecies):
            # Find reactions involving the species that are bimolecular.
            # This includes a species reacting with itself (if its own concentration is high enough).
            if bimolecularReact[i, j]:
                if core_spc_list[i].reactive and core_spc_list[j].reactive:
                    spc_tuples.append((core_spc_list[i], core_spc_list[j]))

    if trimolecularReact is not None:
        for i in xrange(numOldCoreSpecies):
            for j in xrange(i, numOldCoreSpecies):
                for k in xrange(j, numOldCoreSpecies):
                    # Find reactions involving the species that are trimolecular.
                    if trimolecularReact[i, j, k]:
                        if core_spc_list[i].reactive and core_spc_list[
                                j].reactive and core_spc_list[k].reactive:
                            spc_tuples.append(
                                (core_spc_list[i], core_spc_list[j],
                                 core_spc_list[k]))

    if procnum == 1:
        # React all families like normal (provide empty argument for only_families)
        spc_fam_tuples = zip(spc_tuples)
    else:
        # Identify and split families that are prone to generate many reactions into sublists.
        family_list = getDB('kinetics').families.keys()
        major_families = [
            'H_Abstraction',
            'R_Recombination',
            'Intra_Disproportionation',
            'Intra_RH_Add_Endocyclic',
            'Singlet_Carbene_Intra_Disproportionation',
            'Intra_ene_reaction',
            'Disproportionation',
            '1,4_Linear_birad_scission',
            'R_Addition_MultipleBond',
            '2+2_cycloaddition_Cd',
            'Diels_alder_addition',
            'Intra_RH_Add_Exocyclic',
            'Intra_Retro_Diels_alder_bicyclic',
            'Intra_2+2_cycloaddition_Cd',
            'Birad_recombination',
            'Intra_Diels_alder_monocyclic',
            '1,4_Cyclic_birad_scission',
            '1,2_Insertion_carbene',
        ]

        split_list = []
        leftovers = []
        for fam in family_list:
            if fam in major_families:
                split_list.append([fam])
            else:
                leftovers.append(fam)
        split_list.append(leftovers)

        # Only employ family splitting for reactants that have a larger number than min_atoms
        min_atoms = 10
        spc_fam_tuples = []
        for i, spc_tuple in enumerate(spc_tuples):
            if any(
                [len(spc.molecule[0].atoms) > min_atoms for spc in spc_tuple]):
                for item in split_list:
                    spc_fam_tuples.append((spc_tuple, item))
            else:
                spc_fam_tuples.append((spc_tuple, ))

    return react(spc_fam_tuples,
                 procnum), [fam_tuple[0] for fam_tuple in spc_fam_tuples]
Example #45
0
def generate_isotope_reactions(isotopeless_reactions, isotopes):
    """
    Find the list of isotope reactions based on the reactions in the isotopeless
    reaction.

    uses the reactSpecies method to find reactions with proper degeneracies and
    then filters out those that don't match products. the proper reactions are
    given kinetics of the previous reaction modified for the degeneracy difference.
    """
    # make sure all isotopeless reactions have templates and are TemplateReaction objects
    for rxn in isotopeless_reactions:
        if not isinstance(rxn, TemplateReaction):
            raise TypeError(
                'reactions sent to generate_isotope_reactions must be a TemplateReaction object'
            )
        if rxn.template is None:
            raise AttributeError(
                'isotope reaction {0} does not have a template attribute. The object is:\n\n{1}'
                .format(str(rxn), repr(rxn)))

    found_reactions = []
    rxn_index = 0
    while rxn_index < len(isotopeless_reactions):
        rxn = isotopeless_reactions[rxn_index]
        # find all reactions involving same reactants
        rxns_w_same_reactants = [rxn]
        rxn_index2 = rxn_index + 1
        while rxn_index2 < len(isotopeless_reactions):
            if isomorphic_species_lists(
                    isotopeless_reactions[rxn_index].reactants,
                    isotopeless_reactions[rxn_index2].reactants,
            ):
                rxns_w_same_reactants.append(isotopeless_reactions[rxn_index2])
                del isotopeless_reactions[rxn_index2]
            else:
                rxn_index2 += 1
        ##### find all pairs of reacting isotoper species #####
        # find the lists of reactants that have identical isotopomers
        reactants = []
        for reactant in rxn.reactants:
            for iso_index, isotopomers in enumerate(isotopes):
                if compare_isotopomers(reactant, isotopomers[0]):
                    reactants.append(iso_index)
                    break
        # find pairs of all reactants to react together
        reactant_pairs = []
        if len(rxn.reactants) == 1:
            reactant_pairs = [[spec] for spec in isotopes[reactants[0]]]
        elif len(rxn.reactants) == 2:
            for spec1 in isotopes[reactants[0]]:
                for spec2 in isotopes[reactants[1]]:
                    reactant_pairs.append([spec1, spec2])
        else:
            raise ValueError('Cannot process reactions with over 2 reactants')

        # remove identical pairs
        rxn_index3 = 0
        while rxn_index3 < len(reactant_pairs):
            rxn_index4 = rxn_index3 + 1
            while rxn_index4 < len(reactant_pairs):
                if isomorphic_species_lists(reactant_pairs[rxn_index3],
                                            reactant_pairs[rxn_index4]):
                    del reactant_pairs[rxn_index4]
                else:
                    rxn_index4 += 1
            rxn_index3 += 1

        # make reaction objects
        for pair in reactant_pairs:
            # copy species so they don't get modified
            speciesTuple = tuple([spc.copy(deep=True) for spc in pair])
            unfiltered_rxns = getDB(
                'kinetics').generate_reactions_from_families(
                    speciesTuple, only_families=[rxn.family])
            # remove reactions whose products don't match the original reactions
            rxn_index5 = 0
            while rxn_index5 < len(unfiltered_rxns):
                for isotopeless_reaction in rxns_w_same_reactants:
                    isotopeless_kinetics = isotopeless_reaction.kinetics
                    isotopeless_degeneracy = isotopeless_reaction.degeneracy
                    if compare_isotopomers(isotopeless_reaction, unfiltered_rxns[rxn_index5],eitherDirection = False)\
                        and isotopeless_reaction.family == unfiltered_rxns[rxn_index5].family\
                        and frozenset(isotopeless_reaction.template) == \
                                     frozenset(unfiltered_rxns[rxn_index5].template):
                        # apply kinetics to new reaction & modify for degeneracy
                        unfiltered_rxns[rxn_index5].kinetics = deepcopy(
                            isotopeless_kinetics)
                        unfiltered_rxns[rxn_index5].kinetics.changeRate(
                            unfiltered_rxns[rxn_index5].degeneracy /
                            isotopeless_degeneracy)
                        rxn_index5 += 1
                        break
                else:  # did not find same prodcuts
                    del unfiltered_rxns[rxn_index5]
            found_reactions.extend(unfiltered_rxns)
        rxn_index += 1
    return found_reactions
Example #46
0
def processThermoData(spc, thermo0, thermoClass=NASA):
    """
    Converts via Wilhoit into required `thermoClass` and sets `E0`.
    
    Resulting thermo is returned.
    """
    # TODO moving this as a global import leads to circular imports.
    from rmgpy.rmg.model import Species

    thermo = None

    # Always convert to Wilhoit so we can compute E0
    if isinstance(thermo0, Wilhoit):
        wilhoit = thermo0
    elif isinstance(thermo0, ThermoData):
        wilhoit = thermo0.toWilhoit(B=1000.)
    else:
        wilhoit = thermo0.toWilhoit()

    # Add on solvation correction
    if Species.solventData and not "Liquid thermo library" in thermo0.comment:
        solvationdatabase = getDB('solvation')
        #logging.info("Making solvent correction for {0}".format(Species.solventName))
        soluteData = solvationdatabase.getSoluteData(spc)
        solvation_correction = solvationdatabase.getSolvationCorrection(
            soluteData, Species.solventData)
        # correction is added to the entropy and enthalpy
        wilhoit.S0.value_si = (wilhoit.S0.value_si +
                               solvation_correction.entropy)
        wilhoit.H0.value_si = (wilhoit.H0.value_si +
                               solvation_correction.enthalpy)

    # Compute E0 by extrapolation to 0 K
    if spc.conformer is None:
        spc.conformer = Conformer()
    spc.conformer.E0 = wilhoit.E0

    # Convert to desired thermo class
    if thermoClass is Wilhoit:
        thermo = wilhoit
    elif thermoClass is NASA:
        if Species.solventData:
            #if liquid phase simulation keep the nasa polynomial if it comes from a liquid phase thermoLibrary. Otherwise convert wilhoit to NASA
            if "Liquid thermo library" in thermo0.comment and isinstance(
                    thermo0, NASA):
                thermo = thermo0
                if thermo.E0 is None:
                    thermo.E0 = wilhoit.E0
            else:
                thermo = wilhoit.toNASA(Tmin=100.0, Tmax=5000.0, Tint=1000.0)
        else:
            #gas phase with species matching thermo library keep the NASA from library or convert if group additivity
            if "Thermo library" in thermo0.comment and isinstance(
                    thermo0, NASA):
                thermo = thermo0
                if thermo.E0 is None:
                    thermo.E0 = wilhoit.E0
            else:
                thermo = wilhoit.toNASA(Tmin=100.0, Tmax=5000.0, Tint=1000.0)
    else:
        raise Exception(
            'thermoClass neither NASA nor Wilhoit.  Cannot process thermo data.'
        )

    if thermo.__class__ != thermo0.__class__:
        # Compute RMS error of overall transformation
        Tlist = numpy.array(
            [300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], numpy.float64)
        err = 0.0
        for T in Tlist:
            err += (thermo.getHeatCapacity(T) - thermo0.getHeatCapacity(T))**2
        err = math.sqrt(err / len(Tlist)) / constants.R
        # logging.log(logging.WARNING if err > 0.2 else 0, 'Average RMS error in heat capacity fit to {0} = {1:g}*R'.format(spc, err))

    return thermo
Example #47
0
    def testaddReverseAttribute(self):
        """
        tests that the addReverseAttribute method gets the reverse degeneracy correct
        """
        from rmgpy.data.rmg import getDB
        from rmgpy.data.kinetics.family import TemplateReaction
        adjlist = [
            '''
        multiplicity 2
        1 H u0 p0 c0 {7,S}
        2 H u0 p0 c0 {4,S}
        3 C u1 p0 c0 {5,S} {7,S} {8,S}
        4 C u0 p0 c0 {2,S} {6,S} {7,D}
        5 H u0 p0 c0 {3,S}
        6 H u0 p0 c0 {4,S}
        7 C u0 p0 c0 {1,S} {3,S} {4,D}
        8 H u0 p0 c0 {3,S}
        ''', '''
        1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S}
        2 C u0 p0 c0 i13 {1,S} {3,D} {7,S}
        3 C u0 p0 c0 {2,D} {8,S} {9,S}
        4 H u0 p0 c0 {1,S}
        5 H u0 p0 c0 {1,S}
        6 H u0 p0 c0 {1,S}
        7 H u0 p0 c0 {2,S}
        8 H u0 p0 c0 {3,S}
        9 H u0 p0 c0 {3,S}
        ''', '''
        multiplicity 2
        1 H u0 p0 c0 {7,S}
        2 H u0 p0 c0 {4,S}
        3 C u1 p0 c0 {5,S} {7,S} {8,S}
        4 C u0 p0 c0 {2,S} {6,S} {7,D}
        5 H u0 p0 c0 {3,S}
        6 H u0 p0 c0 {4,S}
        7 C u0 p0 c0 i13 {1,S} {3,S} {4,D}
        8 H u0 p0 c0 {3,S}
        ''', '''
        1 C u0 p0 c0 {2,S} {4,S} {5,S} {6,S}
        2 C u0 p0 c0 {1,S} {3,D} {7,S}
        3 C u0 p0 c0 {2,D} {8,S} {9,S}
        4 H u0 p0 c0 {1,S}
        5 H u0 p0 c0 {1,S}
        6 H u0 p0 c0 {1,S}
        7 H u0 p0 c0 {2,S}
        8 H u0 p0 c0 {3,S}
        9 H u0 p0 c0 {3,S}
        '''
        ]
        family = getDB('kinetics').families['H_Abstraction']
        r1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[0])])
        r2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[1])])
        p1 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[2])])
        p2 = Species(molecule=[Molecule().fromAdjacencyList(adjlist[3])])
        r1.generateResonanceIsomers(keepIsomorphic=True)
        p1.generateResonanceIsomers(keepIsomorphic=True)

        rxn = TemplateReaction(reactants=[r1, r2], products=[p1, p2])

        rxn.degeneracy = family.calculateDegeneracy(rxn)
        self.assertEqual(rxn.degeneracy, 6)

        family.addReverseAttribute(rxn)

        self.assertEqual(rxn.reverse.degeneracy, 6)
def generate_isotope_reactions(isotopeless_reactions, isotopes):
    """
    Find the list of isotope reactions based on the reactions in the isotopeless
    reaction.

    uses the reactSpecies method to find reactions with proper degeneracies and
    then filters out those that don't match products. the proper reactions are
    given kinetics of the previous reaction modified for the degeneracy difference.
    """
    # make sure all isotopeless reactions have templates and are TemplateReaction objects
    for rxn in isotopeless_reactions:
        if not isinstance(rxn,TemplateReaction):
            raise TypeError('reactions sent to generate_isotope_reactions must be a TemplateReaction object')
        if rxn.template is None:
            raise AttributeError('isotope reaction {0} does not have a template attribute. The object is:\n\n{1}'.format(str(rxn),repr(rxn)))

    found_reactions = []
    rxn_index = 0
    while rxn_index < len(isotopeless_reactions):
        rxn = isotopeless_reactions[rxn_index]
        # find all reactions involving same reactants
        rxns_w_same_reactants = [rxn]
        rxn_index2 = rxn_index + 1
        while rxn_index2 < len(isotopeless_reactions):
            if isomorphic_species_lists(isotopeless_reactions[rxn_index].reactants,
                                      isotopeless_reactions[rxn_index2].reactants,
                                     ):
                rxns_w_same_reactants.append(isotopeless_reactions[rxn_index2])
                del isotopeless_reactions[rxn_index2]
            else:
                rxn_index2 += 1
        ##### find all pairs of reacting isotoper species #####
        # find the lists of reactants that have identical isotopomers
        reactants = []
        for reactant in rxn.reactants:
            for iso_index, isotopomers in enumerate(isotopes):
                if compare_isotopomers(reactant,isotopomers[0]):
                    reactants.append(iso_index)
                    break
        # find pairs of all reactants to react together
        reactant_pairs = []
        if len(rxn.reactants) == 1:
            reactant_pairs = [[spec] for spec in isotopes[reactants[0]]]
        elif len(rxn.reactants) == 2:
            for spec1 in isotopes[reactants[0]]:
                for spec2 in isotopes[reactants[1]]:
                    reactant_pairs.append([spec1, spec2])
        else:
            raise ValueError('Cannot process reactions with over 2 reactants')

        # remove identical pairs
        rxn_index3 = 0
        while rxn_index3 < len(reactant_pairs):
            rxn_index4 = rxn_index3 + 1
            while rxn_index4 < len(reactant_pairs):
                if isomorphic_species_lists(reactant_pairs[rxn_index3],
                                          reactant_pairs[rxn_index4]):
                    del reactant_pairs[rxn_index4]
                else:
                    rxn_index4 += 1
            rxn_index3 += 1

        # make reaction objects
        for pair in reactant_pairs:
            # copy species so they don't get modified
            speciesTuple = tuple([spc.copy(deep=True) for spc in pair])
            unfiltered_rxns = getDB('kinetics').generate_reactions_from_families(speciesTuple,only_families=[rxn.family])
            # remove reactions whose products don't match the original reactions
            rxn_index5 = 0
            while rxn_index5 < len(unfiltered_rxns):
                for isotopeless_reaction in rxns_w_same_reactants:
                    isotopeless_kinetics = isotopeless_reaction.kinetics
                    isotopeless_degeneracy = isotopeless_reaction.degeneracy
                    if compare_isotopomers(isotopeless_reaction, unfiltered_rxns[rxn_index5],eitherDirection = False)\
                        and isotopeless_reaction.family == unfiltered_rxns[rxn_index5].family\
                        and frozenset(isotopeless_reaction.template) == \
                                     frozenset(unfiltered_rxns[rxn_index5].template):
                        # apply kinetics to new reaction & modify for degeneracy
                        unfiltered_rxns[rxn_index5].kinetics = deepcopy(isotopeless_kinetics)
                        unfiltered_rxns[rxn_index5].kinetics.changeRate(unfiltered_rxns[rxn_index5].degeneracy / isotopeless_degeneracy)
                        rxn_index5 += 1
                        break
                else: # did not find same prodcuts
                    del unfiltered_rxns[rxn_index5]
            found_reactions.extend(unfiltered_rxns)
        rxn_index += 1
    return found_reactions
def find_degenerate_reactions(rxn_list, same_reactants=None, template=None, kinetics_database=None, kinetics_family=None):
    """
    Given a list of Reaction objects, this method combines degenerate
    reactions and increments the reaction degeneracy value. For multiple
    transition states, this method keeps them as duplicate reactions.

    If a template is specified, then the reaction list will be filtered
    to leave only reactions which match the specified template, then the
    degeneracy will be calculated as usual.

    A KineticsDatabase or KineticsFamily instance can also be provided to
    calculate the degeneracy for reactions generated in the reverse direction.
    If not provided, then it will be retrieved from the global database.

    This algorithm used to exist in family.__generateReactions, but was moved
    here so it could operate across reaction families.

    This method returns an updated list with degenerate reactions removed.

    Args:
        rxn_list (list):                                reactions to be analyzed
        same_reactants (bool, optional):                indicate whether the reactants are identical
        template (list, optional):                      specify a specific template to filter by
        kinetics_database (KineticsDatabase, optional): provide a KineticsDatabase instance for calculating degeneracy
        kinetics_family (KineticsFamily, optional):     provide a KineticsFamily instance for calculating degeneracy

    Returns:
        Reaction list with degenerate reactions combined with proper degeneracy values
    """
    # If a specific reaction template is requested, filter by that template
    if template is not None:
        selected_rxns = []
        template = frozenset(template)
        for rxn in rxn_list:
            if template == frozenset(rxn.template):
                selected_rxns.append(rxn)
        if not selected_rxns:
            # Only log a warning here. If a non-empty output is expected, then the caller should raise an exception
            logging.warning('No reactions matched the specified template, {0}'.format(template))
            return []
    else:
        selected_rxns = rxn_list

    # We want to sort all the reactions into sublists composed of isomorphic reactions
    # with degenerate transition states
    sorted_rxns = []
    for rxn0 in selected_rxns:
        # find resonance structures for rxn0
        rxn0.ensure_species()
        if len(sorted_rxns) == 0:
            # This is the first reaction, so create a new sublist
            sorted_rxns.append([rxn0])
        else:
            # Loop through each sublist, which represents a unique reaction
            for sub_list in sorted_rxns:
                # Try to determine if the current rxn0 is identical or isomorphic to any reactions in the sublist
                isomorphic = False
                identical = False
                sameTemplate = True
                for rxn in sub_list:
                    isomorphic = rxn0.isIsomorphic(rxn, checkIdentical=False, checkTemplateRxnProducts=True)
                    if isomorphic:
                        identical = rxn0.isIsomorphic(rxn, checkIdentical=True, checkTemplateRxnProducts=True)
                        if identical:
                            # An exact copy of rxn0 is already in our list, so we can move on
                            break
                        sameTemplate = frozenset(rxn.template) == frozenset(rxn0.template)
                    else:
                        # This sublist contains a different product
                        break

                # Process the reaction depending on the results of the comparisons
                if identical:
                    # This reaction does not contribute to degeneracy
                    break
                elif isomorphic:
                    if sameTemplate:
                        # We found the right sublist, and there is no identical reaction
                        # We should add rxn0 to the sublist as a degenerate rxn, and move on to the next rxn
                        sub_list.append(rxn0)
                        break
                    else:
                        # We found an isomorphic sublist, but the reaction templates are different
                        # We need to mark this as a duplicate and continue searching the remaining sublists
                        rxn0.duplicate = True
                        sub_list[0].duplicate = True
                        continue
                else:
                    # This is not an isomorphic sublist, so we need to continue searching the remaining sublists
                    # Note: This else statement is not technically necessary but is included for clarity
                    continue
            else:
                # We did not break, which means that there was no isomorphic sublist, so create a new one
                sorted_rxns.append([rxn0])

    rxn_list = []
    for sub_list in sorted_rxns:
        # Collapse our sorted reaction list by taking one reaction from each sublist
        rxn = sub_list[0]
        # The degeneracy of each reaction is the number of reactions that were in the sublist
        rxn.degeneracy = sum([reaction0.degeneracy for reaction0 in sub_list])
        rxn_list.append(rxn)

    for rxn in rxn_list:
        if rxn.is_forward:
            reduce_same_reactant_degeneracy(rxn, same_reactants)
        else:
            # fix the degeneracy of (not ownReverse) reactions found in the backwards direction
            try:
                family = kinetics_family or kinetics_database.families[rxn.family]
            except AttributeError:
                from rmgpy.data.rmg import getDB
                family = getDB('kinetics').families[rxn.family]
            if not family.ownReverse:
                rxn.degeneracy = family.calculateDegeneracy(rxn)

    return rxn_list
Example #50
0
    def execute(self,
                outputFile,
                plot,
                format='pdf',
                print_summary=True,
                speciesList=None,
                thermoLibrary=None,
                kineticsLibrary=None):

        logging.info('Exploring network...')

        rmg = RMG()

        rmg.speciesConstraints = {
            'allowed':
            ['input species', 'seed mechanisms', 'reaction libraries'],
            'maximumRadicalElectrons': self.maximumRadicalElectrons,
            'explicitlyAllowedMolecules': []
        }

        rmgpy.rmg.input.rmg = rmg

        reaction_model = CoreEdgeReactionModel()

        reaction_model.pressureDependence = self.pdepjob

        reaction_model.pressureDependence.rmgmode = True

        if outputFile:
            reaction_model.pressureDependence.outputFile = os.path.dirname(
                outputFile)

        kineticsDatabase = getDB('kinetics')
        thermoDatabase = getDB('thermo')

        thermoDatabase.libraries['thermojobs'] = thermoLibrary
        thermoDatabase.libraryOrder.insert(0, 'thermojobs')

        kineticsDatabase.libraries['kineticsjobs'] = kineticsLibrary
        kineticsDatabase.libraryOrder.insert(
            0, ('kineticsjobs', 'Reaction Library'))

        jobRxns = [rxn for rxn in reaction_model.core.reactions]

        self.jobRxns = jobRxns

        if outputFile is not None:
            if not os.path.exists(
                    os.path.join(reaction_model.pressureDependence.outputFile,
                                 'pdep')):
                os.mkdir(
                    os.path.join(reaction_model.pressureDependence.outputFile,
                                 'pdep'))
            else:
                shutil.rmtree(
                    os.path.join(reaction_model.pressureDependence.outputFile,
                                 'pdep'))
                os.mkdir(
                    os.path.join(reaction_model.pressureDependence.outputFile,
                                 'pdep'))

        # get the molecular formula for the network
        mmol = None
        for spc in self.source:
            if mmol:
                mmol.merge(spc.molecule[0])
            else:
                mmol = spc.molecule[0]

        form = mmol.getFormula()

        for spec in self.bathGas.keys() + self.source:
            nspec, isNew = reaction_model.makeNewSpecies(spec, reactive=False)
            flags = np.array([
                s.molecule[0].getFormula() == form
                for s in reaction_model.core.species
            ])
            reaction_model.enlarge(nspec,
                                   reactEdge=False,
                                   unimolecularReact=flags,
                                   bimolecularReact=np.zeros(
                                       (len(reaction_model.core.species),
                                        len(reaction_model.core.species))))

        reaction_model.addSeedMechanismToCore('kineticsjobs')

        for lib in kineticsDatabase.libraryOrder:
            if lib[0] != 'kineticsjobs':
                reaction_model.addReactionLibraryToEdge(lib[0])

        for spc in reaction_model.core.species:
            for i, item in enumerate(self.source):
                if spc.isIsomorphic(item):
                    self.source[i] = spc

        # react initial species
        flags = np.array([
            s.molecule[0].getFormula() == form
            for s in reaction_model.core.species
        ])
        reaction_model.enlarge(reactEdge=True,
                               unimolecularReact=flags,
                               bimolecularReact=np.zeros(
                                   (len(reaction_model.core.species),
                                    len(reaction_model.core.species))))

        # find the network we're interested in
        for nwk in reaction_model.networkList:
            if set(nwk.source) == set(self.source):
                self.source = nwk.source
                network = nwk
                break
        else:
            raise ValueError(
                'Did not generate a network with the requested source. This usually means no unimolecular'
                'reactions were generated for the source. Note that library reactions that are not'
                ' properly flagged as elementary_high_p can replace RMG generated reactions that would'
                ' otherwise be part of networks.')

        network.bathGas = self.bathGas

        self.network = network

        # determine T and P combinations

        if self.pdepjob.Tlist:
            Tlist = self.pdepjob.Tlist.value_si
        else:
            Tlist = np.linspace(self.pdepjob.Tmin.value_si,
                                self.pdepjob.Tmax.value_si,
                                self.pdepjob.Tcount)

        if self.pdepjob.Plist:
            Plist = self.pdepjob.Plist.value_si
        else:
            Plist = np.linspace(self.pdepjob.Pmin.value_si,
                                self.pdepjob.Pmax.value_si,
                                self.pdepjob.Pcount)

        # generate the network

        forbiddenStructures = getDB('forbidden')
        incomplete = True

        while incomplete:
            incomplete = False
            for T in Tlist:
                for P in Plist:
                    if network.getLeakCoefficient(T=T, P=P) > self.explore_tol:
                        incomplete = True
                        spc = network.getMaximumLeakSpecies(T=T, P=P)
                        if forbiddenStructures.isMoleculeForbidden(
                                spc.molecule[0]):
                            reaction_model.removeSpeciesFromEdge(
                                reaction_model.reactionSystems, spc)
                            reaction_model.removeEmptyPdepNetworks()
                            logging.error(spc.label)
                        else:
                            logging.info(
                                'adding new isomer {0} to network'.format(spc))
                            flags = np.array([
                                s.molecule[0].getFormula() == form
                                for s in reaction_model.core.species
                            ])
                            reaction_model.enlarge(
                                (network, spc),
                                reactEdge=False,
                                unimolecularReact=flags,
                                bimolecularReact=np.zeros(
                                    (len(reaction_model.core.species),
                                     len(reaction_model.core.species))))

                            flags = np.array([
                                s.molecule[0].getFormula() == form
                                for s in reaction_model.core.species
                            ])
                            reaction_model.enlarge(
                                reactEdge=True,
                                unimolecularReact=flags,
                                bimolecularReact=np.zeros(
                                    (len(reaction_model.core.species),
                                     len(reaction_model.core.species))))

        rmRxns = []
        for rxn in network.pathReactions:  # remove reactions with forbidden species
            for r in rxn.reactants + rxn.products:
                if forbiddenStructures.isMoleculeForbidden(r.molecule[0]):
                    rmRxns.append(rxn)

        for rxn in rmRxns:
            logging.info('Removing forbidden reaction: {0}'.format(rxn))
            network.pathReactions.remove(rxn)

        # clean up output files
        if outputFile is not None:
            path = os.path.join(reaction_model.pressureDependence.outputFile,
                                'pdep')
            for name in os.listdir(path):
                if name.endswith('.py') and '_' in name:
                    if name.split('_')[-1].split('.')[0] != str(
                            len(network.isomers)):
                        os.remove(os.path.join(path, name))
                    else:
                        os.rename(os.path.join(path, name),
                                  os.path.join(path, 'network_full.py'))

        warns = []

        for rxn in jobRxns:
            if rxn not in network.pathReactions:
                warns.append(
                    'Reaction {0} in the input file was not explored during network expansion and was not included in the full network.  This is likely because your explore_tol value is too high.'
                    .format(rxn))

        # reduction process

        if self.energy_tol != np.inf or self.flux_tol != 0.0:

            rxnSet = None

            for T in Tlist:
                if self.energy_tol != np.inf:
                    rxns = network.get_energy_filtered_reactions(
                        T, self.energy_tol)
                    if rxnSet is not None:
                        rxnSet &= set(rxns)
                    else:
                        rxnSet = set(rxns)

                for P in Plist:
                    if self.flux_tol != 0.0:
                        rxns = network.get_rate_filtered_reactions(
                            T, P, self.flux_tol)
                        if rxnSet is not None:
                            rxnSet &= set(rxns)
                        else:
                            rxnSet = set(rxns)

            logging.info('removing reactions during reduction:')
            for rxn in rxnSet:
                logging.info(rxn)

            network.remove_reactions(reaction_model, list(rxnSet))

            for rxn in jobRxns:
                if rxn not in network.pathReactions:
                    warns.append(
                        'Reaction {0} in the input file was not included in the reduced model.'
                        .format(rxn))

        self.network = network

        self.pdepjob.network = network

        self.pdepjob.execute(outputFile,
                             plot,
                             format='pdf',
                             print_summary=True)

        if warns != []:
            logging.info('\nOUTPUT WARNINGS:\n')
            for w in warns:
                logging.warning(w)
def species(label, *args, **kwargs):
    global speciesDict, jobList
    if label in speciesDict:
        raise ValueError('Multiple occurrences of species with label {0!r}.'.format(label))
    logging.info('Loading species {0}...'.format(label))
    
    spec = Species(label=label)
    speciesDict[label] = spec

    path = None
    if len(args) == 1:
        # The argument is a path to a conformer input file
        path = args[0]
        job = StatMechJob(species=spec, path=path)
        logging.debug('Added species {0} to a stat mech job.'.format(label))
        jobList.append(job)
    elif len(args) > 1:
        raise InputError('species {0} can only have two non-keyword argument '
                         'which should be the species label and the '
                         'path to a quantum file.'.format(spec.label))
    
    if len(kwargs) > 0:
        # The species parameters are given explicitly
        structure = None
        E0 = None
        modes = []
        spinMultiplicity = 0
        opticalIsomers = 1
        molecularWeight = None
        collisionModel = None
        energyTransferModel = None
        thermo = None
        reactive = True
        for key, value in kwargs.items():
            if key == 'structure':
                structure = value
            elif key == 'E0':
                E0 = value
            elif key == 'modes':
                modes = value
            elif key == 'spinMultiplicity':
                spinMultiplicity = value
            elif key == 'opticalIsomers':
                opticalIsomers = value
            elif key == 'molecularWeight':
                molecularWeight = value
            elif key == 'collisionModel':
                collisionModel = value
            elif key == 'energyTransferModel':
                energyTransferModel = value
            elif key == 'thermo':
                thermo = value
            elif key == 'reactive':
                reactive = value
            else:
                raise TypeError('species() got an unexpected keyword argument {0!r}.'.format(key))
            
        if structure:
            spec.molecule = [structure]
        spec.conformer = Conformer(E0=E0, modes=modes, spinMultiplicity=spinMultiplicity, opticalIsomers=opticalIsomers)
        if molecularWeight is not None:
            spec.molecularWeight = molecularWeight
        elif spec.molecularWeight is None and is_pdep(jobList):
            # If a structure was given, simply calling spec.molecularWeight will calculate the molecular weight
            # If one of the jobs is pdep and no molecular weight is given or calculated, raise an error
            raise ValueError("No molecularWeight was entered for species {0}. Since a structure wasn't given"
                             " as well, the molecularWeight, which is important for pressure dependent jobs,"
                             " cannot be reconstructed.".format(spec.label))
        spec.transportData = collisionModel
        spec.energyTransferModel = energyTransferModel
        spec.thermo = thermo
        spec.reactive = reactive
        
        if spec.reactive and path is None and spec.thermo is None and spec.conformer.E0 is None:
            if not spec.molecule:
                raise InputError('Neither thermo, E0, species file path, nor structure specified, cannot estimate'
                                 ' thermo properties of species {0}'.format(spec.label))
            try:
                db = getDB('thermo')
                if db is None:
                    raise DatabaseError('Thermo database is None.')
            except DatabaseError:
                logging.warn("The database isn't loaded, cannot estimate thermo for {0}. "
                             "If it is a bath gas, set reactive = False to avoid generating thermo.".format(spec.label))
            else:
                logging.info('No E0 or thermo found, estimating thermo and E0 of species {0} using'
                             ' RMG-Database...'.format(spec.label))
                spec.thermo = db.getThermoData(spec)
                if spec.thermo.E0 is None:
                    th = spec.thermo.toWilhoit()
                    spec.conformer.E0 = th.E0
                    spec.thermo.E0 = th.E0
                else:
                    spec.conformer.E0 = spec.thermo.E0

        if spec.reactive and spec.thermo and not spec.hasStatMech() and structure is not None:
            # generate stat mech info if it wasn't provided before
            spec.generateStatMech()

        if not energyTransferModel:
            # default to RMG's method of generating energyTransferModel
            spec.generateEnergyTransferModel()

    return spec