예제 #1
0
def read_thermo_lib_by_path(lib_path, thermo_db):
    """
    Read thermo library given its library path
    
    Args:
        lib_path (str): Path to thermo library file
        thermo_database (ThermoDatabase): RMG thermo database object
    """
    if not os.path.exists(lib_path):
        logging.error('The library file %s does not exist.' %(lib_path))
        return
    if lib_path not in thermo_db.library_order:
        lib = ThermoLibrary()
        try:
            lib.load(lib_path, ThermoDatabase().local_context,
                    ThermoDatabase().global_context)
        except:
            logging.error('The library file %s is not vaild.' % (lib_path))
            return
        else:
            lib.label = lib_path
            thermo_db.libraries[lib.label] = lib
            thermo_db.library_order.append(lib.label)
            logging.info('Loading thermodynamics library {1} from {0} ...'.format(
            os.path.split(lib_path)[0], os.path.split(lib_path)[1]),)
    else:
        logging.warning('The library %s has already been loaded' %(lib_path))
예제 #2
0
 def load_thermo(self, path, thermo_libraries=None, depository=True):
     """
     Load the RMG thermo database from the given `path` on disk, where
     `path` points to the top-level folder of the RMG thermo database.
     """
     self.thermo = ThermoDatabase()
     self.thermo.load(path, thermo_libraries, depository)
예제 #3
0
def load_thermo_lib_by_path(path: str, thermo_db: ThermoDatabase):
    """
    Load thermo library given its path. This is really helpful when the
    library is not located in the RMG-database.

    Args:
        path (str): Path to thermo library file
        thermo_database (ThermoDatabase): RMG thermo database object
    """
    if not os.path.exists(path):
        raise ValueError(f'The library file {path} does not exist.')

    if path not in thermo_db.library_order:
        lib = ThermoLibrary()
        try:
            lib.load(path,
                     ThermoDatabase().local_context,
                     ThermoDatabase().global_context)
        except:
            raise ValueError(f'The library file {path} is not vaild.')
            return
        else:
            lib.label = path
            thermo_db.libraries[lib.label] = lib
            thermo_db.library_order.append(lib.label)
            print(f'Loading thermo library {os.path.split(path)[1]} '
                  f'from {os.path.split(path)[0]} ...')
    else:
        print(f'The library {path} has already been loaded')
예제 #4
0
def load_thermo_lib_by_path(path: str,
                            thermo_db: ThermoDatabase,
                            reload: bool = False):
    """
    Load thermo library given its library path and store it into
    the given RMG ThermoDatabase instance

    Args:
        path (str): Path to thermo library file
        thermo_database (ThermoDatabase): RMG thermo database object
        reload (bool): Whether to reload the library if this library is in the ThermoDatabase
    """
    lib = ThermoLibrary()
    try:
        lib.load(path,
                 ThermoDatabase().local_context,
                 ThermoDatabase().global_context)
    except FileNotFoundError:
        print(f'The library file {path} does not exist.')
    except (SyntaxError, ImportError):
        print(f'The library file {path} is not valid.')
    else:
        if path in thermo_db.library_order and not reload:
            print(f'The library {path} has already been loaded.')
            return
        elif path not in thermo_db.library_order:
            thermo_db.library_order.append(path)
        lib.label = path
        lib.name = path
        thermo_db.libraries[lib.label] = lib
        print(f'The thermodynamics library {path} is loaded.')
예제 #5
0
def find_thermo_libs(path):
    """
    This function search for the thermo library
    based on /thermo/*.py or /libraries/*.py
    
    Args:
        path (str): The path to project directories
    
    Returns:
        thermo_lib_list (list): Entries of the path to thermo libraries
    """
    # Initiate the thermo lib list
    thermo_lib_list = list()
    # Walk through the dirs under path
    for root_p, dirs, _ in os.walk(path):
        if not dirs:
            continue
        # Use ARC folder organization to check thermo library
        chk_path = os.path.join(root_p, 'thermo')
        if os.path.isdir(chk_path):
            # Find the corresponding thermo lib file
            thermo_lib = glob.glob(os.path.join(chk_path, '*.py'))[0]
            # Check the lib is valid by loading it
            if thermo_lib:
                try:
                    lib = ThermoLibrary()
                    lib.load(thermo_lib, ThermoDatabase().local_context, ThermoDatabase().global_context)
                except:
                    continue
                else:
                    thermo_lib_list.append(thermo_lib)
                    logging.info("Find thermo library at {0}".format(thermo_lib))
    return thermo_lib_list
예제 #6
0
def loadThermoDatabase(path):
    """
    Load the RMG thermodynamics database from `path`.
    """
    global thermoDatabase
    print 'Loading thermodynamics database...'
    thermoDatabase = ThermoDatabase()
    thermoDatabase.load(path)
예제 #7
0
def loadThermoDatabase(path):
    """
    Load the RMG thermodynamics database from `path`.
    """
    global thermoDatabase
    print 'Loading thermodynamics database...'
    thermoDatabase = ThermoDatabase()
    thermoDatabase.load(path)
예제 #8
0
def load_thermo_database(libraries: Optional[list] = None):
    """
    A helper function to load thermo database given libraries used

    Args:
        libraries (Optional[list]): A list of libraries to be imported. All
                                    libraies will be imported if not assigned.
    """
    thermo_db_path = os.path.join(rmg_settings['database.directory'], 'thermo')
    thermo_db = ThermoDatabase()
    thermo_db.load(thermo_db_path, libraries=libraries)
    return thermo_db
예제 #9
0
파일: thermoTest.py 프로젝트: ngvjai/RMG-Py
    def testRemoveGroup(self):
        """
        Test that removing groups using nodes near the root of radical.py
        """
        #load up test data designed for this test
        database2 = ThermoDatabase()
        path = os.path.join(os.path.dirname(rmgpy.__file__),'data/test_data/')
        database2.load(os.path.join(path, 'thermo'), depository = False)

        #load up the thermo radical database as a test
        radGroup = database2.groups['radical']

        #use root as removed groups parent, which should be an LogicOr node
        root = radGroup.top[0]
        #use group to remove as
        groupToRemove = radGroup.entries['RJ']
        children = groupToRemove.children

        #remove the group
        radGroup.removeGroup(groupToRemove)

        #afterwards groupToRemove should not be in the database or root's children
        self.assertFalse(groupToRemove in radGroup.entries.values())
        self.assertFalse(groupToRemove in root.children)

        for child in children:
            #groupToRemove children should all be in roots item.component and children attribuetes
            self.assertTrue(child.label in root.item.components)
            self.assertTrue(child in root.children)
            #the children should all have root a their parent now
            self.assertTrue(child.parent is root)

        #Specific to ThermoDatabase, (above test apply to all base class Database)
        #we check that unicode entry.data pointers are correctly reassigned

        #if groupToRemove is a pointer and another node pointed to it, we copy
        #groupToRemove pointer
        self.assertTrue(radGroup.entries['OJ'].data is groupToRemove.data)

        #Remove an entry with a ThermoData object
        groupToRemove2 = radGroup.entries['CsJ']
        radGroup.removeGroup(groupToRemove2)
        #If groupToRemove was a data object, we point toward parent instead
        self.assertTrue(radGroup.entries['RJ2_triplet'].data == groupToRemove2.parent.label)
        #If the parent pointed toward groupToRemove, we need should have copied data object
        Tlist=[300, 400, 500, 600, 800, 1000, 1500]
        self.assertFalse(isinstance(groupToRemove2.parent.data, basestring))
        self.assertTrue(groupToRemove2.parent.data.getEnthalpy(298) == groupToRemove2.data.getEnthalpy(298))
        self.assertTrue(groupToRemove2.parent.data.getEntropy(298) == groupToRemove2.data.getEntropy(298))
        self.assertFalse(False in [groupToRemove2.parent.data.getHeatCapacity(x) == groupToRemove2.data.getHeatCapacity(x) for x in Tlist])
예제 #10
0
    def setUpClass(cls):
        """A function run ONCE before all unit tests in this class."""
        # Set up a dummy database
        cls.database = RMGDatabase()
        cls.database.load(
            path=os.path.join(settings['test_data.directory'],
                              'testing_database'),
            thermoLibraries=[],
            reactionLibraries=[],
            kineticsFamilies=[],
            depository=False,
            solvation=False,
            testing=True,
        )
        cls.database.loadForbiddenStructures()

        cls.thermoDatabase = ThermoDatabase()  #the real full Thermo Database
        cls.thermoDatabase.load(path=os.path.join(
            settings['database.directory'], 'thermo'),
                                libraries=['primaryThermoLibrary'])

        cls.kineticsDatabase = KineticsDatabase()
        cls.kineticsDatabase.loadFamilies(
            path=os.path.join(settings['test_data.directory'],
                              'testing_database/kinetics/families'),
            families=[
                'Singlet_Carbene_Intra_Disproportionation',
            ],
        )
        cls.family = cls.kineticsDatabase.families[
            'Singlet_Carbene_Intra_Disproportionation']
예제 #11
0
파일: familyTest.py 프로젝트: qize/RMG-Py
    def setUpClass(cls):
        """A function run ONCE before all unit tests in this class."""
        # Set up a dummy database
        cls.database = RMGDatabase()
        cls.database.load(
            path=os.path.join(settings['test_data.directory'], 'testing_database'),
            thermo_libraries=[],
            reaction_libraries=[],
            kinetics_families=[],
            depository=False,
            solvation=False,
            testing=True,
        )
        cls.database.load_forbidden_structures()

        cls.thermoDatabase = ThermoDatabase()  # the real full Thermo Database
        cls.thermoDatabase.load(path=os.path.join(settings['database.directory'], 'thermo'),
                                libraries=['primaryThermoLibrary'])

        cls.kineticsDatabase = KineticsDatabase()
        cls.kineticsDatabase.load_families(
            path=os.path.join(settings['test_data.directory'], 'testing_database/kinetics/families'),
            families=[
                'Singlet_Carbene_Intra_Disproportionation',
            ],
        )
        cls.family = cls.kineticsDatabase.families['Singlet_Carbene_Intra_Disproportionation']
        cls.treerxns = cls.family.get_training_set(thermo_database=cls.thermoDatabase, remove_degeneracy=True,
                                                   estimate_thermo=True, fix_labels=True, get_reverse=True)
예제 #12
0
 def load_old(self, path):
     """
     Load the old RMG database from the given `path` on disk, where `path`
     points to the top-level folder of the old RMG database.
     """
     self.thermo = ThermoDatabase()
     self.thermo.load_old(path)
     self.transport = TransportDatabase()
     # self.transport.load_old(path)   #  Currently no load_old import function available for transport groups
     self.forbidden_structures = ForbiddenStructures()
     self.forbidden_structures.load_old(os.path.join(path, 'ForbiddenStructures.txt'))
     self.kinetics = KineticsDatabase()
     self.kinetics.load_old(path)
     self.statmech = StatmechDatabase()
     self.statmech.load_old(path)
     self.solvation = SolvationDatabase()
예제 #13
0
파일: model.py 프로젝트: kblondal/RMG-tests
    def __init__(self, kernel_type):

        self.kernel_type = kernel_type
        if kernel_type == 'GA':

            from rmgpy.data.thermo import ThermoDatabase
            from rmgpy import settings
            database = ThermoDatabase()
            print('GA Database directory:\n{0}'.format(
                settings['database.directory']))
            database.load(os.path.join(settings['database.directory'],
                                       'thermo'),
                          libraries=[])

            self.kernel = database

        else:
            raise Exception(
                'Kernel type {0} not supported yet.'.format(kernel_type))
예제 #14
0
 def __init__(self):
     self.database = RMGDatabase()
     self.database.kinetics = KineticsDatabase()
     self.database.thermo = ThermoDatabase()
     self.database.transport = TransportDatabase()
     self.database.statmech = StatmechDatabase()
     self.database.solvation = SolvationDatabase()
     self.database.load_forbidden_structures(
         os.path.join(rmgweb.settings.DATABASE_PATH,
                      'forbiddenStructures.py'))
     self.timestamps = {}
예제 #15
0
    def getH298(self, thermo_db=None):
        """
        Compute and return the standard enthalpy of formation of the structure
        in kcal/mol. A :class:`rmgpy.data.thermo.ThermoDatabase` instance can
        be supplied, which is used to search databases and use group additivity
        values.
        """
        # Load thermo database
        if thermo_db is None:
            thermo_db = ThermoDatabase()
            thermo_db.load(os.path.join(settings['database.directory'], 'thermo'))

        # Compute enthalpy for each molecule and add together
        H298 = 0.0
        self.separateMol()
        for mol in self.mols:
            spc = mol.toRMGSpecies()
            spc.thermo = thermo_db.getThermoData(spc)
            H298 += spc.getEnthalpy(298.0) / constants.kcal_to_J

        # Return combined enthalpy of all molecules
        return H298
예제 #16
0
 def setUp(self):
     """
     A function run before each unit test in this class.
     """
     
     self.database = ThermoDatabase()
     self.database.load(os.path.join(settings['database.directory'], 'thermo'))
     
     self.oldDatabase = ThermoDatabase()
     self.oldDatabase.loadOld(os.path.join(settings['database.directory'], '../output/RMG_database'))
     
     self.Tlist = [300, 400, 500, 600, 800, 1000, 1500]
     
     self.testCases = [
         # SMILES            symm  H298     S298     Cp300  Cp400  Cp500  Cp600  Cp800  Cp1000 Cp1500
         
         # 1,3-hexadiene decomposition products
         ['C=CC=CCC',        3,    13.5090, 86.5641, 29.49, 37.67, 44.54, 50.12, 58.66, 64.95, 74.71],
         ['[CH]=CC=CCC',     3,    72.6056, 87.9528, 29.30, 36.92, 43.18, 48.20, 55.84, 61.46, 70.18],
         ['C=[C]C=CCC',      3,    61.2064, 87.2754, 29.68, 36.91, 43.03, 48.11, 55.96, 61.78, 71.54],
         ['C=C[C]=CCC',      3,    61.2064, 87.2754, 29.68, 36.91, 43.03, 48.11, 55.96, 61.78, 71.54],
         ['C=CC=[C]CC',      3,    70.4053, 88.3718, 29.15, 36.46, 42.60, 47.60, 55.32, 61.04, 69.95],
         ['C=CC=C[CH]C',     6,    38.2926, 84.5953, 27.79, 35.46, 41.94, 47.43, 55.74, 61.92, 71.86],
         ['C=CC=CC[CH2]',    2,    62.5044, 89.9747, 28.72, 36.31, 42.63, 47.72, 55.50, 61.21, 70.05],
         ['[CH3]',           6,    35.1084, 46.3644,  9.20,  9.98, 10.75, 11.50, 12.86, 14.08, 16.29],
         ['C=CC=C[CH2]',     2,    46.1521, 75.9733, 22.54, 28.95, 34.24, 38.64, 45.14, 49.97, 57.85],
         ['[CH2]C',          6,    28.3580, 59.0565, 12.11, 14.59, 17.08, 19.35, 22.93, 25.78, 30.30],
         ['C=CC=[CH]',       1,    85.2149, 69.4966, 18.93, 23.55, 27.16, 29.92, 34.02, 37.03, 41.81],
         ['C=[CH]',          1,    71.6377, 55.8964, 10.24, 12.03, 13.71, 15.17, 17.35, 19.07, 21.82],
         ['[CH]=CCC',        3,    59.0278, 75.1332, 20.38, 25.34, 29.68, 33.36, 39.14, 43.48, 50.22],
         
         # Cyclic structures
         ['c1ccccc1',        1,    19.8389, 69.3100, 19.44, 26.64, 32.76, 37.80, 45.24, 50.46, 58.38],
         ['C1CCCCC1',        1,   -29.4456, 74.8296, 27.20, 37.60, 46.60, 54.80, 67.50, 76.20, 88.50],
         ['c1ccc2ccccc2c1',  1,    36.0639, 82.4536, 31.94, 42.88, 52.08, 59.62, 70.72, 78.68, 90.24],
         ['C1CCC1',          1,     6.5148, 67.5963, 17.39, 23.91, 29.86, 34.76, 42.40, 47.98, 56.33],
         ['C1C=CC=C1',       1,    32.5363, 67.0035, 18.16, 24.71, 30.25, 34.70, 41.25, 45.83, 52.61],
     ]
예제 #17
0
    def getH298(self, thermo_db=None):
        """
        Compute and return the standard enthalpy of formation of the structure
        in kcal/mol. A :class:`rmgpy.data.thermo.ThermoDatabase` instance can
        be supplied, which is used to search databases and use group additivity
        values.
        """
        # Load thermo database
        if thermo_db is None:
            thermo_db = ThermoDatabase()
            thermo_db.load(
                os.path.join(settings['database.directory'], 'thermo'))

        # Compute enthalpy for each molecule and add together
        H298 = 0.0
        self.separateMol()
        for mol in self.mols:
            spc = mol.toRMGSpecies()
            spc.thermo = thermo_db.getThermoData(spc)
            H298 += spc.getEnthalpy(298.0) / constants.kcal_to_J

        # Return combined enthalpy of all molecules
        return H298
예제 #18
0
def loadDatabase(component='', section=''):
    """
    Load the requested `component` of the RMG database if modified since last loaded.
    """
    global database
    if not database:
        database = RMGDatabase()
        database.thermo = ThermoDatabase()
        database.kinetics = KineticsDatabase()
        database.loadForbiddenStructures(os.path.join(settings.DATABASE_PATH, 'forbiddenStructures.py'))

    if component in ['thermo', '']:
        if section in ['depository', '']:
            dirpath = os.path.join(settings.DATABASE_PATH, 'thermo', 'depository')
            if isDirModified(dirpath):
                database.thermo.loadDepository(dirpath)
                resetDirTimestamps(dirpath)
        if section in ['libraries', '']:
            dirpath = os.path.join(settings.DATABASE_PATH, 'thermo', 'libraries')
            if isDirModified(dirpath):
                database.thermo.loadLibraries(dirpath)
                # put them in our preferred order, so that when we look up thermo in order to estimate kinetics,
                # we use our favourite values first.
                preferred_order = ['primaryThermoLibrary','DFT_QCI_thermo','GRI-Mech3.0','CBS_QB3_1dHR','KlippensteinH2O2']
                new_order = [i for i in preferred_order if i in database.thermo.libraryOrder]
                for i in database.thermo.libraryOrder:
                    if i not in new_order: new_order.append(i) 
                database.thermo.libraryOrder = new_order
                resetDirTimestamps(dirpath)
        if section in ['groups', '']:
            dirpath = os.path.join(settings.DATABASE_PATH, 'thermo', 'groups')
            if isDirModified(dirpath):
                database.thermo.loadGroups(dirpath)
                resetDirTimestamps(dirpath)
    if component in ['kinetics', '']:
        if section in ['libraries', '']:
            dirpath = os.path.join(settings.DATABASE_PATH, 'kinetics', 'libraries')
            if isDirModified(dirpath):
                database.kinetics.loadLibraries(dirpath)
                resetDirTimestamps(dirpath)
        if section in ['families', '']:
            dirpath = os.path.join(settings.DATABASE_PATH, 'kinetics', 'families')
            if isDirModified(dirpath):
                database.kinetics.loadFamilies(dirpath)
                resetDirTimestamps(dirpath)

    return database
예제 #19
0
class TestThermoDatabase(unittest.TestCase):
    """
    Contains unit tests of the ThermoDatabase class.
    """
    
    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        
        self.database = ThermoDatabase()
        self.database.load(os.path.join(settings['database.directory'], 'thermo'))
        
        self.oldDatabase = ThermoDatabase()
        self.oldDatabase.loadOld(os.path.join(settings['database.directory'], '../output/RMG_database'))
        
        self.Tlist = [300, 400, 500, 600, 800, 1000, 1500]
        
        self.testCases = [
            # SMILES            symm  H298     S298     Cp300  Cp400  Cp500  Cp600  Cp800  Cp1000 Cp1500
            
            # 1,3-hexadiene decomposition products
            ['C=CC=CCC',        3,    13.5090, 86.5641, 29.49, 37.67, 44.54, 50.12, 58.66, 64.95, 74.71],
            ['[CH]=CC=CCC',     3,    72.6056, 87.9528, 29.30, 36.92, 43.18, 48.20, 55.84, 61.46, 70.18],
            ['C=[C]C=CCC',      3,    61.2064, 87.2754, 29.68, 36.91, 43.03, 48.11, 55.96, 61.78, 71.54],
            ['C=C[C]=CCC',      3,    61.2064, 87.2754, 29.68, 36.91, 43.03, 48.11, 55.96, 61.78, 71.54],
            ['C=CC=[C]CC',      3,    70.4053, 88.3718, 29.15, 36.46, 42.60, 47.60, 55.32, 61.04, 69.95],
            ['C=CC=C[CH]C',     6,    38.2926, 84.5953, 27.79, 35.46, 41.94, 47.43, 55.74, 61.92, 71.86],
            ['C=CC=CC[CH2]',    2,    62.5044, 89.9747, 28.72, 36.31, 42.63, 47.72, 55.50, 61.21, 70.05],
            ['[CH3]',           6,    35.1084, 46.3644,  9.20,  9.98, 10.75, 11.50, 12.86, 14.08, 16.29],
            ['C=CC=C[CH2]',     2,    46.1521, 75.9733, 22.54, 28.95, 34.24, 38.64, 45.14, 49.97, 57.85],
            ['[CH2]C',          6,    28.3580, 59.0565, 12.11, 14.59, 17.08, 19.35, 22.93, 25.78, 30.30],
            ['C=CC=[CH]',       1,    85.2149, 69.4966, 18.93, 23.55, 27.16, 29.92, 34.02, 37.03, 41.81],
            ['C=[CH]',          1,    71.6377, 55.8964, 10.24, 12.03, 13.71, 15.17, 17.35, 19.07, 21.82],
            ['[CH]=CCC',        3,    59.0278, 75.1332, 20.38, 25.34, 29.68, 33.36, 39.14, 43.48, 50.22],
            
            # Cyclic structures
            ['c1ccccc1',        1,    19.8389, 69.3100, 19.44, 26.64, 32.76, 37.80, 45.24, 50.46, 58.38],
            ['C1CCCCC1',        1,   -29.4456, 74.8296, 27.20, 37.60, 46.60, 54.80, 67.50, 76.20, 88.50],
            ['c1ccc2ccccc2c1',  1,    36.0639, 82.4536, 31.94, 42.88, 52.08, 59.62, 70.72, 78.68, 90.24],
            ['C1CCC1',          1,     6.5148, 67.5963, 17.39, 23.91, 29.86, 34.76, 42.40, 47.98, 56.33],
            ['C1C=CC=C1',       1,    32.5363, 67.0035, 18.16, 24.71, 30.25, 34.70, 41.25, 45.83, 52.61],
        ]

    def testNewThermoGeneration(self):
        """
        Test that the new ThermoDatabase generates appropriate thermo data.
        """
        
        for smiles, symm, H298, S298, Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500 in self.testCases:
            Cplist = [Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500]
            species = Species(molecule=[Molecule(SMILES=smiles)])
            species.generateResonanceIsomers()
            thermoData = self.database.getThermoData(Species(molecule=[species.molecule[0]]))
            molecule = species.molecule[0]
            for mol in species.molecule[1:]:
                thermoData0 = self.database.getAllThermoData(Species(molecule=[mol]))[0][0]
                for data in self.database.getAllThermoData(Species(molecule=[mol]))[1:]:
                    if data.getEnthalpy(298) < thermoData0.getEnthalpy(298):
                        thermoData0 = data
                if thermoData0.getEnthalpy(298) < thermoData.getEnthalpy(298):
                    thermoData = thermoData0
                    molecule = mol
            
            self.assertEqual(molecule.calculateSymmetryNumber(), symm)
            self.assertTrue(1 - thermoData.getEnthalpy(298) / 4184 / H298 < 0.001)
            self.assertTrue(1 - thermoData.getEntropy(298) / 4.184 / S298 < 0.001)
            for T, Cp in zip(self.Tlist, Cplist):
                self.assertTrue(1 - thermoData.getHeatCapacity(T) / 4.184 / Cp < 0.001)

    def testOldThermoGeneration(self):
        """
        Test that the old ThermoDatabase generates relatively accurate thermo data.
        """
        
        for smiles, symm, H298, S298, Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500 in self.testCases:
            Cplist = [Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500]
            species = Species(molecule=[Molecule(SMILES=smiles)])
            species.generateResonanceIsomers()
            thermoData = self.oldDatabase.getThermoData(Species(molecule=[species.molecule[0]]))
            molecule = species.molecule[0]
            for mol in species.molecule[1:]:
                thermoData0 = self.oldDatabase.getAllThermoData(Species(molecule=[mol]))[0][0]
                for data in self.oldDatabase.getAllThermoData(Species(molecule=[mol]))[1:]:
                    if data.getEnthalpy(298) < thermoData0.getEnthalpy(298):
                        thermoData0 = data
                if thermoData0.getEnthalpy(298) < thermoData.getEnthalpy(298):
                    thermoData = thermoData0
                    molecule = mol
            
            self.assertEqual(molecule.calculateSymmetryNumber(), symm)
            self.assertTrue(1 - thermoData.getEnthalpy(298) / 4184 / H298 < 0.01)
            self.assertTrue(1 - thermoData.getEntropy(298) / 4.184 / S298 < 0.01)
            for T, Cp in zip(self.Tlist, Cplist):
                self.assertTrue(1 - thermoData.getHeatCapacity(T) / 4.184 / Cp < 0.1)
예제 #20
0
class RMGDatabase(object):
    """
    The primary class for working with the RMG database.
    """
    def __init__(self):
        self.thermo = None
        self.transport = None
        self.forbidden_structures = None
        self.kinetics = None
        self.statmech = None
        self.solvation = None
        self.surface = None

        # Store the newly created database in the module.
        global database
        if database is not None:
            logging.warning(
                'An instance of RMGDatabase already exists. Re-initializing it.'
            )
        database = self

    def load(
        self,
        path,
        thermo_libraries=None,
        transport_libraries=None,
        reaction_libraries=None,
        seed_mechanisms=None,
        kinetics_families=None,
        kinetics_depositories=None,
        statmech_libraries=None,
        depository=True,
        solvation=True,
        surface=True,  # on by default, because solvation is also on by default
        testing=False):
        """
        Load the RMG database from the given `path` on disk, where `path`
        points to the top-level folder of the RMG database. If none of the
        optional arguments are provided, then the entire database will be
        loaded. You can use the optional arguments to specify that only certain
        components of the database be loaded.

        Argument testing will load a lighter version of the database used for unit-tests
        """
        if not testing:
            self.load_transport(os.path.join(path, 'transport'),
                                transport_libraries)
            self.load_forbidden_structures(
                os.path.join(path, 'forbiddenStructures.py'))
        self.load_kinetics(os.path.join(path, 'kinetics'), reaction_libraries,
                           seed_mechanisms, kinetics_families,
                           kinetics_depositories)
        if not testing:
            self.load_statmech(os.path.join(path, 'statmech'),
                               statmech_libraries, depository)

        if solvation:
            self.load_solvation(os.path.join(path, 'solvation'))

        if surface:
            self.load_thermo(os.path.join(path, 'thermo'), thermo_libraries,
                             depository, surface)

    def load_thermo(self,
                    path,
                    thermo_libraries=None,
                    depository=True,
                    surface=False):
        """
        Load the RMG thermo database from the given `path` on disk, where
        `path` points to the top-level folder of the RMG thermo database.
        """
        self.thermo = ThermoDatabase()
        self.thermo.load(path, thermo_libraries, depository, surface)

    def load_transport(self, path, transport_libraries=None):
        """
        Load the RMG transport database from the given 'path' on disk, where 
        'path' points to the top-level folder of the RMG transport database.
        """
        self.transport = TransportDatabase()
        self.transport.load(path, transport_libraries)

    def load_forbidden_structures(self, path=None):
        """
        Load the RMG forbidden structures from the given `path` on disk, where
        `path` points to the forbidden structures file.

        If no path is given, a blank forbidden structures object is created.
        """
        self.forbidden_structures = ForbiddenStructures()
        if path is not None:
            self.forbidden_structures.load(path)

    def load_kinetics(self,
                      path,
                      reaction_libraries=None,
                      seed_mechanisms=None,
                      kinetics_families=None,
                      kinetics_depositories=None):
        """
        Load the RMG kinetics database from the given `path` on disk, where
        `path` points to the top-level folder of the RMG kinetics database.
        """
        kinetics_libraries = []
        library_order = []
        if seed_mechanisms is None and reaction_libraries is None:
            kinetics_libraries = None
        if seed_mechanisms is not None:
            for library in seed_mechanisms:
                kinetics_libraries.append(library)
                library_order.append((library, 'Seed'))
        if reaction_libraries is not None:
            for library in reaction_libraries:
                kinetics_libraries.append(library)
                library_order.append((library, 'Reaction Library'))

        self.kinetics = KineticsDatabase()
        self.kinetics.library_order = library_order
        self.kinetics.load(path,
                           families=kinetics_families,
                           libraries=kinetics_libraries,
                           depositories=kinetics_depositories)

    def load_solvation(self, path):
        """
        Load the RMG solvation database from the given `path` on disk, where
        `path` points to the top-level folder of the RMG solvation database.
        """
        self.solvation = SolvationDatabase()
        self.solvation.load(path)

    def load_surface(self, path):
        """
        Load the RMG metal database from the given `path` on disk, where
        `path` points to the top-level folder of the RMG surface database.
        """
        self.surface = MetalDatabase()
        self.surface.load(path)

    def load_statmech(self, path, statmech_libraries=None, depository=True):
        """
        Load the RMG statmech database from the given `path` on disk, where
        `path` points to the top-level folder of the RMG statmech database.
        """
        self.statmech = StatmechDatabase()
        self.statmech.load(path, statmech_libraries, depository)

    def load_old(self, path):
        """
        Load the old RMG database from the given `path` on disk, where `path`
        points to the top-level folder of the old RMG database.
        """
        self.thermo = ThermoDatabase()
        self.thermo.load_old(path)
        self.transport = TransportDatabase()
        # self.transport.load_old(path)   #  Currently no load_old import function available for transport groups
        self.forbidden_structures = ForbiddenStructures()
        self.forbidden_structures.load_old(
            os.path.join(path, 'ForbiddenStructures.txt'))
        self.kinetics = KineticsDatabase()
        self.kinetics.load_old(path)
        self.statmech = StatmechDatabase()
        self.statmech.load_old(path)
        self.solvation = SolvationDatabase()
        # Not completely implemented
        # self.solvation.load_old(path)

    def save(self, path):
        """
        Save the RMG database to the given `path` on disk.
        """
        if not os.path.exists(path):
            os.makedirs(path)
        self.forbidden_structures.save(
            os.path.join(path, 'forbiddenStructures.py'))
        self.thermo.save(os.path.join(path, 'thermo'))
        # self.transport.save(os.path.join(path, 'transport')) #Currently no function for saving transport groups
        self.kinetics.save(os.path.join(path, 'kinetics'))
        self.statmech.save(os.path.join(path, 'statmech'))
        self.solvation.save(os.path.join(path, 'solvation'))
        self.transport.save(os.path.join(path, 'transport'))

    def save_old(self, path):
        """
        Save the old RMG database to the given `path` on disk.
        """
        if not os.path.exists(path):
            os.makedirs(path)
        self.thermo.save_old(path)
        self.transport.save_old(path)
        self.forbidden_structures.save_old(
            os.path.join(path, 'ForbiddenStructures.txt'))
        self.kinetics.save_old(path)
        self.statmech.save_old(path)
예제 #21
0
def loadDatabase(component='', section=''):
    """
    Load the requested `component` of the RMG database if modified since last loaded.
    """
    global database
    if not database:
        database = RMGDatabase()
        database.solvation = SolvationDatabase()
        database.thermo = ThermoDatabase()
        database.kinetics = KineticsDatabase()
        database.transport = TransportDatabase()
        database.statmech = StatmechDatabase()
        database.loadForbiddenStructures(
            os.path.join(rmgweb.settings.DATABASE_PATH,
                         'forbiddenStructures.py'))

    if component == 'initialize':
        return database
    if component in ['thermo', '']:
        if section in ['depository', '']:
            dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'thermo',
                                   'depository')
            if isDirModified(dirpath):
                database.thermo.loadDepository(dirpath)
                resetDirTimestamps(dirpath)
        if section in ['libraries', '']:
            dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'thermo',
                                   'libraries')
            if isDirModified(dirpath):
                database.thermo.loadLibraries(dirpath)
                # put them in our preferred order, so that when we look up thermo in order to estimate kinetics,
                # we use our favorite values first.
                preferred_order = [
                    'primaryThermoLibrary', 'DFT_QCI_thermo', 'GRI-Mech3.0',
                    'CBS_QB3_1dHR', 'KlippensteinH2O2'
                ]
                new_order = [
                    i for i in preferred_order
                    if i in database.thermo.libraryOrder
                ]
                for i in database.thermo.libraryOrder:
                    if i not in new_order: new_order.append(i)
                database.thermo.libraryOrder = new_order
                resetDirTimestamps(dirpath)
        if section in ['groups', '']:
            dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'thermo',
                                   'groups')
            if isDirModified(dirpath):
                database.thermo.loadGroups(dirpath)
                resetDirTimestamps(dirpath)

    if component in ['transport', '']:
        if section in ['libraries', '']:
            dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'transport',
                                   'libraries')
            if isDirModified(dirpath):
                database.transport.loadLibraries(dirpath)
                resetDirTimestamps(dirpath)
        if section in ['groups', '']:
            dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'transport',
                                   'groups')
            if isDirModified(dirpath):
                database.transport.loadGroups(dirpath)
                resetDirTimestamps(dirpath)

    if component in ['solvation', '']:
        dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'solvation')
        if isDirModified(dirpath):
            database.solvation.load(dirpath)
            resetDirTimestamps(dirpath)

    if component in ['kinetics', '']:
        if section in ['libraries', '']:
            dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'kinetics',
                                   'libraries')
            if isDirModified(dirpath):
                database.kinetics.loadLibraries(dirpath)
                resetDirTimestamps(dirpath)
        if section in ['families', '']:
            dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'kinetics',
                                   'families')
            if isDirModified(dirpath):
                database.kinetics.loadFamilies(dirpath,
                                               families='all',
                                               depositories='all')
                resetDirTimestamps(dirpath)

                # Make sure to load the entire thermo database prior to adding training values to the rules
                loadDatabase('thermo', '')
                for family in database.kinetics.families.values():
                    oldentries = len(family.rules.entries)
                    family.addKineticsRulesFromTrainingSet(
                        thermoDatabase=database.thermo)
                    newentries = len(family.rules.entries)
                    if newentries != oldentries:
                        print '{0} new entries added to {1} family after adding rules from training set.'.format(
                            newentries - oldentries, family.label)
                    # Filling in rate rules in kinetics families by averaging...
                    family.fillKineticsRulesByAveragingUp()

    if component in ['statmech', '']:
        dirpath = os.path.join(rmgweb.settings.DATABASE_PATH, 'statmech')
        if isDirModified(dirpath):
            database.statmech.load(dirpath)
            resetDirTimestamps(dirpath)

    return database
예제 #22
0
class TestThermoDatabase(unittest.TestCase):
    """
    Contains unit tests of the ThermoDatabase class.
    """
    # Only load these once to save time
    database = ThermoDatabase()
    database.load(os.path.join(settings['database.directory'], 'thermo'))
    oldDatabase = ThermoDatabase()
    oldDatabase.loadOld(
        os.path.join(settings['database.directory'], '../output/RMG_database'))

    def setUp(self):
        """
        A function run before each unit test in this class.
        """

        self.database = self.__class__.database
        self.oldDatabase = self.__class__.oldDatabase

        self.Tlist = [300, 400, 500, 600, 800, 1000, 1500]

        self.testCases = [
            # SMILES            symm  H298     S298     Cp300  Cp400  Cp500  Cp600  Cp800  Cp1000 Cp1500

            # 1,3-hexadiene decomposition products
            [
                'C=CC=CCC', 3, 13.45, 86.37, 29.49, 37.67, 44.54, 50.12, 58.66,
                64.95, 74.71
            ],
            [
                '[CH]=CC=CCC', 3, 72.55, 87.76, 29.30, 36.92, 43.18, 48.20,
                55.84, 61.46, 70.18
            ],
            [
                'C=[C]C=CCC', 3, 61.15, 87.08, 29.68, 36.91, 43.03, 48.11,
                55.96, 61.78, 71.54
            ],
            [
                'C=C[C]=CCC', 3, 61.15, 87.08, 29.68, 36.91, 43.03, 48.11,
                55.96, 61.78, 71.54
            ],
            [
                'C=CC=[C]CC', 3, 70.35, 88.18, 29.15, 36.46, 42.6, 47.6, 55.32,
                61.04, 69.95
            ],
            [
                'C=CC=C[CH]C', 6, 38.24, 84.41, 27.79, 35.46, 41.94, 47.43,
                55.74, 61.92, 71.86
            ],
            [
                'C=CC=CC[CH2]', 2, 62.45, 89.78, 28.72, 36.31, 42.63, 47.72,
                55.50, 61.21, 70.05
            ],
            [
                '[CH3]', 6, 34.81, 46.37, 9.14, 10.18, 10.81, 11.34, 12.57,
                13.71, 15.2
            ],
            [
                'C=CC=C[CH2]', 2, 46.11, 75.82, 22.54, 28.95, 34.24, 38.64,
                45.14, 49.97, 57.85
            ],
            [
                '[CH2]C', 6, 28.6, 59.87, 11.73, 14.47, 17.05, 19.34, 23.02,
                25.91, 31.53
            ],
            [
                'C=CC=[CH]', 1, 85.18, 69.37, 18.93, 23.55, 27.16, 29.92,
                34.02, 37.03, 41.81
            ],
            [
                'C=[CH]', 1, 71.62, 56.61, 10.01, 11.97, 13.66, 15.08, 17.32,
                19.05, 21.85
            ],
            [
                '[CH]=CCC', 3, 58.99, 75.0, 20.38, 25.34, 29.68, 33.36, 39.14,
                43.48, 50.22
            ],

            # Cyclic Structures
            [
                'C1CCCCC1', 12, -29.45, 69.71, 27.20, 37.60, 46.60, 54.80,
                67.50, 76.20, 88.50
            ],
            [
                'C1CCC1', 8, 6.51, 63.35, 17.39, 23.91, 29.86, 34.76, 42.40,
                47.98, 56.33
            ],
            [
                'C1C=CC=C1', 2, 32.5, 65.5, 18.16, 24.71, 30.25, 34.7, 41.25,
                45.83, 52.61
            ],
        ]

    @work_in_progress
    def testNewThermoGeneration(self):
        """
        Test that the new ThermoDatabase generates appropriate thermo data.
        """

        for smiles, symm, H298, S298, Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500 in self.testCases:
            Cplist = [Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500]
            species = Species(molecule=[Molecule(SMILES=smiles)])
            species.generateResonanceIsomers()
            species.molecule[0]
            thermoData = self.database.getThermoDataFromGroups(
                Species(molecule=[species.molecule[0]]))
            molecule = species.molecule[0]
            for mol in species.molecule[1:]:
                thermoData0 = self.database.getAllThermoData(
                    Species(molecule=[mol]))[0][0]
                for data in self.database.getAllThermoData(
                        Species(molecule=[mol]))[1:]:
                    if data.getEnthalpy(298) < thermoData0.getEnthalpy(298):
                        thermoData0 = data
                if thermoData0.getEnthalpy(298) < thermoData.getEnthalpy(298):
                    thermoData = thermoData0
                    molecule = mol
            self.assertAlmostEqual(H298,
                                   thermoData.getEnthalpy(298) / 4184,
                                   places=1,
                                   msg="H298 error for {0}".format(smiles))
            self.assertAlmostEqual(S298,
                                   thermoData.getEntropy(298) / 4.184,
                                   places=1,
                                   msg="S298 error for {0}".format(smiles))
            for T, Cp in zip(self.Tlist, Cplist):
                self.assertAlmostEqual(Cp,
                                       thermoData.getHeatCapacity(T) / 4.184,
                                       places=1,
                                       msg="Cp{1} error for {0}".format(
                                           smiles, T))

    @work_in_progress
    def testSymmetryNumberGeneration(self):
        """
        Test we generate symmetry numbers correctly.
        
        This uses the new thermo database to generate the H298, used 
        to select the stablest resonance isomer.
        """
        for smiles, symm, H298, S298, Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500 in self.testCases:
            species = Species(molecule=[Molecule(SMILES=smiles)])
            species.generateResonanceIsomers()
            thermoData = self.database.getThermoDataFromGroups(
                Species(molecule=[species.molecule[0]]))
            # pick the molecule with lowest H298
            molecule = species.molecule[0]
            for mol in species.molecule[1:]:
                thermoData0 = self.database.getAllThermoData(
                    Species(molecule=[mol]))[0][0]
                for data in self.database.getAllThermoData(
                        Species(molecule=[mol]))[1:]:
                    if data.getEnthalpy(298) < thermoData0.getEnthalpy(298):
                        thermoData0 = data
                if thermoData0.getEnthalpy(298) < thermoData.getEnthalpy(298):
                    thermoData = thermoData0
                    molecule = mol
            self.assertEqual(
                molecule.calculateSymmetryNumber(),
                symm,
                msg="Symmetry number error for {0}".format(smiles))

    @work_in_progress
    def testOldThermoGeneration(self):
        """
        Test that the old ThermoDatabase generates relatively accurate thermo data.
        """
        for smiles, symm, H298, S298, Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500 in self.testCases:
            Cplist = [Cp300, Cp400, Cp500, Cp600, Cp800, Cp1000, Cp1500]
            species = Species(molecule=[Molecule(SMILES=smiles)])
            species.generateResonanceIsomers()
            thermoData = self.oldDatabase.getThermoData(
                Species(molecule=[species.molecule[0]]))
            molecule = species.molecule[0]
            for mol in species.molecule[1:]:
                thermoData0 = self.oldDatabase.getAllThermoData(
                    Species(molecule=[mol]))[0][0]
                for data in self.oldDatabase.getAllThermoData(
                        Species(molecule=[mol]))[1:]:
                    if data.getEnthalpy(298) < thermoData0.getEnthalpy(298):
                        thermoData0 = data
                if thermoData0.getEnthalpy(298) < thermoData.getEnthalpy(298):
                    thermoData = thermoData0
                    molecule = mol

            self.assertAlmostEqual(H298,
                                   thermoData.getEnthalpy(298) / 4184,
                                   places=1,
                                   msg="H298 error for {0}".format(smiles))
            self.assertAlmostEqual(S298,
                                   thermoData.getEntropy(298) / 4.184,
                                   places=1,
                                   msg="S298 error for {0}".format(smiles))
            for T, Cp in zip(self.Tlist, Cplist):
                self.assertAlmostEqual(Cp,
                                       thermoData.getHeatCapacity(T) / 4.184,
                                       places=1,
                                       msg="Cp{1} error for {0}".format(
                                           smiles, T))
    def genNetwork(self, mol_object, **kwargs):
        """
        Execute the automatic reaction discovery procedure.
        """
        # Database
        qm_collection = db['qm_calculate_center']
        config_collection = db['config']
        statistics_collection = db['statistics']
        targets = list(config_collection.find({'generations': 1}))
        config_collection.update_one(
            targets[0], {"$set": {
                'config_path': kwargs['config_path']
            }}, True)
        # Reactant information
        reactant_inchi_key = mol_object.write('inchiKey').strip()  # inchikey

        # Generate all possible products
        gen = Generate(mol_object, **kwargs)
        self.logger.info('Generating all possible products...')
        gen.generateProducts()
        prod_mols = gen.get_prods()
        add_bonds = gen.get_add_bonds()
        break_bonds = gen.get_break_bonds()
        prod_mols_filtered = []
        self.logger.info(f'{len(prod_mols)} possible products are generated\n')

        # Filter reactions based on standard heat of reaction  delta H
        if self.method.lower() == 'mopac':
            self.logger.info(
                f'Now use {self.method} to filter the delta H of reactions....\n'
            )
            if self.generations == 1:
                os.mkdir(path.join(path.dirname(self.ard_path), 'reactions'))
                H298_reac = self.get_mopac_H298(mol_object)
                config_collection.update_one(
                    targets[0], {
                        "$set": {
                            'reactant_energy': H298_reac,
                            'use_irc': self.use_irc,
                            'use_qmmm': self.use_qmmm
                        }
                    }, True)
                mol_object_copy = mol_object.copy()
                for prod_mol in prod_mols:
                    if self.filter_dh_mopac(
                            mol_object,
                            self.cluster_bond,
                            prod_mol,
                            add_bonds[prod_mols.index(prod_mol)],
                            break_bonds[prod_mols.index(prod_mol)],
                            len(prod_mols),
                            qm_collection,
                            refH=None):
                        prod_mols_filtered.append(prod_mol)
                    # Recovery
                    mol_object_copy = mol_object.copy()
            else:
                H298_reac = targets[0]['reactant_energy']
                mol_object_copy = mol_object.copy()
                for prod_mol in prod_mols:
                    if self.filter_dh_mopac(
                            mol_object,
                            self.cluster_bond,
                            prod_mol,
                            add_bonds[prod_mols.index(prod_mol)],
                            break_bonds[prod_mols.index(prod_mol)],
                            len(prod_mols),
                            qm_collection,
                            refH=H298_reac):
                        prod_mols_filtered.append(prod_mol)
                    # Recovery
                    mol_object_copy = mol_object.copy()
        elif self.method.lower() == 'xtb':
            self.logger.info(
                'Now use {} to filter the delta H of reactions....\n'.format(
                    self.method))
            if self.generations == 1:
                os.mkdir(path.join(path.dirname(self.ard_path), 'reactions'))
                H298_reac = self.get_xtb_H298(
                    config_path=kwargs['config_path'])
                config_collection.update_one(
                    targets[0], {
                        "$set": {
                            'reactant_energy': H298_reac,
                            'use_irc': self.use_irc,
                            'use_qmmm': self.use_qmmm
                        }
                    }, True)
                mol_object_copy = mol_object.copy()
                for prod_mol in prod_mols:
                    if self.filter_dh_xtb(
                            mol_object,
                            prod_mol,
                            self.cluster_bond,
                            add_bonds[prod_mols.index(prod_mol)],
                            break_bonds[prod_mols.index(prod_mol)],
                            len(prod_mols),
                            qm_collection,
                            config_path=kwargs['config_path'],
                            refH=None):
                        prod_mols_filtered.append(prod_mol)
                    mol_object.setCoordsFromMol(mol_object_copy)
            else:
                H298_reac = targets[0]['reactant_energy']
                mol_object_copy = mol_object.copy()
                for prod_mol in prod_mols:
                    if self.filter_dh_xtb(
                            mol_object,
                            prod_mol,
                            self.cluster_bond,
                            add_bonds[prod_mols.index(prod_mol)],
                            break_bonds[prod_mols.index(prod_mol)],
                            len(prod_mols),
                            qm_collection,
                            config_path=kwargs['config_path'],
                            refH=H298_reac):
                        prod_mols_filtered.append(prod_mol)
                    mol_object.setCoordsFromMol(mol_object_copy)
        else:
            self.logger.info(
                'Now use {} to filter the delta H of reactions....\n'.format(
                    self.method))
            # Load thermo database and choose which libraries to search
            thermo_db = ThermoDatabase()
            thermo_db.load(path.join(settings['database.directory'], 'thermo'))
            thermo_db.libraryOrder = [
                'primaryThermoLibrary',
                'NISTThermoLibrary',
                'thermo_DFT_CCSDTF12_BAC',
                'CBS_QB3_1dHR',
                'DFT_QCI_thermo',
                'BurkeH2O2',
                'GRI-Mech3.0-N',
            ]
            if self.generations == 1:
                H298_reac = mol_object.getH298(thermo_db)
                update_field = {'reactant_energy': H298_reac}
                config_collection.update_one(targets[0],
                                             {"$set": update_field}, True)
            else:
                H298_reac = targets[0]['reactant_energy']
            prod_mols_filtered = [
                mol for mol in prod_mols
                if self.filter_dh_rmg(H298_reac, mol, thermo_db)
            ]
            self.logger.info('Generate geometry........\n')
            for mol in prod_mols_filtered:
                index = prod_mols.index(mol)
                # Generate geometry and return path
                mol_object.gen3D(self.fixed_atoms,
                                 forcefield=self.forcefield,
                                 method=self.constraintff_alg,
                                 make3D=False)
                reac_mol_copy = mol_object.copy()
                dir_path = self.gen_geometry(mol_object, mol, reac_mol_copy,
                                             add_bonds[index],
                                             break_bonds[index])
                product_inchi_key = mol.write('inchiKey').strip()
                self.logger.info(
                    f"\nReactant inchi key: {reactant_inchi_key}\nProduct inchi key: {product_inchi_key}\nReactant smiles: {mol_object.write('can').split()}\nProduct smiles: {mol.write('can').split()}\nDirectory path: {dir_path}\n"
                )

                qm_collection.insert_one({
                    'reaction': [reactant_inchi_key, product_inchi_key],
                    'reactant_smiles':
                    mol_object.write('can').split()[0],
                    'reactant_inchi_key':
                    reactant_inchi_key,
                    'product_inchi_key':
                    product_inchi_key,
                    'product_smiles':
                    mol.write('can').split()[0],
                    'path':
                    dir_path,
                    'ssm_status':
                    'job_unrun',
                    'generations':
                    self.generations,
                })

        self.logger.info('After delta H filter {} product remain.\n'.format(
            len(prod_mols_filtered)))
        # Generate geometry and insert to database
        statistics_collection.insert_one({
            'reactant_smiles':
            mol_object.write('can').split()[0],
            'reactant_inchi_key':
            reactant_inchi_key,
            'add how many products':
            len(prod_mols_filtered),
            'generations':
            self.generations
        })
예제 #24
0
class TestCyclicThermo(unittest.TestCase):
    """
    Contains unit tests of the ThermoDatabase class.
    """
    database = ThermoDatabase()
    database.load(os.path.join(settings['database.directory'], 'thermo'))

    def setUp(self):
        """
        A function run before each unit test in this class.
        """

        self.database = self.__class__.database

    def testComputeGroupAdditivityThermoForTwoRingMolecule(self):
        """
        The molecule being tested has two rings, one is 13cyclohexadiene5methylene
        the other is benzene ring. This method is to test thermo estimation will
        give two different corrections accordingly. 
        """
        spec = Species().fromSMILES('CCCCCCCCCCCC(CC=C1C=CC=CC1)c1ccccc1')
        spec.generateResonanceIsomers()
        thermo = self.database.getThermoDataFromGroups(spec)

        ringGroups, polycyclicGroups = self.database.getRingGroupsFromComments(
            thermo)
        self.assertEqual(len(ringGroups), 2)
        self.assertEqual(len(polycyclicGroups), 0)

        expected_matchedRingsLabels = ['13cyclohexadiene5methylene', 'Benzene']
        expected_matchedRings = [
            self.database.groups['ring'].entries[label]
            for label in expected_matchedRingsLabels
        ]

        self.assertEqual(set(ringGroups), set(expected_matchedRings))

    def testThermoForMonocyclicAndPolycyclicSameMolecule(self):
        """
        Test a molecule that has both a polycyclic and a monocyclic ring in the same molecule
        """
        spec = Species().fromSMILES('C(CCC1C2CCC1CC2)CC1CCC1')
        spec.generateResonanceIsomers()
        thermo = self.database.getThermoDataFromGroups(spec)
        ringGroups, polycyclicGroups = self.database.getRingGroupsFromComments(
            thermo)
        self.assertEqual(len(ringGroups), 1)
        self.assertEqual(len(polycyclicGroups), 1)

        expected_matchedRingsLabels = ['Cyclobutane']
        expected_matchedRings = [
            self.database.groups['ring'].entries[label]
            for label in expected_matchedRingsLabels
        ]
        self.assertEqual(set(ringGroups), set(expected_matchedRings))

        expected_matchedPolyringsLabels = ['norbornane']
        expected_matchedPolyrings = [
            self.database.groups['polycyclic'].entries[label]
            for label in expected_matchedPolyringsLabels
        ]

        self.assertEqual(set(polycyclicGroups), set(expected_matchedPolyrings))

    def testPolycyclicPicksBestThermo(self):
        """
        Test that RMG prioritizes thermo correctly and chooses the thermo from the isomer which
        has a non generic polycyclic ring correction
        """

        spec = Species().fromSMILES('C1=C[C]2CCC=C2C1')
        spec.generateResonanceIsomers()

        thermoDataList = []
        for molecule in spec.molecule:
            thermo = self.database.estimateRadicalThermoViaHBI(
                molecule, self.database.computeGroupAdditivityThermo)
            thermoDataList.append(thermo)

        thermoDataList.sort(key=lambda x: x.getEnthalpy(298))
        most_stable_thermo = thermoDataList[0]
        ringGroups, polycyclicGroups = self.database.getRingGroupsFromComments(
            most_stable_thermo)

        selected_thermo = self.database.getThermoDataFromGroups(spec)

        self.assertNotEqual(selected_thermo, thermoDataList)

        selected_ringGroups, selected_polycyclicGroups = self.database.getRingGroupsFromComments(
            selected_thermo)

        # The group used to estimate the most stable thermo is the generic polycyclic group and
        # therefore is not selected.  Note that this unit test will have to change if the correction is fixed later.
        self.assertEqual(polycyclicGroups[0].label, 'PolycyclicRing')
        self.assertEqual(selected_polycyclicGroups[0].label, 'C12CCC=C1CC=C2')

    def testGetRingGroupsFromComments(self):
        """
        Test that getRingGroupsFromComments method works for fused polycyclics.
        """
        # set-up RMG object
        rmg = RMG()

        # load kinetic database and forbidden structures
        rmg.database = RMGDatabase()
        path = os.path.join(settings['database.directory'])

        # forbidden structure loading
        rmg.database.loadThermo(os.path.join(path, 'thermo'))

        smi = 'C12C(C3CCC2C3)C4CCC1C4'  #two norbornane rings fused together
        spc = Species().fromSMILES(smi)

        spc.generateThermoData(rmg.database)

        thermodb = rmg.database.thermo
        thermodb.getRingGroupsFromComments(spc.thermo)

        import rmgpy.data.rmg
        rmgpy.data.rmg.database = None
예제 #25
0
    def execute(self, **kwargs):
        """
        Execute the automatic reaction discovery procedure.
        """
        start_time = time.time()
        reac_mol = self.initialize()
        # self.optimizeReactant(reac_mol, **kwargs)

        gen = Generate(reac_mol)
        self.logger.info('Generating all possible products...')
        gen.generateProducts(nbreak=self.nbreak, nform=self.nform)
        prod_mols = gen.prod_mols
        self.logger.info('{} possible products generated\n'.format(
            len(prod_mols)))

        # Load thermo database and choose which libraries to search
        thermo_db = ThermoDatabase()
        thermo_db.load(os.path.join(settings['database.directory'], 'thermo'))
        thermo_db.libraryOrder = [
            'primaryThermoLibrary',
            'NISTThermoLibrary',
            'thermo_DFT_CCSDTF12_BAC',
            'CBS_QB3_1dHR',
            'DFT_QCI_thermo',
            'KlippensteinH2O2',
            'GRI-Mech3.0-N',
        ]

        # Filter reactions based on standard heat of reaction
        H298_reac = reac_mol.getH298(thermo_db)
        self.logger.info('Filtering reactions...')
        prod_mols_filtered = [
            mol for mol in prod_mols
            if self.filterThreshold(H298_reac, mol, thermo_db, **kwargs)
        ]
        self.logger.info('{} products remaining\n'.format(
            len(prod_mols_filtered)))

        # Generate 3D geometries
        if prod_mols_filtered:
            self.logger.info('Feasible products:\n')
            rxn_dir = util.makeOutputSubdirectory(self.output_dir, 'reactions')

            # These two lines are required so that new coordinates are
            # generated for each new product. Otherwise, Open Babel tries to
            # use the coordinates of the previous molecule if it is isomorphic
            # to the current one, even if it has different atom indices
            # participating in the bonds. a hydrogen atom is chosen
            # arbitrarily, since it will never be the same as any of the
            # product structures.
            Hatom = gen3D.readstring('smi', '[H]')
            ff = pybel.ob.OBForceField.FindForceField(self.forcefield)

            reac_mol_copy = reac_mol.copy()
            for rxn, mol in enumerate(prod_mols_filtered):
                mol.gen3D(forcefield=self.forcefield, make3D=False)
                arrange3D = gen3D.Arrange3D(reac_mol, mol)
                msg = arrange3D.arrangeIn3D()
                if msg != '':
                    self.logger.info(msg)

                ff.Setup(
                    Hatom.OBMol
                )  # Ensures that new coordinates are generated for next molecule (see above)
                reac_mol.gen3D(make3D=False)
                ff.Setup(Hatom.OBMol)
                mol.gen3D(make3D=False)
                ff.Setup(Hatom.OBMol)

                reactant = reac_mol.toNode()
                product = mol.toNode()

                rxn_num = '{:04d}'.format(rxn)
                output_dir = util.makeOutputSubdirectory(rxn_dir, rxn_num)
                kwargs['output_dir'] = output_dir
                kwargs['name'] = rxn_num

                self.logger.info('Product {}: {}\n{}\n****\n{}\n'.format(
                    rxn, product.toSMILES(), reactant, product))
                self.makeInputFile(reactant, product, **kwargs)

                reac_mol.setCoordsFromMol(reac_mol_copy)
        else:
            self.logger.info('No feasible products found')

        # Finalize
        self.finalize(start_time)