Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change input file's behavior of loading families through kineticFamilies field. #195

Merged
merged 9 commits into from
Apr 3, 2014
2 changes: 1 addition & 1 deletion examples/rmg/minimal/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
reactionLibraries = [],
seedMechanisms = [],
kineticsDepositories = ['training'],
kineticsFamilies = ['!Intra_Disproportionation','!Substitution_O'],
kineticsFamilies = 'default',
kineticsEstimator = 'rate rules',
)

Expand Down
92 changes: 74 additions & 18 deletions rmgpy/data/kinetics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from rmgpy.molecule import Molecule, Group
from rmgpy.species import Species
from rmgpy.reaction import Reaction
from rmgpy.data.base import LogicNode
from rmgpy.data.base import LogicNode, DatabaseError

from .common import KineticsError, saveEntry
from .depository import DepositoryReaction, KineticsDepository
Expand All @@ -59,6 +59,7 @@ class KineticsDatabase(object):
"""

def __init__(self):
self.recommendedFamilies = {}
self.families = {}
self.libraries = {}
self.libraryOrder = []
Expand Down Expand Up @@ -101,34 +102,88 @@ def load(self, path, families=None, libraries=None, depositories=None):
Load the kinetics database from the given `path` on disk, where `path`
points to the top-level folder of the families database.
"""
self.loadRecommendedFamiliesList(os.path.join(path, 'families', 'recommended.py')),
self.loadFamilies(os.path.join(path, 'families'), families, depositories)
self.loadLibraries(os.path.join(path, 'libraries'), libraries)

def loadRecommendedFamiliesList(self, filepath):
"""
Load the list of recommended families from the given file

The file is usually 'kinetics/families/recommended.py'.
This is stored as a dictionary of True or False values (checked here),
and should contain entries for every available family (checked in loadFamilies).
"""
try:
global_context = {}
global_context['__builtins__'] = None
global_context['True'] = True
global_context['False'] = False
local_context = {}
local_context['__builtins__'] = None
f = open(filepath, 'r')
exec f in global_context, local_context
f.close()
self.recommendedFamilies = local_context['recommendedFamilies']
except:
raise DatabaseError('Error while reading list of recommended families from {0}/recommended.py.'.format(path))
for recommended in self.recommendedFamilies.values():
if not isinstance(recommended, bool):
raise DatabaseError("recommendedFamilies dictionary should contain only True or False values")

def loadFamilies(self, path, families=None, depositories=None):
"""
Load the kinetics families from the given `path` on disk, where `path`
points to the top-level folder of the kinetics families.
"""
logging.info('Loading kinetics families from {0}'.format(path))

familiesToLoad = []
for (root, dirs, files) in os.walk(os.path.join(path)):
if root == path:
if families is None or families == 'all':
# All families are loaded by default
for d in dirs:
break # all we wanted was the list of dirs in the base path

if families == 'default':
logging.info('Loading default kinetics families from {0}'.format(path))
for d in dirs: # load them in directory listing order, like other methods (better than a random dict order)
try:
recommended = self.recommendedFamilies[d]
except KeyError:
raise DatabaseError('Family {0} not found in recommendation list (probably at {1}/recommended.py)'.format(d, path))
if recommended:
familiesToLoad.append(d)
for label, value in self.recommendedFamilies.iteritems():
if label not in dirs:
raise DatabaseError('Family {0} found (in {1}/recommended.py) not found on disk.'.format(label, path))

elif families == 'all':
# All families are loaded
logging.info('Loading all of the kinetics families from {0}'.format(path))
for d in dirs:
familiesToLoad.append(d)
elif families == 'none':
logging.info('Not loading any of the kinetics families from {0}'.format(path))
# Don't load any of the families
familiesToLoad = []
elif isinstance(families, list):
logging.info('Loading the user-specified kinetics families from {0}'.format(path))
# If all items in the list start with !, all families will be loaded except these
if len(families) == 0:
raise DatabaseError('Kinetics families should be a non-empty list, or set to `default`, `all`, or `none`.')
elif all([label.startswith('!') for label in families]):
for d in dirs:
if '!{0}'.format(d) not in families:
familiesToLoad.append(d)
elif any([label.startswith('!') for label in families]):
raise DatabaseError('Families list must either all or none have prefix "!", but not a mix.')
else: # only the families given will be loaded
for d in dirs:
if d in families:
familiesToLoad.append(d)
elif isinstance(families, list) or isinstance(families, tuple):
# If all items in the list start with !, all families will be loaded except these
if all([label.startswith('!') for label in families]):
for d in dirs:
if '!{0}'.format(d) not in families:
familiesToLoad.append(d)
# Otherwise only the families given will be loaded
else:
for d in dirs:
if d in families:
familiesToLoad.append(d)
for label in families:
if label not in dirs:
raise DatabaseError('Family {0} not found on disk.'.format(label))
else:
raise DatabaseError('Kinetics families was not specified properly. Should be set to `default`,`all`,`none`, or a list.')

# Now we know what families to load, so let's load them
self.families = {}
Expand Down Expand Up @@ -265,14 +320,15 @@ def saveOld(self, path):
// Families can be deactivated by simply changing the "on/off" column to off.
//
// This file was generated by exporting the entirety of RMG-Py,
// so has all families on by default. You may want to turn some off.
// so the on/off decisions come from the defaults there.
//
////////////////////////////////////////////////////////////////////////////////

// No. on/off Forward reaction
""")
for number, label in enumerate(sorted(self.families.keys())):
f.write("{num:<2d} on {label}\n".format(num=number, label=label))
onoff = 'on ' if self.recommendedFamilies[label] else 'off'
f.write("{num:<2d} {onoff} {label}\n".format(num=number, label=label, onoff=onoff))

def generateReactions(self, reactants, products=None, **options):
"""
Expand Down
26 changes: 26 additions & 0 deletions rmgpy/data/kinetics/kineticsTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
import unittest
from external.wip import work_in_progress

from rmgpy import settings
from rmgpy.data.kinetics import *
from rmgpy.data.base import DatabaseError
###################################################

class TestKineticsDatabase(unittest.TestCase):

def testLoadFamilies(self):
"""
Test that the loadFamilies function raises the correct exceptions
"""
path = os.path.join(settings['database.directory'],'kinetics','families')
database = KineticsDatabase()

with self.assertRaises(DatabaseError):
database.loadFamilies(path, families='random')
with self.assertRaises(DatabaseError):
database.loadFamilies(path, families=['!H_Abstraction','Disproportionation'])
with self.assertRaises(DatabaseError):
database.loadFamilies(path, families=['fake_family'])
with self.assertRaises(DatabaseError):
database.loadFamilies(path, families=[])
8 changes: 3 additions & 5 deletions rmgpy/rmg/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,10 @@ def database(
else:
assert isinstance(kineticsDepositories,list), "kineticsDepositories should be either 'default', 'all', or a list of names eg. ['training','PrIMe']."
rmg.kineticsDepositories = kineticsDepositories
if kineticsFamilies == 'default':
pass
elif kineticsFamilies == 'all':
pass
if kineticsFamilies in ('default', 'all', 'none'):
rmg.kineticsFamilies = kineticsFamilies
else:
assert isinstance(kineticsFamilies,list), "kineticsFamilies should be either 'default', 'all', or a list of names eg. ['H_Abstraction','!Intra_Disproportionation']."
assert isinstance(kineticsFamilies,list), "kineticsFamilies should be either 'default', 'all', 'none', or a list of names eg. ['H_Abstraction','R_Recombination'] or ['!Intra_Disproportionation']."
rmg.kineticsFamilies = kineticsFamilies

def species(label, structure, reactive=True):
Expand Down