Skip to content

Commit

Permalink
Merge pull request #1647 from ReactionMechanismGenerator/pdepnetworkgen3
Browse files Browse the repository at this point in the history
Automatic Network Generation 3
  • Loading branch information
alongd authored Aug 4, 2019
2 parents 589ba9a + 48f12c8 commit d66fbfe
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 81 deletions.
2 changes: 1 addition & 1 deletion arkane/data/methoxy_explore.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,5 +238,5 @@
source=['methoxy'],
explore_tol=0.01,
energy_tol=4.5e1,
flux_tol=1e-10,
flux_tol=1e-15,
)
53 changes: 33 additions & 20 deletions arkane/explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis

forbiddenStructures = getDB('forbidden')
incomplete = True
checkedSpecies = []

while incomplete:
incomplete = False
Expand All @@ -214,6 +215,15 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis
for network in self.networks:
# compute the characteristic rate coefficient by summing all rate coefficients
# from the reactant channel
for spc in reaction_model.edge.species:
if spc in checkedSpecies:
continue
if forbiddenStructures.isMoleculeForbidden(spc.molecule[0]):
reaction_model.removeSpeciesFromEdge(reaction_model.reactionSystems, spc)
reaction_model.removeEmptyPdepNetworks()
else:
checkedSpecies.append(spc)

kchar = 0.0
for rxn in network.netReactions: # reaction_model.core.reactions+reaction_model.edge.reactions:
if (set(rxn.reactants) == set(self.source)
Expand All @@ -227,21 +237,14 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis
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,
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,
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:
Expand Down Expand Up @@ -279,6 +282,7 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis
if self.energy_tol != np.inf or self.flux_tol != 0.0:

rxnSet = None
productSet = None

for T in Tlist:
if self.energy_tol != np.inf:
Expand All @@ -290,17 +294,26 @@ def execute(self, outputFile, plot, format='pdf', print_summary=True, speciesLis

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)
products = network.get_rate_filtered_products(T, P, self.flux_tol)
products = [tuple(x) for x in products]
if productSet is not None:
productSet &= set(products)
else:
rxnSet = set(rxns)
productSet = set(products)


logging.info('removing reactions during reduction:')
for rxn in rxnSet:
logging.info(rxn)
if rxnSet:
logging.info('removing reactions during reduction:')
for rxn in rxnSet:
logging.info(rxn)
rxnSet = list(rxnSet)
if productSet:
logging.info('removing products during reduction:')
for prod in productSet:
logging.info([x.label for x in prod])
productSet = list(productSet)

network.remove_reactions(reaction_model, list(rxnSet))
network.remove_reactions(reaction_model, rxns=rxnSet, prods=productSet)

for rxn in jobRxns:
if rxn not in network.pathReactions:
Expand Down
174 changes: 123 additions & 51 deletions rmgpy/rmg/pdep.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def addPathReaction(self, newReaction):
self.pathReactions.append(newReaction)
self.invalidate()

def get_energy_filtered_reactions(self,T,tol):
def get_energy_filtered_reactions(self, T, tol):
"""
Returns a list of products and isomers that are greater in Free Energy
than a*R*T + Gfsource(T)
Expand Down Expand Up @@ -354,33 +354,43 @@ def get_energy_filtered_reactions(self,T,tol):

return filtered_rxns

def get_rate_filtered_reactions(self,T,P,tol):
def get_rate_filtered_products(self, T, P, tol):
"""
determines the set of pathReactions that have fluxes less than
tol at steady state where all A => B + C reactions are irreversible
and there is a constant flux from/to the source configuration of 1.0
"""
c = self.solve_SS_network(T,P)
isomerSpcs = [iso.species[0] for iso in self.isomers]
filtered_rxns = []
for rxn in self.pathReactions:
val = 0.0
val2 = 0.0
if rxn.reactants[0] in isomerSpcs:
ind = isomerSpcs.index(rxn.reactants[0])
kf = rxn.getRateCoefficient(T,P)
val = kf*c[ind]
if rxn.products[0] in isomerSpcs:
ind2 = isomerSpcs.index(rxn.products[0])
kr = rxn.getRateCoefficient(T,P)/rxn.getEquilibriumConstant(T)
val2 = kr*c[ind2]

if max(val,val2) < tol:
filtered_rxns.append(rxn)

return filtered_rxns

def solve_SS_network(self,T,P):
filtered_prod = []
if c is not None:
for rxn in self.netReactions:
val = 0.0
val2 = 0.0
if rxn.reactants[0] in isomerSpcs:
ind = isomerSpcs.index(rxn.reactants[0])
kf = rxn.getRateCoefficient(T,P)
val = kf*c[ind]
if rxn.products[0] in isomerSpcs:
ind2 = isomerSpcs.index(rxn.products[0])
kr = rxn.getRateCoefficient(T,P)/rxn.getEquilibriumConstant(T)
val2 = kr*c[ind2]

if max(val,val2) < tol:
filtered_prod.append(rxn.products)

return filtered_prod

else:
logging.warn("Falling back flux reduction from Steady State analysis to rate coefficient analysis")
ks = np.array([rxn.getRateCoefficient(T,P) for rxn in self.netReactions])
frs = ks/ks.sum()
inds = [i for i in xrange(len(frs)) if frs[i] < tol]
filtered_prod = [self.netReactions[i].products for i in inds]
return filtered_prod


def solve_SS_network(self, T, P):
"""
calculates the steady state concentrations if all A => B + C
reactions are irreversible and the flux from/to the source
Expand All @@ -392,8 +402,8 @@ def solve_SS_network(self,T,P):

isomerSpcs = [iso.species[0] for iso in self.isomers]

for rxn in self.pathReactions:

for rxn in self.netReactions:
if rxn.reactants[0] in isomerSpcs:
ind = isomerSpcs.index(rxn.reactants[0])
kf = rxn.getRateCoefficient(T,P)
Expand All @@ -406,16 +416,16 @@ def solve_SS_network(self,T,P):
A[ind2,ind2] -= kr
else:
ind2 = None
if ind and ind2:

if ind is not None and ind2 is not None:
A[ind,ind2] += kr
A[ind2,ind] += kf

if bimolecular:
if rxn.reactants[0].species == self.source:
if rxn.reactants[0] == self.source:
kf = rxn.getRateCoefficient(T,P)
b[ind2] += kf
elif rxn.products[0].species == self.source:
elif rxn.products[0] == self.source:
kr = rxn.getRateCoefficient(T,P)/rxn.getEquilibriumConstant(T)
b[ind] += kr

Expand All @@ -429,20 +439,27 @@ def solve_SS_network(self,T,P):
if len(b) == 1:
return np.array([b[0]/A[0,0]])

con = np.linalg.cond(A) #this matrix can be very ill-conditioned so we enhance precision accordingly
mp.dps = 30+int(np.log10(con))
Amp = mp.matrix(A.tolist())
bmp = mp.matrix(b.tolist())

c = mp.qr_solve(Amp,bmp)
con = np.linalg.cond(A)

c = np.array(list(c[0]))

if any(c<=0.0):
c, rnorm = opt.nnls(A,b)
if np.log10(con) < 15:
c = np.linalg.solve(A,b)
else:
logging.warn("Matrix Ill-conditioned, attempting to use Arbitrary Precision Arithmetic")
mp.dps = 30+int(np.log10(con))
Amp = mp.matrix(A.tolist())
bmp = mp.matrix(b.tolist())

c = c.astype(np.float64)

try:
c = mp.qr_solve(Amp,bmp)

c = np.array(list(c[0]))

if any(c<=0.0):
c, rnorm = opt.nnls(A,b)

c = c.astype(np.float64)
except: #fall back to raw flux analysis rather than solve steady state problem
return None
return c


Expand Down Expand Up @@ -472,33 +489,88 @@ def remove_disconnected_reactions(self):
logging.info('Removing rxn: {}'.format(rxn))
self.pathReactions.remove(rxn)

nrxns = []
for nrxn in self.netReactions:
if nrxn.products not in keptProducts or nrxn.reactants not in keptProducts:
logging.info('Removing net rxn: {}'.format(nrxn))
else:
logging.info('Keeping net rxn: {}'.format(nrxn))
nrxns.append(nrxn)
self.netReactions = nrxns

prods = []
for prod in self.products:
if prod.species not in keptProducts:
logging.info('Removing product: {}'.format(prod))
self.products.remove(prod)

else:
logging.info("Keeping product: {}".format(prod))
prods.append(prod)

self.products = prods

rcts = []
for rct in self.reactants:
if rct.species not in keptProducts:
logging.info('Removing product: {}'.format(rct))
self.reactants.remove(react)

else:
logging.info("Keeping product: {}".format(rct))
rcts.append(rct)
self.reactants = rcts

isos = []
for iso in self.isomers:
if iso.species not in keptProducts:
logging.info('Removing isomer: {}'.format(iso))
self.isomers.remove(iso)
if iso in self.explored:
self.explored.remove(iso)
else:
logging.info("Keeping isomer: {}".format(iso))
isos.append(iso)

self.isomers = isos
self.explored = [iso.species[0] for iso in isos]

def remove_reactions(self,reactionModel,rxns):
self.Nisom = len(self.isomers)
self.Nreac = len(self.reactants)
self.Nprod = len(self.products)

def remove_reactions(self, reactionModel, rxns=None, prods=None):
"""
removes a list of reactions from the network and all reactions/products
left disconnected by removing those reactions
"""
for rxn in rxns:
self.pathReactions.remove(rxn)

if rxns:
for rxn in rxns:
self.pathReactions.remove(rxn)

if prods:
isomers = [x.species[0] for x in self.isomers]

for prod in prods:
prod = [x for x in prod]
if prod[0] in isomers: #skip isomers
continue
for rxn in self.pathReactions:
if rxn.products == prod or rxn.reactants == prod:
self.pathReactions.remove(rxn)

prodspc = [x[0] for x in prods]
for prod in prods:
prod = [x for x in prod]
if prod[0] in isomers: #deal with isomers
for rxn in self.pathReactions:
if rxn.reactants == prod and rxn.products[0] not in isomers and rxn.products[0] not in prodspc:
break
if rxn.products == prod and rxn.reactants[0] not in isomers and rxn.reactants not in prodspc:
break
else:
for rxn in self.pathReactions:
if rxn.reactants == prod or rxn.products == prod:
self.pathReactions.remove(rxn)


self.remove_disconnected_reactions()


self.cleanup()

self.invalidate()

assert self.pathReactions != [], 'Reduction process removed all reactions, cannot update network with no reactions'
Expand Down
13 changes: 4 additions & 9 deletions rmgpy/rmg/pdepTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,14 @@ def setUp(self):
self.pdepnetwork.index = 1
self.pdepnetwork.explored = []


def test_SS_solver(self):
c = self.pdepnetwork.solve_SS_network(1000.0,100000.0)
self.assertAlmostEquals(c[0],4.791463e-06,2)

def test_energy_filter(self):
rxns = self.pdepnetwork.get_energy_filtered_reactions(1000.0,0.0)
self.assertEquals(len(rxns),1)
self.assertEquals(rxns[0],self.pdepnetwork.pathReactions[0])

def test_flux_filter(self):
rxns = self.pdepnetwork.get_rate_filtered_reactions(1000.0,100000.0,1.0)
self.assertEquals(len(rxns),0)
prods = self.pdepnetwork.get_rate_filtered_products(1000.0,100000.0,1.0)
self.assertEquals(len(prods),0)

if __name__ == '__main__':
unittest.main()

0 comments on commit d66fbfe

Please sign in to comment.