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

Network Explorer Part 2 #1545

Merged
merged 14 commits into from
Apr 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions arkane/data/methoxy_explore.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,7 @@

explorer(
source=['methoxy'],
explore_tol=(1e-2,'s'),
explore_tol=0.01,
energy_tol=4.5e1,
flux_tol=1e-10,
)

207 changes: 117 additions & 90 deletions arkane/explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis
mmol = None
for spc in self.source:
if mmol:
mmol.merge(spc.molecule[0])
mmol = mmol.merge(spc.molecule[0])
else:
mmol = spc.molecule[0]
mmol = spc.molecule[0].copy(deep=True)

form = mmol.getFormula()

Expand All @@ -143,26 +143,36 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis
self.source[i] = spc

# react initial species
flags = np.array([s.molecule[0].getFormula()==form for s in reaction_model.core.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=np.zeros((len(reaction_model.core.species),len(reaction_model.core.species))))

# find the network we're interested in
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
network = nwk
break
else:
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.')

network.bathGas = self.bathGas
self.network = network
for network in networks:
network.bathGas = self.bathGas

self.networks = networks

# determine T and P combinations

if self.pdepjob.Tlist:
Expand All @@ -184,88 +194,105 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis
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)
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:
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'))

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

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)
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)

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)
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)
10 changes: 5 additions & 5 deletions arkane/explorerTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,21 @@ def test_reactions(self):
"""
test that the right number of reactions are in output network
"""
self.assertEqual(len(self.explorerjob.network.pathReactions),6)
self.assertEqual(len(self.explorerjob.networks[0].pathReactions),6)

def test_isomers(self):
"""
test that the right number of isomers are in the output network
"""
self.assertEqual(len(self.explorerjob.network.isomers),2)
self.assertEqual(len(self.explorerjob.networks[0].isomers),2)

def test_job_rxns(self):
"""
test that in this case all the reactions in the job
ended up in the final network
"""
for rxn in self.explorerjob.jobRxns:
self.assertIn(rxn,self.explorerjob.network.pathReactions)
self.assertIn(rxn,self.explorerjob.networks[0].pathReactions)


if __name__ == '__main__':
Expand Down
12 changes: 5 additions & 7 deletions arkane/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,25 +497,23 @@ def pressureDependence(label,
jobList.append(job)


def explorer(source, explore_tol=(0.01,'s^-1'), energy_tol=np.inf, flux_tol=0.0, bathGas=None, maximumRadicalElectrons=np.inf):
def explorer(source, explore_tol=0.01, energy_tol=np.inf, flux_tol=0.0, bathGas=None, maximumRadicalElectrons=np.inf):
global jobList,speciesDict
for job in jobList:
if isinstance(job, PressureDependenceJob):
pdepjob = job
break
else:
raise InputError('the explorer block must occur after the pressureDependence block')

source = [speciesDict[name] for name in source]

explore_tol = Quantity(explore_tol)


if bathGas:
bathGas0 = bathGas or {}; bathGas = {}
for spec, fraction in bathGas0.items():
bathGas[speciesDict[spec]] = fraction
job = ExplorerJob(source=source,pdepjob=pdepjob,explore_tol=explore_tol.value_si,

job = ExplorerJob(source=source,pdepjob=pdepjob,explore_tol=explore_tol,
energy_tol=energy_tol,flux_tol=flux_tol,bathGas=bathGas, maximumRadicalElectrons=maximumRadicalElectrons)
jobList.append(job)

Expand Down
6 changes: 4 additions & 2 deletions arkane/mainTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"""

import unittest
import logging
import os
import shutil
from nose.plugins.attrib import attr
Expand All @@ -53,15 +54,16 @@ def setUpClass(cls):
"""A function that is run ONCE before all unit tests in this class."""
cls.base_path = os.path.join(os.path.dirname(os.path.dirname(rmgpy.__file__)), 'examples', 'arkane')
cls.failed = []
cls.example_types = ['species', 'reactions', 'networks']
cls.example_types = ['species', 'reactions', 'explorer', 'networks']

def test_arkane_examples(self):
for example_type in self.example_types:
example_type_path = os.path.join(self.base_path, example_type)
for example in os.listdir(example_type_path):
for example in sorted(os.listdir(example_type_path)):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check whether #1565 solved this problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't solve the issue

path = os.path.join(example_type_path, example)
arkane = Arkane(inputFile=os.path.join(path, 'input.py'), outputDirectory=path)
arkane.plot = True
logging.info("running {}".format(example))
arkane.execute()
with open(os.path.join(path, 'arkane.log'), 'r') as f:
log = f.readlines()
Expand Down
19 changes: 18 additions & 1 deletion arkane/pdep.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,24 @@ def save(self, outputFile):
Plist = self.Plist.value_si
Tcount = Tlist.shape[0]
Pcount = Plist.shape[0]


f.write("#Thermo used: \n")
spcs = []
for rxn in self.network.pathReactions:
spcs.extend(rxn.reactants)
spcs.extend(rxn.products)
for spc in spcs:
if spc.thermo:
f.write("#"+spc.label+" SMILES: "+spc.molecule[0].toSMILES()+"\n")
f.write("#"+spc.thermo.comment+"\n")
f.write("\n#Path Reactions used: \n")
for rxn in self.network.pathReactions:
if rxn.kinetics:
f.write("#"+str(rxn)+"\n")
for s in rxn.kinetics.comment.split("\n"):
f.write("#"+s+"\n")
f.write("\n")

count = 0
printed_reactions = [] # list of rxns already printed
for prod in range(Nprod):
Expand Down
7 changes: 3 additions & 4 deletions documentation/source/users/arkane/input_explorer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pressure-dependent calculation input file you append an explorer block. For exa

explorer(
source=['methoxy'],
explore_tol=(1e-2,'s^-1'),
explore_tol=0.01,
energy_tol=1e4,
flux_tol=1e-6,
)
Expand All @@ -23,11 +23,10 @@ source channel. The network is expanded starting from this starting isomer/chan
Network Exploration
===================

The ``explore_tol`` is largest acceptable total rate at which isomers inside the network isomerize to become
isomers that are not part of the network. Network expansion is done starting from just the network source using
The ``explore_tol`` is a fraction of the flux from all net reactions from the source to the other channels in the network. Network expansion is done starting from just the network source using
values from the rest of the Arkane job when available, otherwise from RMG. It cycles through all of the
temperature and pressure points specified for fitting in the pressure dependence job and checks the total network
leak rate at each one. Whenever this rate is greater than ``explore_tol`` the outside isomer with the most leak is
leak rate at each one. Whenever this rate is greater than ``explore_tol*kchar``, where ``kchar`` is the total flux from all net reactions away from the source, the outside isomer with the most leak is
added to the network and reacted and the loop is flagged to cycle through all of the temperatures and pressures
again. Once this loop is finished a network_full.py file is generated in the pdep directory that has the full
explored network.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@

explorer(
source=['methoxy'],
explore_tol=(1e-2,'s^-1'),
explore_tol=0.01,
energy_tol=8e1,
flux_tol=1e-6,
bathGas={'He':1.0},
Expand Down
Loading