Skip to content

Commit

Permalink
Adapt solver/reactor object syntax to deal with variable condition re…
Browse files Browse the repository at this point in the history
…actors

Also adaption of conversion calculations and return type for simulate so that the time and conversion can be put into the RMG_Memory objects. Addition of sensConditions for specifying the conditions under, which to do sensitivity analysis.
  • Loading branch information
mjohnson541 committed May 16, 2018
1 parent 0fc6f86 commit 031b93c
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 84 deletions.
121 changes: 80 additions & 41 deletions rmgpy/rmg/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,47 +133,51 @@ def adjacencyList(string):
def simpleReactor(temperature,
pressure,
initialMoleFractions,
nSimsTerm=6,
terminationConversion=None,
terminationTime=None,
sensitivity=None,
sensitivityThreshold=1e-3
sensitivityThreshold=1e-3,
sensitivityTemperature=None,
sensitivityPressure=None,
sensitivityMoleFractions=None,
):
logging.debug('Found SimpleReactor reaction system')

for value in initialMoleFractions.values():
if value < 0:
raise InputError('Initial mole fractions cannot be negative.')

for spec in initialMoleFractions:
initialMoleFractions[spec] = float(initialMoleFractions[spec])

totalInitialMoles = sum(initialMoleFractions.values())
if totalInitialMoles != 1:
logging.warning('Initial mole fractions do not sum to one; normalizing.')
logging.info('')
logging.info('Original composition:')
for spec, molfrac in initialMoleFractions.iteritems():
logging.info("{0} = {1}".format(spec,molfrac))
for spec in initialMoleFractions:
initialMoleFractions[spec] /= totalInitialMoles
logging.info('')
logging.info('Normalized mole fractions:')
for spec, molfrac in initialMoleFractions.iteritems():
logging.info("{0} = {1}".format(spec,molfrac))

if type(temperature) != list and type(pressure) != list:
for key,value in initialMoleFractions.iteritems():
if not isinstance(value,list):
initialMoleFractions[key] = float(value)
if value < 0:
raise InputError('Initial mole fractions cannot be negative.')
else:
if len(value) != 2:
raise InputError("Initial mole fraction values must either be a number or a list with 2 entries")
initialMoleFractions[key] = [float(value[0]),float(value[1])]
if len(value) > 2:
raise InputError('mole ranges can only have one or two entries')
elif value[0] < 0 or value[1] < 0:
raise InputError('Initial mole fractions cannot be negative.')
elif value[1] < value[0]:
raise InputError('Initial mole fraction range out of order: {0}'.format(key))

if not isinstance(temperature,list):
T = Quantity(temperature)
P = Quantity(pressure)
else:
if type(temperature) != list:
temperature = list(temperature)
if type(pressure) != list:
pressure = list(pressure)
if len(temperature) != 2:
raise InputError('Temperature and pressure ranges can either be in the form of (number,units) or a list with 2 entries of the same format')
T = [Quantity(t) for t in temperature]

if not isinstance(pressure,list):
P = Quantity(pressure)
else:
if len(pressure) > 2:
raise InputError('Temperature and pressure ranges can either be in the form of (number,units) or a list with 2 entries of the same format')
P = [Quantity(p) for p in pressure]
if len(T) > 2 or len(P) > 2:
raise InputError('Temperature and pressure ranges can only have one or two entries')


if not isinstance(temperature,list) and not isinstance(pressure,list) and all([not isinstance(x,list) for x in initialMoleFractions.values()]):
nSimsTerm=1

termination = []
if terminationConversion is not None:
for spec, conv in terminationConversion.iteritems():
Expand All @@ -188,33 +192,59 @@ def simpleReactor(temperature,
if isinstance(sensitivity, str): sensitivity = [sensitivity]
for spec in sensitivity:
sensitiveSpecies.append(speciesDict[spec])
system = SimpleReactor(T, P, initialMoleFractions, termination, sensitiveSpecies, sensitivityThreshold)

if not isinstance(T,list):
sensitivityTemperature = T
if not isinstance(P,list):
sensitivityPressure = P
if not any([isinstance(x,list) for x in initialMoleFractions.itervalues()]):
sensitivityMoleFractions = initialMoleFractions
if sensitivityMoleFractions is None or sensitivityTemperature is None or sensitivityPressure is None:
sensConditions = None
else:
sensConditions = sensitivityMoleFractions
sensConditions['T'] = Quantity(sensitivityTemperature)
sensConditions['P'] = Quantity(sensitivityPressure)

system = SimpleReactor(T, P, initialMoleFractions, nSimsTerm, termination, sensitiveSpecies, sensitivityThreshold,sensConditions)
rmg.reactionSystems.append(system)


# Reaction systems
def liquidReactor(temperature,
initialConcentrations,
terminationConversion=None,
nSimsTerm = 4,
terminationTime=None,
sensitivity=None,
sensitivityThreshold=1e-3,
sensitivityTemperature=None,
sensitivityConcentrations=None,
constantSpecies=None):

logging.debug('Found LiquidReactor reaction system')

if type(temperature) != list:
if not isinstance(temperature,list):
T = Quantity(temperature)
else:
if len(temperature) != 2:
raise InputError('Temperature and pressure ranges can either be in the form of (number,units) or a list with 2 entries of the same format')
T = [Quantity(t) for t in temperature]
if len(T) > 2:
raise InputError('Temperature ranges can only have one or two entries')

for spec,conc in initialConcentrations.iteritems():
concentration = Quantity(conc)
# check the dimensions are ok
# convert to mol/m^3 (or something numerically nice? or must it be SI)
initialConcentrations[spec] = concentration.value_si
if not isinstance(conc,list):
concentration = Quantity(conc)
# check the dimensions are ok
# convert to mol/m^3 (or something numerically nice? or must it be SI)
initialConcentrations[spec] = concentration.value_si
else:
if len(conc) != 2:
raise InputError("Concentration values must either be in the form of (number,units) or a list with 2 entries of the same format")
initialConcentrations[spec] = [Quantity(conc[0]),Quantity(conc[1])]

if not isinstance(temperature,list) and all([not isinstance(x,list) for x in initialConcentrations.itervalues()]):
nSimsTerm=1

termination = []
if terminationConversion is not None:
for spec, conv in terminationConversion.iteritems():
Expand All @@ -236,9 +266,18 @@ def liquidReactor(temperature,
logging.debug(" {0}".format(constantSpecie))
if not speciesDict.has_key(constantSpecie):
raise InputError('Species {0} not found in the input file'.format(constantSpecie))


system = LiquidReactor(T, initialConcentrations, termination, sensitiveSpecies, sensitivityThreshold,constantSpecies)

if not isinstance(T,list):
sensitivityTemperature = T
if not any([isinstance(x,list) for x in initialConcentrations.itervalues()]):
sensitivityConcentrations = initialConcentrations
if sensitivityConcentrations is None or sensitivityTemperature is None:
sensConditions = None
else:
sensConditions = Quantity(sensitivityConcentrations)
sensConditions['T'] = Quantity(sensitivityTemperature)

system = LiquidReactor(T, initialConcentrations, nSimsTerm, termination, sensitiveSpecies, sensitivityThreshold, sensConditions, constantSpecies)
rmg.reactionSystems.append(system)

def simulator(atol, rtol, sens_atol=1e-6, sens_rtol=1e-4):
Expand Down
5 changes: 3 additions & 2 deletions rmgpy/rmg/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ def execute(self, **kwargs):
# Turn pruning off if we haven't reached minimum core size.
prune = False

try: terminated,resurrected,obj,newSurfaceSpecies,newSurfaceReactions = reactionSystem.simulate(
try: terminated,resurrected,obj,newSurfaceSpecies,newSurfaceReactions,t,x = reactionSystem.simulate(
coreSpecies = self.reactionModel.core.species,
coreReactions = self.reactionModel.core.reactions,
edgeSpecies = self.reactionModel.edge.species,
Expand Down Expand Up @@ -806,7 +806,7 @@ def execute(self, **kwargs):
# Run sensitivity analysis post-model generation if sensitivity analysis is on
for index, reactionSystem in enumerate(self.reactionSystems):

if reactionSystem.sensitiveSpecies:
if reactionSystem.sensitiveSpecies and reactionSystem.sensConditions:
logging.info('Conducting sensitivity analysis of reaction system %s...' % (index+1))

sensWorksheet = []
Expand All @@ -826,6 +826,7 @@ def execute(self, **kwargs):
sensWorksheet = sensWorksheet,
modelSettings = self.modelSettingsList[-1],
simulatorSettings = self.simulatorSettingsList[-1],
conditions = reactionSystem.sensConditions,
)

plot_sensitivity(self.outputDirectory, index, reactionSystem.sensitiveSpecies)
Expand Down
5 changes: 3 additions & 2 deletions rmgpy/solver/base.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,13 @@ cdef class ReactionSystem(DASx):

# methods
cpdef initializeModel(self, list coreSpecies, list coreReactions, list edgeSpecies, list edgeReactions, list surfaceSpecies=?,
list surfaceReactions=?, list pdepNetworks=?, atol=?, rtol=?, sensitivity=?, sens_atol=?, sens_rtol=?, filterReactions=?)
list surfaceReactions=?, list pdepNetworks=?, atol=?, rtol=?, sensitivity=?, sens_atol=?, sens_rtol=?, filterReactions=?,
dict conditions=?)

cpdef simulate(self, list coreSpecies, list coreReactions, list edgeSpecies,
list edgeReactions,list surfaceSpecies, list surfaceReactions,
list pdepNetworks=?, bool prune=?, bool sensitivity=?, list sensWorksheet=?, object modelSettings=?,
object simulatorSettings=?)
object simulatorSettings=?, dict conditions=?)

cpdef logRates(self, double charRate, object species, double speciesRate, double maxDifLnAccumNum, object network, double networkRate)

Expand Down
39 changes: 26 additions & 13 deletions rmgpy/solver/base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ cdef class ReactionSystem(DASx):

cpdef initializeModel(self, list coreSpecies, list coreReactions, list edgeSpecies, list edgeReactions, list surfaceSpecies=None,
list surfaceReactions=None, list pdepNetworks=None, atol=1e-16, rtol=1e-8, sensitivity=False,
sens_atol=1e-6, sens_rtol=1e-4, filterReactions=False):
sens_atol=1e-6, sens_rtol=1e-4, filterReactions=False, dict conditions=None):
"""
Initialize a simulation of the reaction system using the provided
kinetic model. You will probably want to create your own version of this
Expand All @@ -200,7 +200,21 @@ cdef class ReactionSystem(DASx):
if surfaceReactions is None:
surfaceReactions = []


if conditions:
isConc = hasattr(self,'initialConcentrations')

This comment has been minimized.

Copy link
@rwest

rwest Jun 1, 2018

Member

Perhaps this should be done in an inherited sub class, rather than having the base class try to figure out whether it is dealing with concentrations or mole fractions? Ideally the base class would only have things common to all the reactor types. Could it call set_initial_conditions or something like it? (trying to deal with a RMG-Cat which has a new reactor type which also has initial surface coverages, etc.)

This comment has been minimized.

Copy link
@mjohnson541

mjohnson541 Jun 2, 2018

Author Contributor

That would probably make a lot more sense, I wrote this PR a while ago, but I can't see any reason why that wouldn't work, it would certainly be easier to handle that in the subclass.

keys = conditions.keys()
if 'T' in keys and hasattr(self,'T'):
self.T = Quantity(conditions['T'],'K')
if 'P' in keys and hasattr(self,'P'):
self.P = Quantity(conditions['P'],'Pa')
for k in keys:
if isConc:
if k in self.initialConcentrations.keys():
self.initialConcentrations[k] = conditions[k] #already in SI units
else:
if k in self.initialMoleFractions.keys():
self.initialMoleFractions[k] = conditions[k]

self.numCoreSpecies = len(coreSpecies)
self.numCoreReactions = len(coreReactions)
self.numEdgeSpecies = len(edgeSpecies)
Expand Down Expand Up @@ -520,7 +534,7 @@ cdef class ReactionSystem(DASx):
cpdef simulate(self, list coreSpecies, list coreReactions, list edgeSpecies,
list edgeReactions,list surfaceSpecies, list surfaceReactions,
list pdepNetworks=None, bool prune=False, bool sensitivity=False, list sensWorksheet=None, object modelSettings=None,
object simulatorSettings=None):
object simulatorSettings=None, dict conditions=None):
"""
Simulate the reaction system with the provided reaction model,
consisting of lists of core species, core reactions, edge species, and
Expand Down Expand Up @@ -549,7 +563,7 @@ cdef class ReactionSystem(DASx):
cdef bint terminated
cdef object maxSpecies, maxNetwork
cdef int i, j, k
cdef numpy.float64_t maxSurfaceDifLnAccumNum, maxSurfaceSpeciesRate
cdef numpy.float64_t maxSurfaceDifLnAccumNum, maxSurfaceSpeciesRate, conversion
cdef int maxSurfaceAccumReactionIndex, maxSurfaceSpeciesIndex
cdef object maxSurfaceAccumReaction, maxSurfaceSpecies
cdef numpy.ndarray[numpy.float64_t,ndim=1] surfaceSpeciesProduction, surfaceSpeciesConsumption
Expand All @@ -563,7 +577,7 @@ cdef class ReactionSystem(DASx):
# cython declations for sensitivity analysis
cdef numpy.ndarray[numpy.int_t, ndim=1] sensSpeciesIndices
cdef numpy.ndarray[numpy.float64_t, ndim=1] moleSens, dVdk, normSens
cdef list time_array, normSens_array, newSurfaceReactions, newSurfaceReactionInds, newObjects, newObjectInds, conversions
cdef list time_array, normSens_array, newSurfaceReactions, newSurfaceReactionInds, newObjects, newObjectInds

zeroProduction = False
zeroConsumption = False
Expand Down Expand Up @@ -609,7 +623,7 @@ cdef class ReactionSystem(DASx):

self.initializeModel(coreSpecies, coreReactions, edgeSpecies, edgeReactions, surfaceSpecies, surfaceReactions,
pdepNetworks, absoluteTolerance, relativeTolerance, sensitivity, sensitivityAbsoluteTolerance,
sensitivityRelativeTolerance, filterReactions)
sensitivityRelativeTolerance, filterReactions,conditions)

prunableSpeciesIndices = self.prunableSpeciesIndices
prunableNetworkIndices = self.prunableNetworkIndices
Expand All @@ -630,6 +644,7 @@ cdef class ReactionSystem(DASx):
maxNetwork = None
maxNetworkRate = 0.0
iteration = 0
conversion = 0.0

maxEdgeSpeciesRateRatios = self.maxEdgeSpeciesRateRatios
maxNetworkLeakRateRatios = self.maxNetworkLeakRateRatios
Expand Down Expand Up @@ -667,12 +682,11 @@ cdef class ReactionSystem(DASx):

logging.info('Resurrecting Model...')

conversions = []

conversion = 0.0
for term in self.termination:
if isinstance(term, TerminationConversion):
index = speciesIndex[term.species]
conversions.append(1-(y_coreSpecies[index] / y0[index]))
conversion = 1-(y_coreSpecies[index] / y0[index])

if invalidObjects == []:
#species flux criterion
Expand All @@ -695,7 +709,7 @@ cdef class ReactionSystem(DASx):
invalidObjects.append(obj)

if invalidObjects != []:
return False,True,invalidObjects,surfaceSpecies,surfaceReactions,self.t,conversions
return False,True,invalidObjects,surfaceSpecies,surfaceReactions,self.t,conversion
else:
logging.error('Model Resurrection has failed')
logging.error("Core species names: {!r}".format([getSpeciesIdentifier(s) for s in coreSpecies]))
Expand Down Expand Up @@ -1031,7 +1045,6 @@ cdef class ReactionSystem(DASx):
logging.info('terminating simulation due to interrupt...')
break

conversions = []
# Finish simulation if any of the termination criteria are satisfied
for term in self.termination:
if isinstance(term, TerminationTime):
Expand All @@ -1042,7 +1055,7 @@ cdef class ReactionSystem(DASx):
break
elif isinstance(term, TerminationConversion):
index = speciesIndex[term.species]
conversions.append(1-(y_coreSpecies[index] / y0[index]))
conversion = 1-(y_coreSpecies[index] / y0[index])
if 1 - (y_coreSpecies[index] / y0[index]) > term.conversion:
terminated = True
logging.info('At time {0:10.4e} s, reached target termination conversion: {1:f} of {2}'.format(self.t,term.conversion,term.species))
Expand Down Expand Up @@ -1088,7 +1101,7 @@ cdef class ReactionSystem(DASx):

# Return the invalid object (if the simulation was invalid) or None
# (if the simulation was valid)
return terminated, False, invalidObjects, surfaceSpecies, surfaceReactions, self.t, conversions
return terminated, False, invalidObjects, surfaceSpecies, surfaceReactions, self.t, conversion

cpdef logRates(self, double charRate, object species, double speciesRate, double maxDifLnAccumNum, object network, double networkRate):
"""
Expand Down
Loading

0 comments on commit 031b93c

Please sign in to comment.