Skip to content

Commit 85cf644

Browse files
committed
Reducing code duplication by passing in failsSpeciesConstraints.
Now passes species constraints function through to family reaction generation routines instead of running checks in multiple locations.
1 parent 6a000f7 commit 85cf644

File tree

3 files changed

+35
-59
lines changed

3 files changed

+35
-59
lines changed

rmgpy/data/kinetics/__init__.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -332,18 +332,18 @@ def saveOld(self, path):
332332
onoff = 'on ' if self.recommendedFamilies[label] else 'off'
333333
f.write("{num:<2d} {onoff} {label}\n".format(num=number, label=label, onoff=onoff))
334334

335-
def generateReactions(self, reactants, products=None, **options):
335+
def generateReactions(self, reactants, products=None, failsSpeciesConstraints=None):
336336
"""
337337
Generate all reactions between the provided list of one or two
338338
`reactants`, which should be :class:`Molecule` objects. This method
339339
searches the depository, libraries, and groups, in that order.
340340
"""
341341
reactionList = []
342-
reactionList.extend(self.generateReactionsFromLibraries(reactants, products, **options))
343-
reactionList.extend(self.generateReactionsFromFamilies(reactants, products, **options))
342+
reactionList.extend(self.generateReactionsFromLibraries(reactants, products, failsSpeciesConstraints=failsSpeciesConstraints))
343+
reactionList.extend(self.generateReactionsFromFamilies(reactants, products, failsSpeciesConstraints=failsSpeciesConstraints))
344344
return reactionList
345345

346-
def generateReactionsFromLibraries(self, reactants, products, **options):
346+
def generateReactionsFromLibraries(self, reactants, products, failsSpeciesConstraints=None):
347347
"""
348348
Generate all reactions between the provided list of one or two
349349
`reactants`, which should be :class:`Molecule` objects. This method
@@ -353,11 +353,10 @@ def generateReactionsFromLibraries(self, reactants, products, **options):
353353
for label, libraryType in self.libraryOrder:
354354
# Generate reactions from reaction libraries (no need to generate them from seeds)
355355
if libraryType == "Reaction Library":
356-
print 'generating reactions from library {0}'.format(label)
357-
reactionList.extend(self.generateReactionsFromLibrary(reactants, products, self.libraries[label], **options))
356+
reactionList.extend(self.generateReactionsFromLibrary(reactants, products, self.libraries[label], failsSpeciesConstraints=failsSpeciesConstraints))
358357
return reactionList
359358

360-
def generateReactionsFromLibrary(self, reactants, products, library, **options):
359+
def generateReactionsFromLibrary(self, reactants, products, library, failsSpeciesConstraints=None):
361360
"""
362361
Generate all reactions between the provided list of one or two
363362
`reactants`, which should be :class:`Molecule` objects. This method
@@ -380,7 +379,7 @@ def generateReactionsFromLibrary(self, reactants, products, library, **options):
380379
reactionList = filterReactions(reactants, products, reactionList)
381380
return reactionList
382381

383-
def generateReactionsFromFamilies(self, reactants, products, only_families=None, **options):
382+
def generateReactionsFromFamilies(self, reactants, products, only_families=None, failsSpeciesConstraints=None):
384383
"""
385384
Generate all reactions between the provided list of one or two
386385
`reactants`, which should be :class:`Molecule` objects. This method
@@ -397,7 +396,7 @@ def generateReactionsFromFamilies(self, reactants, products, only_families=None,
397396
reactionList = []
398397
for label, family in self.families.iteritems():
399398
if only_families is None or label in only_families:
400-
reactionList.extend(family.generateReactions(reactants, **options))
399+
reactionList.extend(family.generateReactions(reactants, failsSpeciesConstraints=failsSpeciesConstraints))
401400
if products:
402401
reactionList = filterReactions(reactants, products, reactionList)
403402
return reactionList

rmgpy/data/kinetics/family.py

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,7 @@ def applyRecipe(self, reactantStructures, forward=True, unique=True):
10781078
# Return the product structures
10791079
return productStructures
10801080

1081-
def __generateProductStructures(self, reactantStructures, maps, forward, **options):
1081+
def __generateProductStructures(self, reactantStructures, maps, forward, failsSpeciesConstraints=None):
10821082
"""
10831083
For a given set of `reactantStructures` and a given set of `maps`,
10841084
generate and return the corresponding product structures. The
@@ -1087,6 +1087,8 @@ def __generateProductStructures(self, reactantStructures, maps, forward, **optio
10871087
parameter is a list of mappings of the top-level tree node of each
10881088
*template* reactant to the corresponding *structure*. This function
10891089
returns a list of the product structures.
1090+
`failsSpeciesConstraints` is a function that accepts a :class:`Molecule`
1091+
structure and returns `True` if it is forbidden.
10901092
"""
10911093

10921094
productStructuresList = []
@@ -1125,41 +1127,11 @@ def __generateProductStructures(self, reactantStructures, maps, forward, **optio
11251127
productStructures.reverse()
11261128

11271129
# Apply the generated species constraints (if given)
1128-
if options:
1129-
explicitlyAllowedMolecules = options.get('explicitlyAllowedMolecules')
1130-
maxCarbonAtoms = options.get('maximumCarbonAtoms', 1000000)
1131-
maxHydrogenAtoms = options.get('maximumHydrogenAtoms', 1000000)
1132-
maxOxygenAtoms = options.get('maximumOxygenAtoms', 1000000)
1133-
maxNitrogenAtoms = options.get('maximumNitrogenAtoms', 1000000)
1134-
maxSiliconAtoms = options.get('maximumSiliconAtoms', 1000000)
1135-
maxSulfurAtoms = options.get('maximumSulfurAtoms', 1000000)
1136-
maxHeavyAtoms = options.get('maximumHeavyAtoms', 1000000)
1137-
maxRadicals = options.get('maximumRadicalElectrons', 1000000)
1130+
if failsSpeciesConstraints:
11381131
for struct in productStructures:
1139-
allowed = False
1140-
for molecule in explicitlyAllowedMolecules:
1141-
if struct.isIsomorphic(molecule):
1142-
allowed = True
1143-
break
1144-
if allowed:
1145-
continue
1146-
H = struct.getNumAtoms('H')
1147-
if struct.getNumAtoms('C') > maxCarbonAtoms:
1148-
raise ForbiddenStructureException()
1149-
if H > maxHydrogenAtoms:
1150-
raise ForbiddenStructureException()
1151-
if struct.getNumAtoms('O') > maxOxygenAtoms:
1152-
raise ForbiddenStructureException()
1153-
if struct.getNumAtoms('N') > maxNitrogenAtoms:
1154-
raise ForbiddenStructureException()
1155-
if struct.getNumAtoms('Si') > maxSiliconAtoms:
1156-
raise ForbiddenStructureException()
1157-
if struct.getNumAtoms('S') > maxSulfurAtoms:
1158-
raise ForbiddenStructureException()
1159-
if len(struct.atoms) - H > maxHeavyAtoms:
1160-
raise ForbiddenStructureException()
1161-
if (struct.getNumberOfRadicalElectrons() > maxRadicals) and (len(struct.atoms) - H > 1):
1162-
raise ForbiddenStructureException()
1132+
if failsSpeciesConstraints(struct):
1133+
raise ForbiddenStructureException()
1134+
11631135

11641136
# Generate other possible electronic states
11651137
electronicStructuresList1 = []
@@ -1397,7 +1369,7 @@ def __matchReactantToTemplate(self, reactant, templateReactant):
13971369
elif isinstance(struct, Group):
13981370
return reactant.findSubgraphIsomorphisms(struct)
13991371

1400-
def generateReactions(self, reactants, **options):
1372+
def generateReactions(self, reactants, failsSpeciesConstraints=None):
14011373
"""
14021374
Generate all reactions between the provided list of one or two
14031375
`reactants`, which should be either single :class:`Molecule` objects
@@ -1410,12 +1382,12 @@ def generateReactions(self, reactants, **options):
14101382
reactionList = []
14111383

14121384
# Forward direction (the direction in which kinetics is defined)
1413-
reactionList.extend(self.__generateReactions(reactants, forward=True, **options))
1385+
reactionList.extend(self.__generateReactions(reactants, forward=True, failsSpeciesConstraints=failsSpeciesConstraints))
14141386

14151387
if self.ownReverse:
14161388
# for each reaction, make its reverse reaction and store in a 'reverse' attribute
14171389
for rxn in reactionList:
1418-
reactions = self.__generateReactions(rxn.products, products=rxn.reactants, forward=True, **options)
1390+
reactions = self.__generateReactions(rxn.products, products=rxn.reactants, forward=True, failsSpeciesConstraints=failsSpeciesConstraints)
14191391
if len(reactions) != 1:
14201392
logging.error("Expecting one matching reverse reaction, not {0} in reaction family {1} for forward reaction {2}.\n".format(len(reactions), self.label, str(rxn)))
14211393
for reactant in rxn.reactants:
@@ -1429,7 +1401,7 @@ def generateReactions(self, reactants, **options):
14291401

14301402
else: # family is not ownReverse
14311403
# Reverse direction (the direction in which kinetics is not defined)
1432-
reactionList.extend(self.__generateReactions(reactants, forward=False, **options))
1404+
reactionList.extend(self.__generateReactions(reactants, forward=False, failsSpeciesConstraints=failsSpeciesConstraints))
14331405

14341406
# Return the reactions as containing Species objects, not Molecule objects
14351407
for reaction in reactionList:
@@ -1458,14 +1430,16 @@ def calculateDegeneracy(self, reaction):
14581430
raise Exception('Unable to calculate degeneracy for reaction {0} in reaction family {1}.'.format(reaction, self.label))
14591431
return reactions[0].degeneracy
14601432

1461-
def __generateReactions(self, reactants, products=None, forward=True, **options):
1433+
def __generateReactions(self, reactants, products=None, forward=True, failsSpeciesConstraints=None):
14621434
"""
14631435
Generate a list of all of the possible reactions of this family between
14641436
the list of `reactants`. The number of reactants provided must match
14651437
the number of reactants expected by the template, or this function
14661438
will return an empty list. Each item in the list of reactants should
14671439
be a list of :class:`Molecule` objects, each representing a resonance
14681440
isomer of the species of interest.
1441+
`failsSpeciesConstraints` is an optional function that accepts a :class:`Molecule`
1442+
structure and returns `True` if it is forbidden.
14691443
"""
14701444

14711445
rxnList = []; speciesList = []
@@ -1501,7 +1475,7 @@ def __generateReactions(self, reactants, products=None, forward=True, **options)
15011475
for map in mappings:
15021476
reactantStructures = [molecule]
15031477
try:
1504-
productStructuresList = self.__generateProductStructures(reactantStructures, [map], forward, **options)
1478+
productStructuresList = self.__generateProductStructures(reactantStructures, [map], forward, failsSpeciesConstraints=failsSpeciesConstraints)
15051479
except ForbiddenStructureException:
15061480
pass
15071481
else:
@@ -1529,7 +1503,7 @@ def __generateReactions(self, reactants, products=None, forward=True, **options)
15291503
for mapB in mappingsB:
15301504
reactantStructures = [moleculeA, moleculeB]
15311505
try:
1532-
productStructuresList = self.__generateProductStructures(reactantStructures, [mapA, mapB], forward, **options)
1506+
productStructuresList = self.__generateProductStructures(reactantStructures, [mapA, mapB], forward, failsSpeciesConstraints=failsSpeciesConstraints)
15331507
except ForbiddenStructureException:
15341508
pass
15351509
else:
@@ -1550,7 +1524,7 @@ def __generateReactions(self, reactants, products=None, forward=True, **options)
15501524
for mapB in mappingsB:
15511525
reactantStructures = [moleculeA, moleculeB]
15521526
try:
1553-
productStructuresList = self.__generateProductStructures(reactantStructures, [mapA, mapB], forward, **options)
1527+
productStructuresList = self.__generateProductStructures(reactantStructures, [mapA, mapB], forward, failsSpeciesConstraints=failsSpeciesConstraints)
15541528
except ForbiddenStructureException:
15551529
pass
15561530
else:

rmgpy/rmg/model.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -603,15 +603,14 @@ def react(self, database, speciesA, speciesB=None):
603603
Generates reactions involving :class:`rmgpy.species.Species` speciesA and speciesB.
604604
"""
605605
reactionList = []
606-
options = self.speciesConstraints
607606
if speciesB is None:
608607
for moleculeA in speciesA.molecule:
609-
reactionList.extend(database.kinetics.generateReactions([moleculeA], **options))
608+
reactionList.extend(database.kinetics.generateReactions([moleculeA], failsSpeciesConstraints=self.failsSpeciesConstraints))
610609
moleculeA.clearLabeledAtoms()
611610
else:
612611
for moleculeA in speciesA.molecule:
613612
for moleculeB in speciesB.molecule:
614-
reactionList.extend(database.kinetics.generateReactions([moleculeA, moleculeB], **options))
613+
reactionList.extend(database.kinetics.generateReactions([moleculeA, moleculeB], failsSpeciesConstraints=self.failsSpeciesConstraints))
615614
moleculeA.clearLabeledAtoms()
616615
moleculeB.clearLabeledAtoms()
617616
return reactionList
@@ -1706,10 +1705,10 @@ def saveChemkinFile(self, path, verbose_path, dictionaryPath=None, transportPath
17061705

17071706
def failsSpeciesConstraints(self, species):
17081707
"""
1709-
Checks whether the species passes the speciesConstraints set by the user. If not,
1710-
returns `True` for failing speciesConstraints.
1708+
Pass in either a `Species` or `Molecule` object and checks whether it passes
1709+
the speciesConstraints set by the user. If not, returns `True` for failing speciesConstraints.
17111710
"""
1712-
explicitlyAllowedMolecules = self.speciesConstraints.get('explicitlyAllowedMolecules')
1711+
explicitlyAllowedMolecules = self.speciesConstraints.get('explicitlyAllowedMolecules', [])
17131712
maxCarbonAtoms = self.speciesConstraints.get('maximumCarbonAtoms', 1000000)
17141713
maxHydrogenAtoms = self.speciesConstraints.get('maximumHydrogenAtoms', 1000000)
17151714
maxOxygenAtoms = self.speciesConstraints.get('maximumOxygenAtoms', 1000000)
@@ -1719,7 +1718,11 @@ def failsSpeciesConstraints(self, species):
17191718
maxHeavyAtoms = self.speciesConstraints.get('maximumHeavyAtoms', 1000000)
17201719
maxRadicals = self.speciesConstraints.get('maximumRadicalElectrons', 1000000)
17211720

1722-
struct = species.molecule[0]
1721+
if isinstance(species, rmgpy.species.Species):
1722+
struct = species.molecule[0]
1723+
else:
1724+
# expects a molecule here
1725+
struct = species
17231726
for molecule in explicitlyAllowedMolecules:
17241727
if struct.isIsomorphic(molecule):
17251728
return False

0 commit comments

Comments
 (0)