def write_xml(family_names = None): """Writes the library to xml files """ import os import xml.dom.minidom # Create document dom = xml.dom.minidom.Document() # Process root element root = dom.createElement('rmgoutput') dom.appendChild(root) if not family_names: family_names = reaction.kineticsDatabase.families.keys() for family_name in family_names: family = reaction.kineticsDatabase.families[family_name] print if not family.library: logging.debug("Family '%s' has no data in the library."%family_name) if family.reverse.library: logging.debug("(but its reverse '%s' does)"%family.reverse.label) continue logging.info("Writing xml for reaction family: %s (%s)"%(family_name, os.path.basename(os.path.abspath(family._path))) ) family.library.toXML(dom,root) print dom.toprettyxml()
def drawAllTrees(root): """ Draws all of the trees in the thermo and kinetics database. The trees are placed in the folder specified by `root`. """ import os, os.path thermoDatabases = {'group': species.thermoDatabase.groupDatabase, '1,5-interactions': species.thermoDatabase.int15Database, 'gauche': species.thermoDatabase.gaucheDatabase, 'other': species.thermoDatabase.otherDatabase, 'radical': species.thermoDatabase.radicalDatabase, 'ring': species.thermoDatabase.ringDatabase} # Create directories try: os.makedirs(root) os.makedirs(os.path.join(root,'thermo')) os.makedirs(os.path.join(root,'kinetics')) for key in thermoDatabases: os.makedirs(os.path.join(root,'thermo',key)) for key in reaction.kineticsDatabase.families: os.makedirs(os.path.join(root,'kinetics',key)) except OSError: raise # Process thermo databases for key, thermoDatabase in thermoDatabases.iteritems(): # Process all structures in dictionary for label, node in thermoDatabase.dictionary.iteritems(): if isinstance(node, structure.Structure): logging.info('\t'+ label) graph = node.toDOT() graph.set('fontsize','10') saveDOTAndImage(graph, root+os.sep+'thermo'+os.sep+key+os.sep+label, 'svg', 'neato') # Process tree itself logging.info('\t'+key) graph = thermoDatabase.tree.toDOT() graph.set('fontsize','10') saveDOTAndImage(graph, root+os.sep+'thermo'+os.sep+key, 'svg', 'dot') print 'Created DOT for thermo database %s' % (key) # Process kinetics databases for key, family in reaction.kineticsDatabase.families.iteritems(): # Process all structures in dictionary for label, node in family.dictionary.iteritems(): if isinstance(node, structure.Structure): logging.info('\t'+ label) graph = node.toDOT() graph.set('fontsize','10') saveDOTAndImage(graph, root+os.sep+'thermo'+os.sep+key+os.sep+label, 'svg', 'neato') # Process tree itself logging.info('\t'+key) graph = family.tree.toDOT() graph.set('fontsize','10') saveDOTAndImage(graph, root+os.sep+'kinetics'+os.sep+key, 'svg', 'neato') logging.info('Created DOT for kinetics database %s'%(key) )
def print_node_tree(node,indent=0): logging.info(' '*indent + node.ljust(17-indent) + ("\t%7.2g"*len(group_values[node])) % group_values[node] + "\t%6.2g\t%d"%(group_error[node],group_count[node]) + ("\t%7.3g"*len(group_error_MAD_by_T[node])) % group_error_MAD_by_T[node] ) children = family.tree.children[node] if children: children.sort() for child in children: # recurse! print_node_tree(child,indent+1)
settings.wallTime = int(data[-1]) + 60 * int(data[-2]) + 3600 * int(data[-3]) elif len(data) == 4: settings.wallTime = int(data[-1]) + 60 * int(data[-2]) + 3600 * int(data[-3]) + 86400 * int(data[-4]) else: raise ValueError('Invalid format for wall time; should be HH:MM:SS.') except ValueError, e: raise ValueError('Invalid format for wall time; should be HH:MM:SS.') # Save initialization time settings.initializationTime = time.time() # Set up log (uses stdout and a file) logging.initialize(options.verbose, os.path.join(settings.outputDirectory,'RMG.log')) # Log start timestamp logging.info('RMG execution initiated at ' + time.asctime() + '\n') # Print out RMG header logging.logHeader() # Make output subdirectories plotDir = os.path.join(settings.outputDirectory, 'plot') if os.path.exists(plotDir): for f in os.listdir(plotDir): os.remove(plotDir + '/' + f) os.rmdir(plotDir) os.mkdir(plotDir) specDir = os.path.join(settings.outputDirectory, 'species') if os.path.exists(specDir): for f in os.listdir(specDir):
def fit_groups(family_names = None): """Decouples a nested tree and fits values to groups for each seperate tree. If given a list of family names, only does those families. """ import os import math import numpy import numpy.linalg import pylab if not family_names: family_names = reaction.kineticsDatabase.families.keys() for family_name in family_names: family = reaction.kineticsDatabase.families[family_name] print if not family.library: logging.debug("Family '%s' has no data in the library."%family_name) if family.reverse.library: logging.debug("(but its reverse '%s' does)"%family.reverse.label) continue logging.info("Fitting groups for reaction family: %s (%s)"%(family_name, os.path.basename(os.path.abspath(family._path))) ) # Get set of all nodes node_set = family.tree.parent.keys() non_top_nodes = [ node for node in node_set if family.tree.parent[node] ] top_nodes = [ node for node in node_set if not family.tree.parent[node] ] group_names = [node for node in non_top_nodes] # poor man's copy group_names.append("Constant") family.tree.children['Constant']=[] #: a dictionary of lists. key = node, value = list of kinetics items which contributed to that node kinetics_used_in={'Constant':[]} for node in node_set: # must initialise in loop so each has a separate list instance! kinetics_used_in[node] = list() Ts = [300, 500, 1000, 1500] def rates_string(k): """Return a string representing the rates of :class:`kinetics` object k log10 of the k at a bunch of T values""" string = "%5.2f "*len(Ts) return string%tuple([ math.log10(k.getRateConstant(T,Hrxn)) for T in Ts ]) A_list = [] b_list = [] # Get available data to_delete=[] for key, kinetics in family.library.iteritems(): if kinetics.alpha: logging.warning("Warning: %s %s has EP alpha = %g"%(kinetics.index, kinetics.label, kinetics.alpha)) to_delete.append(key) #if re.search('O2b',kinetics.label): # logging.warning("Removing %s %s because I don't like O2b"%(kinetics.index, kinetics.label)) # to_delete.append(key) # for key in to_delete: del family.library[key] logging.warning("Deleting %s from kinetics library!"%key) for key, kinetics in family.library.iteritems(): nodes = key.split(';') # example: # nodes = ['A11', 'B11'] # kinetics = <rmg.reaction.ArrheniusEPKinetics instance> #b_row = [ math.log(kinetics.A), # kinetics.n, # kinetics.alpha, # kinetics.E0 ] if kinetics.alpha: logging.warning("Warning: %s has EP alpha = %g"%(nodes,kinetics.alpha)) Hrxn=0 b_row = [ math.log10(kinetics.getRateConstant(T,Hrxn)) for T in Ts ] all_ancestors=list() kinetics.used_in_groups = list() kinetics.used_in_combinations = list() for node in nodes: # start with the most specific - the node itself # then add the ancestors ancestors = [node] ancestors.extend( family.tree.ancestors(node) ) # append to the list of lists all_ancestors.append(ancestors) # add to the list kinetics.used_in_groups.extend(ancestors) for ancestor in ancestors: kinetics_used_in[ancestor].append(kinetics) kinetics_used_in['Constant'].append(kinetics) # example # all_ancestors = [['A11','A1','A'], ['B11','B1','B']] # kinetics.used_in_groups = [ 'A11','A1','A','B11','B1','B' ] # kinetics_used_in['A11'] = kinetics_used_in['A1'] ... = [... <kinetics>] all_combinations = data.getAllCombinations(all_ancestors) # example: # all_combinations = # [['A11', 'B11'], ['A1', 'B11'], ['A', 'B11'], ['A11', 'B1'], # ['A1', 'B1'], ['A', 'B1'], ['A11', 'B'], ['A1', 'B'], ['A', 'B']] for combination in all_combinations: # Create a row of the A matrix. Each column is for a non_top_node # It contains 1 if that node exists in combination, else 0 A_row = [int(node in combination) for node in non_top_nodes] # Add on a column at the end for constant C which is always there A_row.append(1) kinetics.used_in_combinations.append(len(A_list)) A_list.append(A_row) b_list.append(b_row) A = numpy.array(A_list) b = numpy.array(b_list) logging.info("Library contained %d rates"%len(family.library)) logging.info("Matrix for inversion is %d x %d"%A.shape) x, residues, rank, s = numpy.linalg.lstsq(A,b) fitted_b = numpy.dot(A,x) errors = fitted_b - b #: squared and SUMMED over temperatures, not averaged errors_sum_squared = numpy.sum(errors*errors, axis=1) group_values=dict() group_error=dict() group_count=dict() group_error_MAD_by_T=dict() for node in top_nodes: group_values[node] = tuple([0 for i in Ts]) # eg. (0 0 0 0 0) group_error[node] = 0 group_count[node] = 0 group_error_MAD_by_T[node] = tuple([0 for i in Ts]) # eg. (0 0 0 0 0) for i in range(len(x)): group_values[group_names[i]] = tuple(x[i,:]) for i in range(len(x)): # for each group #: vector of 1s and 0s, one for each rate-group rates_in_group = A[:,i] #: number of data points training this group (each measured rate may be counted many times) group_count[group_names[i]] = sum(rates_in_group) #: RMS error for this group (where M = mean over temperatures and rates training the group) group_error[group_names[i]] = numpy.sqrt( sum(rates_in_group * errors_sum_squared) / sum(rates_in_group) / len(Ts) ) #: Mean Absolute Deviation, reported by Temperature (as tuple) group_error_MAD_by_T[group_names[i]] = tuple( numpy.dot(rates_in_group, abs(errors)) / sum(rates_in_group) ) for key, kinetics in family.library.iteritems(): rows = kinetics.used_in_combinations #: RMS error for this rate (where M = mean over temperatures and group combinations it's estimated by) kinetics.RMS_error = numpy.sqrt( sum([errors_sum_squared[i] for i in rows]) / len(rows) / len(Ts) ) kinetics.key = key rates = family.library.values() rates.sort(cmp=lambda x,y: cmp(x.RMS_error, y.RMS_error)) print "Rate expressions sorted by how well they are predicted by their group combinations" rates_1000 = [] rates_err = [] for k in rates: print "%-5s %-30s\tRMS error: %.2f Rates: %s %.30s"%(k.index, k.key, k.RMS_error, rates_string(k), k.comment ) rates_1000.append( math.log10(k.getRateConstant(1000,Hrxn)) ) rates_err.append( k.RMS_error ) # [Ts.index(T)] rates_1000 = numpy.array(rates_1000) rates_err = numpy.array(rates_err) fig_number = family_names.index(family_name) fig1 = pylab.figure( fig_number ) pylab.plot(rates_1000, rates_err, 'o') pylab.xlabel('log10(k) at 1000K') pylab.ylabel('RMSE') pylab.show() def print_node_tree(node,indent=0): print (' '*indent + node.ljust(17-indent) + ("\t%7.2g"*len(group_values[node])) % group_values[node] + "\t%6.2g\t%d"%(group_error[node],group_count[node]) + ("\t%7.3g"*len(group_error_MAD_by_T[node])) % group_error_MAD_by_T[node] ) children = family.tree.children[node] if children: children.sort() for child in children: # recurse! print_node_tree(child,indent+1) print ("Log10(k) at T= " + ("\t%7g"*len(Ts)) % tuple(Ts) + '\t RMS\tcount' + ("\tMAD @ %d"*len(Ts)) % tuple(Ts) ) print_node_tree('Constant') for node in top_nodes: print_node_tree(node) print fig = pylab.figure( 100 + fig_number ) xvals = numpy.array([ group_count[group] for group in group_names ]) yvals = numpy.array([ group_error[group] for group in group_names ]) pylab.semilogx(xvals,yvals,'o',picker=5) # 5 points tolerance pylab.title(family_name) def onpick(event): thisline = event.artist xdata = thisline.get_xdata() ydata = thisline.get_ydata() for ind in event.ind: group_name = group_names[ind] print "#%d Name: %s \tRates:%d \tNode-Rates:%d \tRMS error: %g"%(ind, group_name, len(kinetics_used_in[group_name]) , xvals[ind], yvals[ind]) print "MAD errors:"+(" %.2f"*len(Ts))%group_error_MAD_by_T[group_name] print "Kinetics taken from:" rates = kinetics_used_in[group_name] rates.sort(cmp=lambda x,y: cmp(x.RMS_error, y.RMS_error)) for k in rates: print "%s\tIndex:%s \t%s "%(k.key,k.index,repr(k)) print "RMS error: %.2f"%(k.RMS_error), print "Rates: ",rates_string(k) for combo in k.used_in_combinations: #print "A[%d,%d] ="%(combo,ind),A[combo,ind] if not A[combo,ind]: #print "Rate didn't use the node in question (presumably used an ancestor)" continue print "Using", used_nodes = [ group_names[i] for i in A[combo,:].nonzero()[0] ] used_nodes.remove(group_name) print group_name + ' with ' + ' + '.join(used_nodes) + '\t', rms = numpy.sqrt( errors_sum_squared[combo] / len(Ts) ) print "RMSE: %.2f Err(T):"%(rms), errors[combo] print #print 'check %g:'%ind, zip(xdata[ind], ydata[ind]) connection_id = fig.canvas.mpl_connect('pick_event', onpick) # disconnect with: fig.canvas.mpl_disconnect(connection_id) pylab.show() #http://matplotlib.sourceforge.net/users/event_handling.html import pdb; pdb.set_trace()
def calculateRateCoefficients(self, Tlist, Plist, Elist, method, errorCheck=True): """ Calculate the phenomenological rate coefficients for the network. """ K = numpy.zeros([len(Tlist), len(Plist),\ len(self.isomers), len(self.isomers)], numpy.float64) try: for t, T in enumerate(Tlist): # Calculate equilibrium distributions for isomer in self.isomers: if isomer.densStates is not None: isomer.calculateEqDist(Elist, T) # # DEBUG: Plot equilibrium distributions # import pylab # for isomer in self.isomers: # if isomer.densStates is not None: # pylab.plot(Elist / 1000.0, isomer.eqDist, '-') # pylab.xlabel('Energy (kJ/mol)') # pylab.ylabel('Equilibrium distribution') # pylab.show() # Calculate microcanonical rates k(E) # It might seem odd that this is dependent on temperature, and it # isn't -- unless the Arrhenius expression has a negative n for reaction in self.pathReactions: reaction.kf, reaction.kb = reaction.calculateMicrocanonicalRate(Elist, T, reaction.reactant.densStates, reaction.product.densStates) # # DEBUG: Plot microcanonical rates # import pylab # for i, reaction in enumerate(self.pathReactions): # if reaction.isIsomerization() or reaction.isDissociation(): # pylab.semilogy(Elist / 1000.0, reaction.kf, '-') # if reaction.isIsomerization() or reaction.isAssociation(): # pylab.semilogy(Elist / 1000.0, reaction.kb, '--') # pylab.xlabel('Energy (kJ/mol)') # pylab.ylabel('Microcanonical rate') # pylab.show() for p, P in enumerate(Plist): # Calculate collision frequencies for isomer in self.isomers: if isomer.isUnimolecular(): isomer.calculateCollisionFrequency(T, P, self.bathGas) # Determine phenomenological rate coefficients using approximate # method K[t,p,:,:] = self.applyApproximateMethod(T, P, Elist, method, errorCheck) except UnirxnNetworkException, e: if method.lower() == 'reservoirstate': logging.warning(e.msg) else: logging.error(e.msg) # Save network to file for later testing fstr = 'unirxn_input.xml' logging.info('Troublesome network saved to %s.' % fstr) import io io.writeInputFile(fstr, self, Tlist, Plist, Elist, method, 'none') if method.lower() == 'reservoirstate': logging.info('Falling back to modified strong collision for this network.') return self.calculateRateCoefficients(Tlist, Plist, Elist, 'modifiedstrongcollision') else: raise e
def simulate(self, model): """ Conduct a simulation of the current reaction system using the core-edge reaction model `model`. Edge species fluxes are tracked, relative to the characteristic core flux at that time, throughout the simulation. If one exceeds `model.fluxToleranceInterrupt` the simulation is interrupted, and that species is returned. The highest relative flux reached by each species during the simulation is stored for later analysis. If one or more of these exceed `model.fluxToleranceMoveToCore` then the species with the highest will be returned. If the simulation completes without interruption, then any that fall below `model.fluxToleranceKeepInEdge` will be removed from the edge, along with the reactions that involve them. Returns: (tlist, ylist, dydtlist, valid?, Edge_species_with_highest_flux) """ # try writing cantera file sim,gas = self.runCantera(model) # Assemble stoichiometry matrix for all core and edge species # Rows are species (core, then edge); columns are reactions (core, then edge) stoichiometry = model.getStoichiometryMatrix() tlist = []; ylist = []; dydtlist = [] maxRelativeSpeciesFluxes = numpy.zeros(len(model.core.species) + len(model.edge.species), float) maxRelativeNetworkLeakFluxes = numpy.zeros(len(model.unirxnNetworks), float) endtime = 10.0 # default. check for user value: for target in model.termination: if target.__class__ == modelmodule.TerminationTime: endtime = target.time # Set up initial conditions P = gas.pressure() V = sim.reactors()[0].volume() T = gas.temperature() # Note that, for molar density, Cantera thinks in kmol/m^3, while # RMG thinks in mol/m^3 Ni = gas.molarDensity()*1000.0 * gas.moleFractions() * V y = [P, V, T]; y.extend(Ni) Ni0 = Ni y0 = y # # Output information about simulation at current time header = 'Time ' for target in model.termination: if target.__class__ == modelmodule.TerminationConversion: header += 'Conv ' header += 'Char. flux Max. rel. flux to edge' logging.debug(header) # self.printSimulationStatus(model, 0, y, y0, criticalFlux, maxSpeciesFlux, maxSpecies) # tlist.append(0.0); ylist.append(y0) # dydtlist.append(self.getResidual(0.0, y0, model, stoichiometry)) done = False time = 0.0 while not done: # Conduct integration # Uses a fixed (on a log scale) time step nexttime = min(endtime,time*1.2589254117941673) # advance cantera one step if sim.step(endtime) < endtime: # didn't get to endtime, so take another step if sim.step(endtime) < nexttime: # still didn't get to endtime, so advance to nextime sim.advance(nexttime) # # Uses the same time steps that the Cantera solver used # sim.step(endtime) # Get state at current time time = sim.time() P = gas.pressure() V = sim.reactors()[0].volume() T = gas.temperature() # Note that, for molar density, Cantera thinks in kmol/m^3, while # RMG thinks in mol/m^3 Ni = gas.molarDensity()*1000.0 * gas.moleFractions() * V y = [P, V, T]; y.extend(Ni) # Calculate species fluxes of all core and edge species at the # current time dNidt = self.getSpeciesFluxes(model, P, V, T, Ni, stoichiometry) # Determine characteristic species flux charFlux = math.sqrt(sum([x*x for x in dNidt[0:len(model.core.species)]])) # Store the highest relative flux for each species for i in range(len(dNidt)): if maxRelativeSpeciesFluxes[i] < abs(dNidt[i])/charFlux: maxRelativeSpeciesFluxes[i] = abs(dNidt[i])/charFlux # Test for model validity criticalFlux = charFlux * model.fluxToleranceInterrupt edgeValid, maxSpecies, maxSpeciesFlux = self.isModelValid(model, dNidt, criticalFlux) # Test leak fluxes of unimolecular networks if settings.unimolecularReactionNetworks: maxNetwork = None; maxNetworkFlux = 0.0 # Get current leak fluxes of all unimolecular reaction networks networkLeakFluxes = self.getNetworkLeakFluxes(model, P, V, T, Ni, criticalFlux) for i in range(len(networkLeakFluxes)): if maxRelativeNetworkLeakFluxes[i] < abs(networkLeakFluxes[i]) / criticalFlux: maxRelativeNetworkLeakFluxes[i] = abs(networkLeakFluxes[i]) / criticalFlux if networkLeakFluxes[i] > maxNetworkFlux or maxNetwork is None: maxNetwork = model.unirxnNetworks[i] maxNetworkFlux = networkLeakFluxes[i] networksValid = (maxNetworkFlux <= criticalFlux) else: networksValid = True maxNetwork = None maxNetworkFlux = 0.0 # Output information about simulation at current time self.printSimulationStatus(model, time, y, y0, charFlux, maxSpeciesFlux/charFlux, maxSpecies) tlist.append(time); ylist.append(y) #dydtlist.append(self.getResidual(time, solver.y, model, stoichiometry)) # Exit simulation if model is not valid (exceeds interruption criterion) if not edgeValid or not networksValid: print gas logging.info('') # Choose the item with the maximum flux and act on it if maxSpeciesFlux >= maxNetworkFlux: logging.info('At t = %s, an edge species flux exceeds the critical flux for simulation interruption' % (time)) logging.info('\tCharacteristic flux: %s' % (charFlux)) logging.info('\tCritical flux: %s (%s times charFlux)' % (criticalFlux, model.fluxToleranceInterrupt)) logging.info('\tSpecies flux for %s: %s (%.2g times charFlux)' % (maxSpecies, maxSpeciesFlux, maxSpeciesFlux/charFlux)) return tlist, ylist, dydtlist, False, maxSpecies else: logging.info('At t = %s, a network leak flux exceeds the critical flux for simulation interruption' % (time)) logging.info('\tCharacteristic flux: %s' % (charFlux)) logging.info('\tCritical flux: %s (%s times charFlux)' % (criticalFlux, model.fluxToleranceInterrupt)) logging.info('\tNetwork leak flux for %s: %s (%.2g times charFlux)' % (maxNetwork, maxNetworkFlux, maxNetworkFlux/charFlux)) return tlist, ylist, dydtlist, False, maxNetwork # Test for simulation completion for target in model.termination: if target.__class__ == modelmodule.TerminationConversion: index = model.core.species.index(target.species) + 3 conversion = 1.0 - y[index] / y0[index] if conversion > target.conversion: done = True elif target.__class__ == modelmodule.TerminationTime: if time > target.time: done = True logging.info(str(gas)) # Compare maximum species fluxes maxRelativeSpeciesFlux = 0.0; maxSpecies = None speciesToRemove = []; maxRelativeFluxes_dict = {} for i in range(len(model.core.species), len(maxRelativeSpeciesFluxes)): spec = model.edge.species[i - len(model.core.species)] # pick out the single highest-flux edge species if maxRelativeSpeciesFluxes[i] > maxRelativeSpeciesFlux: maxRelativeSpeciesFlux = maxRelativeSpeciesFluxes[i] maxSpecies = spec # mark for removal those species whose flux is always too low if maxRelativeSpeciesFluxes[i] < model.fluxToleranceKeepInEdge: speciesToRemove.append(spec) # put max relative flux in dictionary maxRelativeFluxes_dict[spec] = maxRelativeSpeciesFluxes[i] edgeValid = maxRelativeSpeciesFlux <= model.fluxToleranceMoveToCore # Compare maximum network leak fluxes maxRelativeNetworkLeakFlux = 0.0; maxNetwork = None if settings.unimolecularReactionNetworks: # Compare maximum species fluxes for i in range(len(model.unirxnNetworks)): # pick out the single highest-flux edge species if maxRelativeNetworkLeakFluxes[i] > maxRelativeNetworkLeakFlux or maxNetwork is None: maxRelativeNetworkLeakFlux = maxRelativeNetworkLeakFluxes[i] maxNetwork = model.unirxnNetworks[i] networksValid = maxRelativeNetworkLeakFlux < model.fluxToleranceMoveToCore else: networksValid = True def removalSortKey(sp): return maxRelativeFluxes_dict[sp] speciesToRemove.sort(key=removalSortKey) # If model is not valid at these criteria, then return if not edgeValid or not networksValid: # trim the edge according to fluxToleranceKeepInEdge logging.info("Removing from edge %d/%d species whose relative flux never exceeded %s"%( len(speciesToRemove),len(model.edge.species),model.fluxToleranceKeepInEdge ) ) logging.info("Max. rel. flux.\tSpecies") for sp in speciesToRemove: logging.info("%-10.3g \t%s"%(maxRelativeFluxes_dict[sp], sp)) model.removeSpeciesFromEdge(sp) # trim the edge according to maximumEdgeSpecies if len(model.edge.species)> model.maximumEdgeSpecies: logging.info("Removing from edge %d/%d species to reach maximum edge size of %s species"%( len(model.edge.species)-model.maximumEdgeSpecies, len(model.edge.species), model.maximumEdgeSpecies ) ) edgeSpeciesCopy = model.edge.species[:] edgeSpeciesCopy.sort(key=removalSortKey) logging.info("Max. rel. flux.\tSpecies") while len(model.edge.species)>model.maximumEdgeSpecies: sp = edgeSpeciesCopy.pop(0) logging.info("%-10.3g \t%s"%(maxRelativeFluxes_dict[sp], sp)) model.removeSpeciesFromEdge(sp) criticalFlux = charFlux * model.fluxToleranceMoveToCore print gas logging.info('') # Choose the item with the maximum flux and act on it if maxSpeciesFlux >= maxNetworkFlux: logging.info('At some time the species flux for %s exceeded the critical flux\nrelative to the characteristic core flux at that time' % (maxSpecies)) logging.info('\tCharacteristic flux: %s' % (charFlux)) logging.info('\tCritical flux: %s (%s times charFlux)' % (criticalFlux, model.fluxToleranceMoveToCore)) logging.info('\tSpecies flux for %s: %s (%.2g times charFlux)' % (maxSpecies, maxSpeciesFlux, maxSpeciesFlux/charFlux)) return tlist, ylist, dydtlist, False, maxSpecies else: logging.info('At some time the network leak flux for %s exceeded the critical flux\nrelative to the characteristic core flux at that time' % (maxNetwork)) logging.info('\tCharacteristic flux: %s' % (charFlux)) logging.info('\tCritical flux: %s (%s times charFlux)' % (criticalFlux, model.fluxToleranceMoveToCore)) logging.info('\tNetwork leak flux for %s: %s (%.2g times charFlux)' % (maxNetwork, maxNetworkFlux, maxNetworkFlux/charFlux)) return tlist, ylist, dydtlist, False, maxNetwork return tlist, ylist, dydtlist, True, None
def runCantera(self, model): """ Execute a simulation of the reaction system in Cantera. The procedure: (1) write a CTML (Cantera) file, (2) read it into Cantera, (3) create the reactor in Cantera, and (4) return the simulation results. """ # Create a folder in the scratch directory for Cantera files if needed cantera_folder = os.path.join(settings.scratchDirectory,'cantera') os.path.exists(cantera_folder) or os.mkdir(cantera_folder) # Write the CTML file to scratch/cantera/ folder cti_file = os.path.join(cantera_folder, 'cantera_input_%03d' % len(model.core.species)) logging.debug("Writing CTML file %s" % cti_file) ctml_writer.dataset(cti_file) # change name ctml_writer.write() import Cantera import Cantera.Reactor # Load the CTML file into Cantera logging.info("Preparing Cantera simulation %d" % len(model.core.species)) Cantera.reset() gas = Cantera.importPhase('%s.xml' % cti_file, 'chem', loglevel=1) # Set initial concentrations moleFractions = numpy.zeros(len(model.core.species)) for spec, conc in self.initialMoleFraction.iteritems(): moleFractions[gas.speciesIndex(str(spec))] = conc gas.setMoleFractions(moleFractions) # it normalises it to 1 # Set initial temperature and pressure gas.set(T=self.initialTemperature, P=self.initialPressure) # create a batch reactor if self.heatTransferCoeff == 1.0e100: reactor = Cantera.Reactor.Reactor(gas, volume=self.volume, energy='off') else: reactor = Cantera.Reactor.Reactor(gas, volume=self.volume) # set the inital environment conditions gasAir = Cantera.Air() gasAir.set(T=self.reservoirTemperature, P=self.reservoirPressure) # create a reservoir for the environment environment = Cantera.Reactor.Reservoir(gasAir) # Define a wall between the reactor and the environment, and # make it flexible, so that the pressure in the reactor is held # at the environment pressure, and conductive so the temperature likewise wall = Cantera.Reactor.Wall(reactor, environment) wall.set(K=self.expansionCoeff) wall.set(A=self.area) wall.setHeatTransferCoeff(self.heatTransferCoeff) # W/m2/K # put reactor in a reactor network so it can be integrated sim = Cantera.Reactor.ReactorNet([reactor]) sim.setTolerances(atol=model.absoluteTolerance, rtol=model.relativeTolerance) #import pdb; pdb.set_trace() return sim, gas
def printAtomTypeCounts(): """ Iterates through all of the dictionaries of the thermo and kinetics libraries and counts the number of atom types in each. In this counting scheme, compound atom types are treated as different, e.g. {Cd,Cs} is counted as one {Cd,Cs} and not as one Cd and one Cs. """ thermoDatabases = {'group': thermo.thermoDatabase.groupDatabase, '1,5-interactions': thermo.thermoDatabase.int15Database, 'gauche': thermo.thermoDatabase.gaucheDatabase, 'other': thermo.thermoDatabase.otherDatabase, 'radical': thermo.thermoDatabase.radicalDatabase, 'primary': thermo.thermoDatabase.primaryDatabase, 'ring': thermo.thermoDatabase.ringDatabase} # The total count of atom types totalAtomTypeCount = {} # Thermo databases for key, database in thermoDatabases.iteritems(): localAtomTypeCount = {} for label, struct in database.dictionary.iteritems(): if isinstance(struct, structure.Structure): for atom in struct.atoms(): atomType = [atomType.label for atomType in atom._atomType] atomType.sort() atomType = ','.join(atomType) if atomType in totalAtomTypeCount: totalAtomTypeCount[atomType] += 1 else: totalAtomTypeCount[atomType] = 1 if atomType in localAtomTypeCount: localAtomTypeCount[atomType] += 1 else: localAtomTypeCount[atomType] = 1 logging.info('%s:'%key) for atomType, count in localAtomTypeCount.iteritems(): logging.info('\t%i instances of %s' % (count, atomType)) logging.info('') # Kinetics databases for key, database in reaction.kineticsDatabase.families.iteritems(): localAtomTypeCount = {} for label, struct in database.dictionary.iteritems(): if isinstance(struct, structure.Structure): for atom in struct.atoms(): atomType = [atomType.label for atomType in atom._atomType] atomType.sort() atomType = ','.join(atomType) if atomType in totalAtomTypeCount: totalAtomTypeCount[atomType] += 1 else: totalAtomTypeCount[atomType] = 1 if atomType in localAtomTypeCount: localAtomTypeCount[atomType] += 1 else: localAtomTypeCount[atomType] = 1 logging.info( '%s:' % key ) for atomType, count in localAtomTypeCount.iteritems(): logging.info( '\t%i instances of %s' % (count, atomType) ) logging.info( '' ) # Print the totals logging.info( 'Totals:' ) for atomType, count in totalAtomTypeCount.iteritems(): logging.info( '\t%i instances of %s' % (count, atomType) )
def onpick(event): thisline = event.artist xdata = thisline.get_xdata() ydata = thisline.get_ydata() for ind in event.ind: group_name = group_names[ind] logging.info("#%d Name: %s \tRates:%d \tNode-Rates:%d \tRMS error: %g"%( ind, group_name, len(kinetics_used_in[group_name]) , xvals[ind], yvals[ind])) logging.info("MAD errors:"+(" %.2f"*len(Ts))%group_error_MAD_by_T[group_name]) logging.info("Kinetics taken from:") rates = kinetics_used_in[group_name] rates.sort(cmp=lambda x,y: cmp(x.RMS_error, y.RMS_error)) for k in rates: logging.info("%s\tIndex:%s \t%s "%(k.key,k.index,repr(k))) logging.info("RMS error: %.2f"%(k.RMS_error) + " Rates: " + rates_string(k) ) for combo in k.used_in_combinations: #logging.info( "A[%d,%d] ="%(combo,ind),A[combo,ind] ) if not A[combo,ind]: #logging.info("Rate didn't use the node in question (presumably used an ancestor)") continue used_nodes = [ group_names[i] for i in A[combo,:].nonzero()[0] ] used_nodes.remove(group_name) rms = numpy.sqrt( errors_sum_squared[combo] / len(Ts) ) logging.info("Using" + group_name + ' with ' + ' + '.join(used_nodes) + "\t RMSE: %.2f Err(T):"%rms + str(errors[combo]) ) logging.info("")
def execute(inputFile, options): # Set directories settings.libraryDirectory = options.libraryDirectory # Set up log (uses stdout and a file) logging.initialize(options.verbose, os.path.join(settings.outputDirectory,'RMG.log')) # Log start timestamp logging.info('RMG execution initiated at ' + time.asctime() + '\n') # Print out RMG header logging.logHeader() # Read input file network, Tlist, Plist, grainSize, numGrains, method, model = io.readInputFile(inputFile) # Shift network such that lowest-energy isomer has a ground state of 0.0 logging.info('Zeroing lowest energy isomer...') network.shiftToZeroEnergy() # Determine energy grains logging.info('Determining energy grains...') Elist = network.determineEnergyGrains(grainSize, numGrains, max(Tlist)) # Calculate density of states for all isomers in network logging.info('Calculating densities of states...') network.calculateDensitiesOfStates(Elist) # # DEBUG: Plot densities of states # import pylab # legend = [] # for isomer in network.isomers: # if isomer.densStates is not None: # pylab.semilogy(Elist / 1000.0, isomer.densStates, '-') # #legend.append(str(isomer)) # pylab.xlabel('Energy (kJ/mol)') # pylab.ylabel('Density of states (mol/J)') # #pylab.legend(legend, loc=4) # pylab.show() # Determine phenomenological rate coefficients logging.info('Calculating phenomenological rate coefficients...') K = network.calculateRateCoefficients(Tlist, Plist, Elist, method) # Create net reaction objects network.netReactions = [] index = 0 for i, reactantIsomer in enumerate(network.isomers): for j, productIsomer in enumerate(network.isomers[0:i]): netReaction = reaction.PDepReaction(reactantIsomer.species, productIsomer.species, network, None) netReaction.id = 'netReaction%i' % (index+1) index += 1 network.netReactions.append(netReaction) # Fit interpolation model if model[0].lower() == 'chebyshev': modelType, degreeT, degreeP = model chebyshev = kinetics.ChebyshevKinetics() chebyshev.fitToData(Tlist, Plist, K[:,:,j,i], degreeT, degreeP) netReaction.kinetics = [chebyshev] elif model.lower() == 'pdeparrhenius': pDepArrhenius = kinetics.PDepArrheniusKinetics() pDepArrhenius.fitToData(Tlist, Plist, K[:,:,j,i]) netReaction.kinetics = [pDepArrhenius] else: pass # Save results to file logging.info('Saving results...') outputFile = os.path.join(os.path.dirname(inputFile), 'output.xml') io.writeOutputFile(outputFile, network, Tlist, Plist, Elist, method, model) logging.info('') # Log end timestamp logging.info('RMG execution terminated at ' + time.asctime())