def getFluxGraphEdgesDict(spc_rop_dict, core_reactions): graph_edges_dict = {} for rxn in core_reactions: for pair in rxn.pairs: if pair in graph_edges_dict: # get flux from spc_rop_dict species_string = getSpeciesIdentifier(pair[0]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[pair][rxn] = flux elif (pair[1], pair[0]) in graph_edges_dict: # get flux from spc_rop_dict species_string = getSpeciesIdentifier(pair[1]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[(pair[1], pair[0])][rxn] = flux else: # get flux from spc_rop_dict graph_edges_dict[pair] = {} species_string = getSpeciesIdentifier(pair[0]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[pair][rxn] = flux return graph_edges_dict
def simulateOne(reactionModel, atol, rtol, reactionSystem): """ Simulates one reaction system, listener registers results, which are returned at the end. The returned data consists of a array of the species names, and the concentration data. The concentration data consists of a number of elements for each timestep the solver took to reach the end time of the batch reactor simulation. Each element consists of the time and the concentration data of the species at that particular timestep in the order of the species names. """ #register as a listener listener = ConcentrationListener() coreSpecies = reactionModel.core.species regex = r'\([0-9]+\)' #cut of '(one or more digits)' speciesNames = [] for spc in coreSpecies: name = getSpeciesIdentifier(spc) name_cutoff = re.split(regex, name)[0] speciesNames.append(name_cutoff) listener.speciesNames = speciesNames reactionSystem.attach(listener) pdepNetworks = [] for source, networks in reactionModel.networkDict.items(): pdepNetworks.extend(networks) simulatorSettings = SimulatorSettings(atol, rtol) modelSettings = ModelSettings(toleranceKeepInEdge=0, toleranceMoveToCore=1, toleranceInterruptSimulation=1) terminated, obj, sspcs, srxns = reactionSystem.simulate( coreSpecies=reactionModel.core.species, coreReactions=reactionModel.core.reactions, edgeSpecies=reactionModel.edge.species, edgeReactions=reactionModel.edge.reactions, surfaceSpecies=[], surfaceReactions=[], pdepNetworks=pdepNetworks, modelSettings=modelSettings, simulatorSettings=simulatorSettings, ) assert terminated #unregister as a listener reactionSystem.detach(listener) return listener.speciesNames, listener.data
def update(self, reactionSystem): """ Opens a file with filename referring to: - reaction system - number of core species Writes to a csv file: - header row with species names - each row with mole fractions of the core species in the given reaction system. """ filename = os.path.join( self.outputDirectory, 'solver', 'simulation_{0}_{1:d}.csv'.format(self.reaction_sys_index + 1, len(self.coreSpecies))) header = ['Time (s)', 'Volume (m^3)'] for spc in self.coreSpecies: header.append(getSpeciesIdentifier(spc)) with open(filename, 'wb') as csvfile: worksheet = csv.writer(csvfile) # add header row: worksheet.writerow(header) # add mole fractions: worksheet.writerows(reactionSystem.snapshots)
def update(self, reactionSystem): """ Opens a file with filename referring to: - reaction system - number of core species Writes to a csv file: - header row with species names - each row with mole fractions of the core species in the given reaction system. """ filename = os.path.join( self.outputDirectory, 'solver', 'simulation_{0}_{1:d}.csv'.format( self.reaction_sys_index + 1, len(self.coreSpecies) ) ) header = ['Time (s)', 'Volume (m^3)'] for spc in self.coreSpecies: header.append(getSpeciesIdentifier(spc)) with open(filename, 'w') as csvfile: worksheet = csv.writer(csvfile) # add header row: worksheet.writerow(header) # add mole fractions: worksheet.writerows(reactionSystem.snapshots)
def simulateOne(reactionModel, atol, rtol, reactionSystem): """ Simulates one reaction system, listener registers results, which are returned at the end. The returned data consists of a array of the species names, and the concentration data. The concentration data consists of a number of elements for each timestep the solver took to reach the end time of the batch reactor simulation. Each element consists of the time and the concentration data of the species at that particular timestep in the order of the species names. """ #register as a listener listener = ConcentrationListener() coreSpecies = reactionModel.core.species regex = r'\([0-9]+\)'#cut of '(one or more digits)' speciesNames = [] for spc in coreSpecies: name = getSpeciesIdentifier(spc) name_cutoff = re.split(regex, name)[0] speciesNames.append(name_cutoff) listener.speciesNames = speciesNames reactionSystem.attach(listener) pdepNetworks = [] for source, networks in reactionModel.networkDict.items(): pdepNetworks.extend(networks) simulatorSettings = SimulatorSettings(atol,rtol) modelSettings = ModelSettings(toleranceKeepInEdge=0,toleranceMoveToCore=1,toleranceInterruptSimulation=1) terminated,resurrected,obj,sspcs,srxns = reactionSystem.simulate( coreSpecies = reactionModel.core.species, coreReactions = reactionModel.core.reactions, edgeSpecies = reactionModel.edge.species, edgeReactions = reactionModel.edge.reactions, surfaceSpecies = [], surfaceReactions = [], pdepNetworks = pdepNetworks, modelSettings = modelSettings, simulatorSettings=simulatorSettings, ) assert terminated #unregister as a listener reactionSystem.detach(listener) return listener.speciesNames, listener.data
def runIgnitionThermoSensitivity(runChemkinJob, inputFile, dictionaryFile): """ Supply a runChemkinJob python function which returns the ignition delay with a chemkin file input. This will run finite difference sensitivities to enthalpies and save them to a csv file. """ from rmgpy.chemkin import loadChemkinFile, saveChemkinFile, getSpeciesIdentifier from rmgpy.quantity import Quantity speciesList, reactionList = loadChemkinFile(inputFile, dictionaryPath=dictionaryFile, readComments=False) num_species = len(speciesList) deltaH = Quantity(0.5, 'kcal/mol').value_si worksheet = csv.writer(file('ignition_thermo_sensitivity.csv', 'w')) worksheet.writerow([ 'Species', 'd[del H] (kcal/mol)', 'tau_high', 'tau_low', 'd[ln tau]/d[del H]' ]) print 'Running thermo sensitivity analysis using finite differences...' for index, species in enumerate(speciesList): species_index = index + 1 species_string = getSpeciesIdentifier(species) print 'At species {0} of {1}. {2}'.format(species_index, num_species, species_string) species.thermo.changeBaseEnthalpy(deltaH) saveChemkinFile('chem_temp.inp', speciesList, reactionList, verbose=False) tau_high = runChemkinJob('chem_temp.inp') species.thermo.changeBaseEnthalpy(-deltaH) # reset the thermo species.thermo.changeBaseEnthalpy(-deltaH) saveChemkinFile('chem_temp.inp', speciesList, reactionList, verbose=False) tau_low = runChemkinJob('chem_temp.inp') species.thermo.changeBaseEnthalpy(deltaH) # reset the kinetics if tau_high != 0 and tau_low != 0: sens = numpy.log(tau_high / tau_low) / (2 * deltaH) else: sens = 0 worksheet.writerow([species_string, '1', tau_high, tau_low, sens])
def renderSpecies(species, thermo=False): speciesLabel = getSpeciesIdentifier(species) speciesHTML = """ <tr class="species"> <td class="label">{label}</td> <td class="structure"><a href={url}><img src="species/{filename}" alt="{label}" title="{label}"></a></td> <td class="smiles">{smiles}</td> """.format( url=species.molecule[0].getURL(), filename=speciesLabel+'.png', label=speciesLabel, smiles=species.molecule[0].toSMILES(), ) if thermo: thermo = species.thermo speciesHTML += """<td class="thermo"> <table align="center"> <tr> <th>H298</th> <th>S298</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td>{H298:.2f}</td> <td>{S298:.2f}</td> <td>{Cp300:.2f}</td> <td>{Cp500:.2f}</td> <td>{Cp1000:.2f}</td> <td>{Cp1500:.2f}</td> </tr> <tr class="comment"><th colspan=6>Comments</th></tr> <tr class="comment"><td colspan=6>{comments}</td></tr> </table></td> """.format(H298=thermo.getEnthalpy(298)/4184, S298=thermo.getEntropy(298)/4.184, Cp300=thermo.getHeatCapacity(300)/4.184, Cp500=thermo.getHeatCapacity(300)/4.184, Cp1000=thermo.getHeatCapacity(300)/4.184, Cp1500=thermo.getHeatCapacity(300)/4.184, comments=thermo.comment) speciesHTML += """</tr>""" return speciesHTML
def runIgnitionThermoSensitivity(runChemkinJob, inputFile, dictionaryFile): """ Supply a runChemkinJob python function which returns the ignition delay with a chemkin file input. This will run finite difference sensitivities to enthalpies and save them to a csv file. """ from rmgpy.chemkin import loadChemkinFile, saveChemkinFile, getSpeciesIdentifier from rmgpy.quantity import Quantity speciesList, reactionList = loadChemkinFile(inputFile, dictionaryPath = dictionaryFile, readComments = False) num_species = len(speciesList) deltaH = Quantity(0.5, 'kcal/mol').value_si worksheet = csv.writer(file('ignition_thermo_sensitivity.csv', 'w')) worksheet.writerow(['Species', 'd[del H] (kcal/mol)', 'tau_high', 'tau_low', 'd[ln tau]/d[del H]']) logging.info('Running thermo sensitivity analysis using finite differences...') for index, species in enumerate(speciesList): species_index = index + 1 species_string = getSpeciesIdentifier(species) logging.info('At species {0} of {1}. {2}'.format(species_index, num_species, species_string)) species.thermo.changeBaseEnthalpy(deltaH) saveChemkinFile('chem_temp.inp', speciesList, reactionList, verbose = False) tau_high = runChemkinJob('chem_temp.inp') species.thermo.changeBaseEnthalpy(-deltaH) # reset the thermo species.thermo.changeBaseEnthalpy(-deltaH) saveChemkinFile('chem_temp.inp', speciesList, reactionList, verbose = False) tau_low = runChemkinJob('chem_temp.inp') species.thermo.changeBaseEnthalpy(deltaH) # reset the kinetics if tau_high != 0 and tau_low != 0: sens = numpy.log(tau_high / tau_low) / (2 * deltaH) else: sens = 0 worksheet.writerow([species_string, '1', tau_high, tau_low, sens])
def getFluxGraphEdgesDict(spc_rop_dict, core_reactions): graph_edges_dict = {} for rxn in core_reactions: for pair in rxn.pairs: if pair in graph_edges_dict: # get flux from spc_rop_dict species_string = getSpeciesIdentifier(pair[0]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[pair][rxn] = flux else: # for rxns like PDD + rad4 == PDD + rad1 species_string = getSpeciesIdentifier(pair[1]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[pair][rxn] = -flux elif (pair[1], pair[0]) in graph_edges_dict: # get flux from spc_rop_dict species_string = getSpeciesIdentifier(pair[1]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[(pair[1], pair[0])][rxn] = flux else: species_string = getSpeciesIdentifier(pair[0]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[(pair[1], pair[0])][rxn] = -flux else: # get flux from spc_rop_dict graph_edges_dict[pair] = {} species_string = getSpeciesIdentifier(pair[0]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[pair][rxn] = flux else: species_string = getSpeciesIdentifier(pair[1]) flux = getROPFlux(spc_rop_dict, species_string, rxn.index) if len(flux) > 0: graph_edges_dict[pair][rxn] = -flux return graph_edges_dict
def renderReactionROP(reactionROP, speciesList, showROP = True, idx=0): reaction = reactionROP.rxnObject reactionHTML = """ <tr class="reaction"><th><a href="{url}" title="Search on RMG website" class="searchlink">{index}.</a> {rxnString}</th> """.format(rxnString=reaction.toChemkin(speciesList, kinetics=False), url=reaction.getURL(), index=reaction.index) if showROP: reactionHTML += """<td>Flux = {flux}</td> <td>Flux % = {percent}</td>""".format(flux=reactionROP.data[idx], percent=reactionROP.fluxPercentage[idx]) reactionHTML += """</tr>""" reactantsHTML = [] for reactant in reaction.reactants: reactantsHTML.append('<a href="{mol_url}"><img src="species/{label}.png" alt="{label}" title="{label}"></a>'.format( mol_url=reactant.molecule[0].getURL(), label=getSpeciesIdentifier(reactant), )) reactionHTML += """<tr><td class="reactants">""" reactionHTML += ' + '.join(reactantsHTML) reactionHTML += """</td>""" reactionHTML += """<td class="reactionArrow">""" if reaction.reversible: reactionHTML +="⇔" else: reactionHTML += "→" reactionHTML += """</td>""" productsHTML = [] for product in reaction.products: productsHTML.append('<a href="{mol_url}"><img src="species/{label}.png" alt="{label}" title="{label}"></a>'.format( mol_url=product.molecule[0].getURL(), label=getSpeciesIdentifier(product), )) reactionHTML += """<td class="products">""" reactionHTML += ' + '.join(productsHTML) reactionHTML += """</td></tr>""" # reactionHTML += """ # <td class="family">{source}</td></tr> # """.format(source=reaction.getSource().label) # reactionHTML +=""" # <tr class="kinetics"> # <td colspan="4">{kinetics}</td> # </tr>""".format(kinetics=reaction.kinetics.toHTML()) reactionHTML += """ <tr class="chemkin"> <th>CHEMKIN String</th></tr> <tr class="chemkin"> <td>{chemkin}</td> </tr> """.format( chemkin=reaction.toChemkin(speciesList) ) return reactionHTML
def extractROPData(ckcsvFile, species="", speciesList=[], reactionList=[]): """ Extract the ROP information from a chemkin run for a single species. Returns a list of SpeciesROP objects. Will only return a list containing a single SpeciesROP object if there are no continuations. Will also identify corresponding species and reaction objects from a loaded Chemkin file if speciesList and reactionList are given. """ from rmgpy.chemkin import getSpeciesIdentifier # Pre-convert the speciesList and reactionList to strings speciesStringList = [] if speciesList: for spec in speciesList: speciesStringList.append(getSpeciesIdentifier(spec)) reactionStringList = [] if reactionList: for rxn in reactionList: rxnString = rxn.toChemkin(kinetics=False) if rxn.reversible: rxnString = rxnString.replace("=", "<=>") reactionStringList.append(rxnString) timeData = {} distanceData = {} speciesROPData = {} with open(ckcsvFile, "r") as stream: reader = csv.reader(stream) for row in reader: label = row[0].strip() tokens = label.split("_") if tokens[0] == "Time": if "Soln" in tokens[-1]: tsolnNumber = int(tokens[-1].split("#")[-1]) else: tsolnNumber = 0 tdata = numpy.array([float(r) for r in row[2:]], numpy.float) tunits = row[1].strip()[1:-1].lower() tdata *= {"sec": 1.0, "min": 60.0, "hr": 3600.0, "msec": 1e-3, "microsec": 1e-6}[tunits] timeData[tsolnNumber] = tdata continue if tokens[0] == "Distance": if "Soln" in tokens[-1]: dsolnNumber = int(tokens[-1].split("#")[-1]) else: dsolnNumber = 0 ddata = numpy.array([float(r) for r in row[2:]], numpy.float) dunits = row[1].strip()[1:-1].lower() ddata *= {"cm": 1.0, "mm": 0.1, "m": 100.0}[dunits] distanceData[dsolnNumber] = ddata continue if len(tokens) > 1: if tokens[1] == "ROP": species_string = tokens[0] if species == species_string: # Create a SpeciesROP object for this solution number if "Soln" in tokens[-1]: solutionNumber = int(tokens[-1].split("#")[-1]) else: # Only 1 solution set solutionNumber = 0 if solutionNumber not in speciesROPData.keys(): speciesROPData[solutionNumber] = SpeciesROP(species, ropData=[]) if speciesList: speciesROPData[solutionNumber].identifySpecies(speciesList, speciesStringList) if tokens[3] == "Total": # Total ROP rate speciesROPData[solutionNumber].totalRopData = numpy.array( [float(r) for r in row[2:]], numpy.float ) else: rxnString = tokens[2] rxnNumber = int(tokens[3].split("#")[-1]) rxnRopData = numpy.array([float(r) for r in row[2:]], numpy.float) dataObject = ReactionROP(rxnString=rxnString, rxnNumber=rxnNumber, data=rxnRopData) if reactionList: dataObject.identifyReaction(reactionList, reactionStringList) speciesROPData[solutionNumber].ropData.append(dataObject) speciesROPList = [] if timeData: if len(timeData) != len(speciesROPData): raise Exception("Number of time and ROP data sets are mismatched. Something went wrong during parsing") else: for solutionNumber in sorted(speciesROPData.keys()): speciesROP = speciesROPData[solutionNumber] speciesROP.xvarData = timeData[solutionNumber] speciesROPList.append(speciesROP) elif distanceData: if len(distanceData) != len(speciesROPData): raise Exception("Number of time and ROP data sets are mismatched. Something went wrong during parsing") else: for solutionNumber in sorted(speciesROPData.keys()): speciesROP = speciesROPData[solutionNumber] speciesROP.xvarData = distanceData[solutionNumber] speciesROPList.append(speciesROP) else: raise Exception("Did not parse any time or distance data. Something went wrong during parsing or simulation") return speciesROPList
def saveFluxAnalysisHTML(path, idx, speciesROP, negativeROPList=[], positiveROPList=[], speciesList=[]): """ Save the flux analysis for a given species to HTML. """ import os from rmgpy.molecule.draw import MoleculeDrawer from rmgpy.chemkin import getSpeciesIdentifier from .html import renderSpecies, renderReactionROP, STYLE_HEADER try: import jinja2 except ImportError: print "jinja2 package not found; HTML output will not be saved." return path = os.path.abspath(path) dirname = os.path.dirname(path) if not os.path.isdir(os.path.join(dirname, "species")): os.makedirs(os.path.join(dirname, "species")) for spec in speciesList: # Draw molecules speciesLabel = getSpeciesIdentifier(spec) fstr = os.path.join(dirname, "species", "{0}.png".format(speciesLabel)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], "png", fstr) except IndexError: raise Exception( "{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.".format( speciesLabel ) ) # title = 'Flux Analysis for Species {label} at x = {x}'.format(label=getSpeciesIdentifier(species), x=speciesROP.xvarData[idx]) # self.totalNegativeRate[idx] # print '{num}. {rxnString}\t Actual flux = {flux}\t % of Total Positive ROP = {fluxpercent}'.format(num=i+1, rxnString=rxnRop.rxnString, flux=rxnRop.data[idx], fluxpercent=rxnRop.fluxPercentage[idx]) environment = jinja2.Environment() environment.filters["renderSpecies"] = renderSpecies environment.filters["renderReactionROP"] = renderReactionROP environment.filters["getSpeciesIdentifier"] = getSpeciesIdentifier # Make HTML file template = environment.from_string( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>Flux Analysis for Species {{speciesROP.speciesObject|getSpeciesIdentifier}} at x = {{speciesROP.xvarData[idx]}}</title> {{style}} </head> <body> <h1>Flux Analysis for Species {{speciesROP.speciesObject|getSpeciesIdentifier}} at x = {{speciesROP.xvarData[idx]}}</h1> <h2>Species</h2> <table class="speciesList"> <tr><th>Label</th><th>Structure</th><th>SMILES</th><th>Thermo</th> {{speciesROP.speciesObject|renderSpecies(thermo=True)}} </table> <hr> <h3>Net ROP = {{speciesROP.totalRopData[idx]}}</h3> <hr size=3> <h2>Top Negative Flux Reactions Depleting Species {{speciesROP.speciesObject|getSpeciesIdentifier}}</h2> <h3>Total Negative ROP = {{speciesROP.totalNegativeRate[idx]}}</h3> <table class="reactionList" hide_comment hide_kinetics hide_chemkin"> {% for rxnROP in negativeROPList %} {{rxnROP|renderReactionROP(speciesList, showROP=True, idx=idx)}} {% endfor %} </table> <hr size=3> <h2>Top Positive Flux Reactions Accumulating Species {{speciesROP.speciesObject|getSpeciesIdentifier}}</h2> <h3>Total Positive ROP = {{speciesROP.totalPositiveRate[idx]}}</h3> <table class="reactionList" hide_comment hide_kinetics hide_chemkin"> {% for rxnROP in positiveROPList %} {{rxnROP|renderReactionROP(speciesList, showROP=True, idx=idx)}} {% endfor %} </table> </body> </html> """ ) f = open(path, "w") f.write( template.render( style=STYLE_HEADER, idx=idx, speciesROP=speciesROP, negativeROPList=negativeROPList, positiveROPList=positiveROPList, speciesList=speciesList, ) ) f.close()
def toChemkin(self): """ Return the chemkin-formatted string for this species. """ from rmgpy.chemkin import getSpeciesIdentifier return getSpeciesIdentifier(self)
def simulate(self): """ Run all the conditions as a cantera simulation. Returns the data as a list of tuples containing: (time, [list of temperature, pressure, and species data]) for each reactor condition """ # Get all the cantera names for the species speciesNamesList = [getSpeciesIdentifier(species) for species in self.speciesList] allData = [] for condition in self.conditions: # First translate the molFrac from species objects to species names newMolFrac = {} for key, value in condition.molFrac.iteritems(): newkey = getSpeciesIdentifier(key) newMolFrac[newkey] = value # Set Cantera simulation conditions if condition.V0 is None: self.model.TPX = condition.T0.value_si, condition.P0.value_si, newMolFrac elif condition.P0 is None: self.model.TDX = condition.T0.value_si, 1.0/condition.V0.value_si, newMolFrac else: raise Exception("Cantera conditions in which T0 and P0 or T0 and V0 are not the specified state variables are not yet implemented.") # Choose reactor if condition.reactorType == 'IdealGasReactor': canteraReactor=ct.IdealGasReactor(self.model) elif condition.reactorType == 'IdealGasConstPressureReactor': canteraReactor=ct.IdealConstPressureGasReactor(self.model) else: raise Exception('Other types of reactor conditions are currently not supported') # Run this individual condition as a simulation canteraSimulation=ct.ReactorNet([canteraReactor]) # Initialize the variables to be saved times=[] temperature=[] pressure=[] speciesData=[] # Begin integration time = 0.0 # Run the simulation over 100 time points while canteraSimulation.time<condition.reactionTime.value_si: # Advance the state of the reactor network in time from the current time to time t [s], taking as many integrator timesteps as necessary. canteraSimulation.step(condition.reactionTime.value_si) times.append(canteraSimulation.time) temperature.append(canteraReactor.T) pressure.append(canteraReactor.thermo.P) speciesData.append(canteraReactor.thermo[speciesNamesList].X) # Convert speciesData to a numpy array speciesData=np.array(speciesData) # Resave data into generic data objects time = GenericData(label = 'Time', data = times, units = 's') temperature = GenericData(label='Temperature', data = temperature, units = 'K') pressure = GenericData(label='Pressure', data = pressure, units = 'Pa') conditionData = [] conditionData.append(temperature) conditionData.append(pressure) for index, species in enumerate(self.speciesList): # Create generic data object that saves the species object into the species object. To allow easier manipulate later. speciesGenericData = GenericData(label=speciesNamesList[index], species = species, data = speciesData[:,index], index = species.index ) conditionData.append(speciesGenericData) allData.append((time,conditionData)) return allData
def saveOutputHTML(path, reactionModel, partCoreEdge='core'): """ Save the current set of species and reactions of `reactionModel` to an HTML file `path` on disk. As part of this process, drawings of all species are created in the species folder (if they don't already exist) using the :mod:`rmgpy.molecule.draw` module. The :mod:`jinja` package is used to generate the HTML; if this package is not found, no HTML will be generated (but the program will carry on). """ from model import PDepReaction from rmgpy.molecule.draw import MoleculeDrawer from rmgpy.chemkin import getSpeciesIdentifier try: import jinja2 except ImportError: logging.warning( "jinja2 package not found; HTML output will not be saved.") return path = os.path.abspath(path) dirname = os.path.dirname(path) # Prepare parameters to pass to jinja template title = 'RMG Output' if partCoreEdge == 'core': species = reactionModel.core.species[:] + reactionModel.outputSpeciesList if not os.path.isdir(os.path.join(dirname, 'species')): os.makedirs(os.path.join(dirname, 'species')) elif partCoreEdge == 'edge': species = reactionModel.edge.species[:] + reactionModel.outputSpeciesList if not os.path.isdir(os.path.join(dirname, 'species_edge')): os.makedirs(os.path.join(dirname, 'species_edge')) re_index_search = re.compile(r'\((\d+)\)$').search for spec in species: # if the species dictionary came from an RMG-Java job, make them prettier # We use the presence of a trailing index on the label to discern this # (A single open parenthesis is not enough (e.g. when using SMILES strings as labels!) match = re_index_search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary if partCoreEdge == 'core': fstr = os.path.join(dirname, 'species', '{0}.png'.format(spec)) elif partCoreEdge == 'edge': fstr = os.path.join(dirname, 'species_edge', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise OutputError( "{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files." .format(getSpeciesIdentifier(spec))) # We want to keep species sorted in the original order in which they were added to the RMG core. # Rather than ordered by index # species.sort(key=lambda x: x.index) if partCoreEdge == 'core': reactions = [rxn for rxn in reactionModel.core.reactions ] + reactionModel.outputReactionList elif partCoreEdge == 'edge': reactions = [rxn for rxn in reactionModel.edge.reactions ] + reactionModel.outputReactionList # We want to keep reactions sorted in original order in which they were added to core # rather than ordered by index #reactions.sort(key=lambda x: x.index) familyCount = {} for rxn in reactions: if isinstance(rxn, PDepReaction): family = "PDepNetwork" else: family = rxn.getSource().label if family in familyCount: familyCount[family] += 1 else: familyCount[family] = 1 families = familyCount.keys() families.sort() ## jinja2 filters etc. to_remove_from_css_names = re.compile('[/.\-+,]') def csssafe(input): "Replace unsafe CSS class name characters with an underscore." return to_remove_from_css_names.sub('_', input) environment = jinja2.Environment() environment.filters['csssafe'] = csssafe # Make HTML file if partCoreEdge == 'core': template = environment.from_string( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>{{ title }}</title> <style type="text/css"> body { font-family: sans-serif; } a { color: #993333; text-decoration: none; } a:visited { color: #993333; } a:hover { text-decoration: underline; } table.speciesList, table.reactionList { width: 100%; border-collapse: collapse; } table.speciesList th, table.reactionList th { text-align: left; } tr.reaction td { border-top: 1px solid #808080; } td.reactants { text-align: right; } td.products { text-align: left; } td.reactionArrow { text-align: center; font-size: 16px; } td.species img, td.reactants img, td.products img { vertical-align: middle; } tr.comment { font-size: small; } tr.kinetics { font-size: small; } .KineticsData { # border: 1px solid gray; } .KineticsData th { width: 15em; word-wrap: none; } .KineticsData td { width: 3em; } .chemkin, .KineticsData_repr { white-space: pre-wrap; font-size: x-small; font-family: "Andale Mono", monospace; } .hide_comment .comment{ display: none !important; } .hide_kinetics .kinetics{ display: none !important; } .hide_chemkin .chemkin{ display: none !important; } </style> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script> <script type="text/javascript" src="../../../external/jquery.min.js"></script> <script type="text/javascript"> function updateFamily(family) { if (family.checked) { $("."+family.value).show(); } else { $("."+family.value).hide(); } } function updateDetails(type) { if (type.checked) { $(".reactionList").removeClass("hide_"+type.value); } else { $(".reactionList").addClass("hide_"+type.value); } } function checkAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = true; updateFamily(this); }); return false; } function uncheckAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = false; updateFamily(this); }); return false; } function checkAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = true; updateDetails(this); }); return false; } function uncheckAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = false; updateDetails(this); }); return false; } $(document).ready(function() { checkAllFamilies(); uncheckAllDetails(); }); </script> </head> <body> <h1>{{ title }}</h1> <h2>Species ({{ species|length }})</h2> <table class="speciesList"> <tr><th>Index</th><th>Structure</th><th>Label</th><th>Mol. Wt. (g/mol)</th></tr> {% for spec in species %} <tr class="species"> <td class="index"> {{ spec.index }}.</td> <td class="structure"><a href={{ spec.molecule[0].getURL() }}><img src="species/{{ spec|replace('#','%23') }}.png" alt="{{ spec }}" title="{{ spec }}"></a></td> <td class="label">{{ spec.label }}</td> <td>{{ "%.2f"|format(spec.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> {% if spec.thermo %} <tr> <td> <table align="center"> <tr> <th>H298</th> <th>S298</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td> {% if spec.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec.thermo.getEnthalpy(298) / 4184) }} {% endif %} </td> <td>{% if spec.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec.thermo.getEntropy(298) / 4.184) }} {% endif %}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(300) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(500) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(1000) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(1500) / 4.184) }}</td> </tr> </table> </td></tr> {% endif %} {% endfor %} </table> <h2>Reactions ({{ reactions|length }})</h2> <form id='familySelector' action=""> <h4>Reaction families:</h4> {% for family in families %} <input type="checkbox" id="{{ family|csssafe }}" name="family" value="{{ family|csssafe }}" checked="checked" onclick="updateFamily(this);"><label for="{{ family|csssafe }}">{{ family }} ({{ familyCount[family] }} rxn{{ 's' if familyCount[family] != 1 }})</label><br> {% endfor %} <a href="javascript:checkAllFamilies();" onclick="checkAllFamilies()">check all</a> <a href="javascript:uncheckAllFamilies();" onclick="uncheckAllFamilies();">uncheck all</a><br> <h4>Reaction Details:</h4> <input type="checkbox" id="kinetics" name="detail" value="kinetics" onclick="updateDetails(this);"><label for="kinetics">Kinetics</label><br> <input type="checkbox" id="comment" name="detail" value="comment" onclick="updateDetails(this);"><label for="comment">Comments</label><br> <input type="checkbox" id="chemkin" name="detail" value="chemkin" onclick="updateDetails(this);"><label for="chemkin">Chemkin strings</label><br> <a href="javascript:checkAllDetails();" onclick="checkAllDetails()">check all</a> <a href="javascript:uncheckAllDetails();" onclick="uncheckAllDetails();">uncheck all</a> </form> <h4>Reaction List:</h4> <table class="reactionList hide_comment hide_kinetics hide_chemkin"> <tr><th>Index</th><th colspan="3" style="text-align: center;">Reaction</th><th>Family</th></tr> {% for rxn in reactions %} <tr class="reaction {{ rxn.getSource().label|csssafe }}"> <td class="index"><a href="{{ rxn.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn.index }}.</a></td> <td class="reactants">{% for reactant in rxn.reactants %}<a href="{{ reactant.molecule[0].getURL() }}"><img src="species/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f g/mol"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow">{% if rxn.reversible %}⇔{% else %}→{% endif %}</td> <td class="products">{% for product in rxn.products %}<a href="{{ product.molecule[0].getURL() }}"><img src="species/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f g/mol"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="family">{{ rxn.getSource().label }}</td> </tr> <tr class="kinetics {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.toHTML() }}</td> </tr> <tr class="chemkin {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.toChemkin(species) }}</td> </tr> <tr class="comment {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.comment }}</td> </tr> {% endfor %} </table> </body> </html> """) elif partCoreEdge == 'edge': template = environment.from_string( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>{{ title }}</title> <style type="text/css"> body { font-family: sans-serif; } a { color: #993333; text-decoration: none; } a:visited { color: #993333; } a:hover { text-decoration: underline; } table.speciesList, table.reactionList { width: 100%; border-collapse: collapse; } table.speciesList th, table.reactionList th { text-align: left; } tr.reaction td { border-top: 1px solid #808080; } td.reactants { text-align: right; } td.products { text-align: left; } td.reactionArrow { text-align: center; font-size: 16px; } td.species img, td.reactants img, td.products img { vertical-align: middle; } tr.comment { font-size: small; } tr.kinetics { font-size: small; } .KineticsData { # border: 1px solid gray; } .KineticsData th { width: 15em; word-wrap: none; } .KineticsData td { width: 3em; } .chemkin, .KineticsData_repr { white-space: pre-wrap; font-size: x-small; font-family: "Andale Mono", monospace; } .hide_comment .comment{ display: none !important; } .hide_kinetics .kinetics{ display: none !important; } .hide_chemkin .chemkin{ display: none !important; } </style> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script> <script type="text/javascript" src="../../../external/jquery.min.js"></script> <script type="text/javascript"> function updateFamily(family) { if (family.checked) { $("."+family.value).show(); } else { $("."+family.value).hide(); } } function updateDetails(type) { if (type.checked) { $(".reactionList").removeClass("hide_"+type.value); } else { $(".reactionList").addClass("hide_"+type.value); } } function checkAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = true; updateFamily(this); }); return false; } function uncheckAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = false; updateFamily(this); }); return false; } function checkAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = true; updateDetails(this); }); return false; } function uncheckAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = false; updateDetails(this); }); return false; } $(document).ready(function() { checkAllFamilies(); uncheckAllDetails(); }); </script> </head> <body> <h1>{{ title }}</h1> <h2>Species ({{ species|length }})</h2> <table class="speciesList"> <tr><th>Index</th><th>Structure</th><th>Label</th><th>Mol. Wt. (g/mol)</th></tr> {% for spec in species %} <tr class="species"> <td class="index"> {{ spec.index }}.</td> <td class="structure"><a href={{ spec.molecule[0].getURL() }}><img src="species_edge/{{ spec|replace('#','%23') }}.png" alt="{{ spec }}" title="{{ spec }}"></a></td> <td class="label">{{ spec.label }}</td> <td>{{ "%.2f"|format(spec.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> {% if spec.thermo %} <tr> <td> <table align="center"> <tr> <th>H298</th> <th>S298</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td> {% if spec.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec.thermo.getEnthalpy(298) / 4184) }} {% endif %} </td> <td>{% if spec.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec.thermo.getEntropy(298) / 4.184) }} {% endif %}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(300) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(500) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(1000) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(1500) / 4.184) }}</td> </tr> </table> </td></tr> {% endif %} {% endfor %} </table> <h2>Reactions ({{ reactions|length }})</h2> <form id='familySelector' action=""> <h4>Reaction families:</h4> {% for family in families %} <input type="checkbox" id="{{ family|csssafe }}" name="family" value="{{ family|csssafe }}" checked="checked" onclick="updateFamily(this);"><label for="{{ family|csssafe }}">{{ family }} ({{ familyCount[family] }} rxn{{ 's' if familyCount[family] != 1 }})</label><br> {% endfor %} <a href="javascript:checkAllFamilies();" onclick="checkAllFamilies()">check all</a> <a href="javascript:uncheckAllFamilies();" onclick="uncheckAllFamilies();">uncheck all</a><br> <h4>Reaction Details:</h4> <input type="checkbox" id="kinetics" name="detail" value="kinetics" onclick="updateDetails(this);"><label for="kinetics">Kinetics</label><br> <input type="checkbox" id="comment" name="detail" value="comment" onclick="updateDetails(this);"><label for="comment">Comments</label><br> <input type="checkbox" id="chemkin" name="detail" value="chemkin" onclick="updateDetails(this);"><label for="chemkin">Chemkin strings</label><br> <a href="javascript:checkAllDetails();" onclick="checkAllDetails()">check all</a> <a href="javascript:uncheckAllDetails();" onclick="uncheckAllDetails();">uncheck all</a> </form> <h4>Reaction List:</h4> <table class="reactionList hide_comment hide_kinetics hide_chemkin"> <tr><th>Index</th><th colspan="3" style="text-align: center;">Reaction</th><th>Family</th></tr> {% for rxn in reactions %} <tr class="reaction {{ rxn.getSource().label|csssafe }}"> <td class="index"><a href="{{ rxn.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn.index }}.</a></td> <td class="reactants">{% for reactant in rxn.reactants %}<a href="{{ reactant.molecule[0].getURL() }}"><img src="species_edge/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f g/mol"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow">{% if rxn.reversible %}⇔{% else %}→{% endif %}</td> <td class="products">{% for product in rxn.products %}<a href="{{ product.molecule[0].getURL() }}"><img src="species_edge/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f g/mol"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="family">{{ rxn.getSource().label }}</td> </tr> <tr class="kinetics {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.toHTML() }}</td> </tr> <tr class="chemkin {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.toChemkin(species) }}</td> </tr> <tr class="comment {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.comment }}</td> </tr> {% endfor %} </table> </body> </html> """) f = open(path, 'w') f.write( template.render(title=title, species=species, reactions=reactions, families=families, familyCount=familyCount)) f.close()
def simulate(self): """ Run all the conditions as a cantera simulation. Returns the data as a list of tuples containing: (time, [list of temperature, pressure, and species data]) for each reactor condition """ # Get all the cantera names for the species speciesNamesList = [getSpeciesIdentifier(species) for species in self.speciesList] inertIndexList = [self.speciesList.index(species) for species in self.speciesList if species.index == -1] allData = [] for condition in self.conditions: # First translate the molFrac from species objects to species names newMolFrac = {} for key, value in condition.molFrac.iteritems(): newkey = getSpeciesIdentifier(key) newMolFrac[newkey] = value # Set Cantera simulation conditions if condition.V0 is None: self.model.TPX = condition.T0.value_si, condition.P0.value_si, newMolFrac elif condition.P0 is None: self.model.TDX = condition.T0.value_si, 1.0/condition.V0.value_si, newMolFrac else: raise Exception("Cantera conditions in which T0 and P0 or T0 and V0 are not the specified state variables are not yet implemented.") # Choose reactor if condition.reactorType == 'IdealGasReactor': canteraReactor=ct.IdealGasReactor(self.model) elif condition.reactorType == 'IdealGasConstPressureReactor': canteraReactor=ct.IdealGasConstPressureReactor(contents=self.model) elif condition.reactorType == 'IdealGasConstPressureTemperatureReactor': canteraReactor=ct.IdealGasConstPressureReactor(contents=self.model, energy='off') else: raise Exception('Other types of reactor conditions are currently not supported') # Run this individual condition as a simulation canteraSimulation=ct.ReactorNet([canteraReactor]) numCtReactions = len(self.model.reactions()) if self.sensitiveSpecies: if ct.__version__ == '2.2.1': print 'Warning: Cantera version 2.2.1 may not support sensitivity analysis unless SUNDIALS was used during compilation.' print 'Warning: Upgrade to newer of Cantera in anaconda using the command "conda update -c rmg cantera"' # Add all the reactions as part of the analysis for i in range(numCtReactions): canteraReactor.add_sensitivity_reaction(i) # Set the tolerances for the sensitivity coefficients canteraSimulation.rtol_sensitivity = 1e-4 canteraSimulation.atol_sensitivity = 1e-6 # Initialize the variables to be saved times=[] temperature=[] pressure=[] speciesData=[] sensitivityData = [] # Begin integration time = 0.0 # Run the simulation over 100 time points while canteraSimulation.time<condition.reactionTime.value_si: # Advance the state of the reactor network in time from the current time to time t [s], taking as many integrator timesteps as necessary. canteraSimulation.step(condition.reactionTime.value_si) times.append(canteraSimulation.time) temperature.append(canteraReactor.T) pressure.append(canteraReactor.thermo.P) speciesData.append(canteraReactor.thermo[speciesNamesList].X) if self.sensitiveSpecies: # Cantera returns mass-based sensitivities rather than molar concentration or mole fraction based sensitivities. # The equation for converting between them is: # # d ln xi = d ln wi - sum_(species i) (dln wi) (xi) # # where xi is the mole fraction of species i and wi is the mass fraction of species i massFracSensitivityArray = canteraSimulation.sensitivities() if condition.reactorType =='IdealGasReactor': # Row 0: mass, Row 1: volume, Row 2: internal energy or temperature, Row 3+: mass fractions of species massFracSensitivityArray = massFracSensitivityArray[3:,:] elif condition.reactorType == 'IdealGasConstPressureReactor' or condition.reactorType == 'IdealGasConstPressureTemperatureReactor': # Row 0: mass, Row 1: enthalpy or temperature, Row 2+: mass fractions of the species massFracSensitivityArray = massFracSensitivityArray[2:,:] else: raise Exception('Other types of reactor conditions are currently not supported') for i in range(len(massFracSensitivityArray)): massFracSensitivityArray[i] *= speciesData[-1][i] sensitivityArray= np.zeros(len(self.sensitiveSpecies)*len(self.model.reactions())) for index, species in enumerate(self.sensitiveSpecies): for j in range(numCtReactions): sensitivityArray[numCtReactions*index+j] = canteraSimulation.sensitivity(species.toChemkin(),j) for i in range(len(massFracSensitivityArray)): if i not in inertIndexList: # massFracSensitivity for inerts are returned as nan in Cantera, so we must not include them here sensitivityArray[numCtReactions*index+j] -= massFracSensitivityArray[i][j] sensitivityData.append(sensitivityArray) # Convert speciesData and sensitivityData to a numpy array speciesData=np.array(speciesData) sensitivityData = np.array(sensitivityData) # Resave data into generic data objects time = GenericData(label = 'Time', data = times, units = 's') temperature = GenericData(label='Temperature', data = temperature, units = 'K') pressure = GenericData(label='Pressure', data = pressure, units = 'Pa') conditionData = [] conditionData.append(temperature) conditionData.append(pressure) for index, species in enumerate(self.speciesList): # Create generic data object that saves the species object into the species object. To allow easier manipulate later. speciesGenericData = GenericData(label=speciesNamesList[index], species = species, data = speciesData[:,index], index = species.index ) conditionData.append(speciesGenericData) reactionSensitivityData = [] for index, species in enumerate(self.sensitiveSpecies): for j in range(numCtReactions): reactionSensitivityGenericData = GenericData(label = 'dln[{0}]/dln[k{1}]: {2}'.format(species.toChemkin(),j+1, self.model.reactions()[j]), species = species, reaction = self.model.reactions()[j], data = sensitivityData[:,numCtReactions*index+j], index = j+1, ) reactionSensitivityData.append(reactionSensitivityGenericData) allData.append((time,conditionData,reactionSensitivityData)) return allData
chemkinPath = args.chemkinPath[0] #Save output in same directory as input outputDirectory = os.path.dirname(os.path.abspath(chemkinPath)) # Initialize (and clear!) the output files for the job outputFile = os.path.join(outputDirectory, 'evaluated_NASA_polynomial.py') with open(outputFile, 'w') as f: pass #Load the checmkin file speciesList, reactionList = loadChemkinFile(chemkinPath) # Make full species identifier the species labels for species in speciesList: species.label = getSpeciesIdentifier(species) species.index = -1 # Evaluate the NASA polynomials for thermo properties of each species at the specified T's and save in tabular format # to output file for i in range(len(speciesList)): species = speciesList[i] if species.thermo: f = open(outputFile, 'a') f.write('# Thermodynamics for {0}:\n'.format(species.label)) H298 = species.getThermoData().getEnthalpy(298) / 4184. S298 = species.getThermoData().getEntropy(298) / 4.184 f.write('# Enthalpy of formation (298 K) = {0:9.3f} kcal/mol\n'.format(H298)) f.write('# Entropy of formation (298 K) = {0:9.3f} cal/(mol*K)\n'.format(S298))
def update_all_labels(self): """Update species and reaction labels to match Chemkin labels""" for reaction in self.reactions: reaction.label = reaction.toChemkin(kinetics=False) for species in self.species: species.label = getSpeciesIdentifier(species)
help='The path of the chemkin file') parser.add_argument('dictionaryPath', metavar='DICTIONARY', type=str, nargs=1, help='The path of the RMG dictionary file') parser.add_argument('name', metavar='NAME', type=str, nargs=1, help='Name of the chemkin library to be saved') args = parser.parse_args() chemkinPath = args.chemkinPath[0] dictionaryPath = args.dictionaryPath[0] name = args.name[0] speciesList, reactionList = loadChemkinFile(chemkinPath, dictionaryPath) # Make full species identifier the species labels for species in speciesList: species.label = getSpeciesIdentifier(species) species.index = -1 # load thermo library entries thermoLibrary = ThermoLibrary(name=name) for i in range(len(speciesList)): species = speciesList[i] if species.thermo: thermoLibrary.loadEntry(index = i + 1, label = species.label, molecule = species.molecule[0].toAdjacencyList(), thermo = species.thermo, shortDesc = species.thermo.comment ) else: logging.warning('Species {0} did not contain any thermo data and was omitted from the thermo library.'.format(str(species)))
def saveDiffHTML(path, commonSpeciesList, speciesList1, speciesList2, commonReactions, uniqueReactions1, uniqueReactions2): """ This function outputs the species and reactions on an HTML page for the comparison of two RMG models. """ from model import PDepReaction from rmgpy.kinetics import Arrhenius, MultiArrhenius, MultiPDepArrhenius from rmgpy.molecule.draw import MoleculeDrawer try: import jinja2 except ImportError: logging.warning("jinja package not found; HTML output will not be saved.") return path = os.path.abspath(path) dirname = os.path.dirname(path) # Prepare parameters to pass to jinja template title = 'RMG Model Comparison' speciesList = [spec1 for spec1, spec2 in commonSpeciesList] + [spec2 for spec1, spec2 in commonSpeciesList] + speciesList1 + speciesList2 re_index = re.compile(r'\((\d+)\)$') if not os.path.isdir(os.path.join(dirname,'species1')): os.makedirs(os.path.join(dirname,'species1')) if not os.path.isdir(os.path.join(dirname,'species2')): os.makedirs(os.path.join(dirname,'species2')) for spec1, spec2 in commonSpeciesList: # if the species dictionary came from an RMG-Java job, make them prettier # We use the presence of a trailing index on the label to discern this # (A single open parenthesis is not enough (e.g. when using SMILES strings as labels!) match1 = re_index.search(spec1.label) if match1: spec1.index = int(match1.group(0)[1:-1]) spec1.label = spec1.label[0:match1.start()] match2 = re_index.search(spec2.label) if match2: spec2.index = int(match2.group(0)[1:-1]) spec2.label = spec2.label[0:match2.start()] # Draw molecules if necessary fstr = os.path.join(dirname, 'species1', '{0}.png'.format(spec1)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec1.molecule[0], 'png', fstr) except IndexError: raise OutputError('{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.'.format(getSpeciesIdentifier(spec1))) fstr = os.path.join(dirname, 'species2', '{0}.png'.format(spec2)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec2.molecule[0], 'png', fstr) except IndexError: raise OutputError('{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.'.format(getSpeciesIdentifier(spec2))) for spec in speciesList1: match = re_index.search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary fstr = os.path.join(dirname, 'species1', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise OutputError('{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.'.format(getSpeciesIdentifier(spec))) for spec in speciesList2: match = re_index.search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary fstr = os.path.join(dirname, 'species2', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise OutputError('{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.'.format(getSpeciesIdentifier(spec))) familyCount1 = {} familyCount2 = {} for rxn1, rxn2 in commonReactions: if isinstance(rxn2.kinetics, (MultiArrhenius,MultiPDepArrhenius)): rxn2.duplicate = True if isinstance(rxn1, PDepReaction): family = "PDepNetwork" else: family = rxn1.getSource().label if family in familyCount1: familyCount1[family] += 1 familyCount2[family] += 1 else: familyCount1[family] = 1 familyCount2[family] = 1 for rxn in uniqueReactions1: if isinstance(rxn, PDepReaction): family = "PDepNetwork" else: family = rxn.getSource().label if family in familyCount1: familyCount1[family] += 1 else: familyCount1[family] = 1 for rxn in uniqueReactions2: if isinstance(rxn, PDepReaction): family = "PDepNetwork" else: family = rxn.getSource().label if family in familyCount2: familyCount2[family] += 1 else: familyCount2[family] = 1 families1 = familyCount1.keys() families2 = familyCount2.keys() families1.sort() families2.sort() ## jinja2 filters etc. to_remove_from_css_names = re.compile('[/.\-+,]') def csssafe(input): "Replace unsafe CSS class name characters with an underscore." return to_remove_from_css_names.sub('_',input) environment = jinja2.Environment() environment.filters['csssafe'] = csssafe # Make HTML file template = environment.from_string( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>{{ title }}</title> <style type="text/css"> body { font-family: sans-serif; } a { color: #993333; text-decoration: none; } a:visited { color: #993333; } a:hover { text-decoration: underline; } table.speciesList, table.reactionList { width: 100%; border-collapse: collapse; } table.speciesList th, table.reactionList th { text-align: left; } tr.reaction td { border-top: 1px solid #808080; } td.reactants { text-align: right; } td.products { text-align: left; } td.reactionArrow { text-align: center; font-size: 16px; } td.species img, td.reactants img, td.products img { vertical-align: middle; } tr.comment { font-size: small; } tr.kinetics { font-size: small; } .KineticsData { # border: 1px solid gray; } .KineticsData th { width: 15em; word-wrap: none; } .KineticsData td { width: 3em; } .chemkin, .KineticsData_repr { white-space: pre-wrap; font-size: x-small; font-family: "Andale Mono", monospace; } .hide_comment .comment{ display: none !important; } .hide_kinetics .kinetics{ display: none !important; } .hide_chemkin .chemkin{ display: none !important; } </style> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script> <script type="text/javascript" src="../../../external/jquery.min.js"></script> <script type="text/javascript"> function updateFamily(family) { if (family.checked) { $("."+family.value).show(); } else { $("."+family.value).hide(); } } function updateDetails(type) { if (type.checked) { $(".reactionList").removeClass("hide_"+type.value); } else { $(".reactionList").addClass("hide_"+type.value); } } function checkAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = true; updateFamily(this); }); return false; } function uncheckAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = false; updateFamily(this); }); return false; } function checkAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = true; updateDetails(this); }); return false; } function uncheckAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = false; updateDetails(this); }); return false; } $(document).ready(function() { checkAllFamilies(); uncheckAllDetails(); }); </script> </head> <body> <h1>{{ title }}</h1> <h2 align="center">Common Species ({{ commonSpecies|length }})</h2> <table width="100%"> {% for spec1, spec2 in commonSpecies %} <tr> <td width="100%" colspan="4"> <table align="center"> <tr class="species"> <td>{{ spec1.label }}</td> <td class="structure" align="center"><a href="{{spec1.molecule[0].getURL()}}"><img src="species1/{{ spec1|replace('#','%23') }}.png" alt="{{ spec1 }}" title="{{ spec1 }}"></a></td> <td>{{ "%.2f"|format(spec1.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> </table> </td> </tr> {% if spec1.thermo and spec2.thermo %} {% if spec1.thermo.isIdenticalTo(spec2.thermo) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="blue">IDENTICAL THERMO WAS FOUND FOR THIS SPECIES.</font></div> </tr> {% elif spec1.thermo.isSimilarTo(spec2.thermo) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="green">SIMILAR THERMO WAS FOUND FOR THIS SPECIES.</font></div> </tr> {% else %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="red">DIFFERENT THERMO WAS FOUND FOR THIS SPECIES.</font></div> </tr> {% endif%} <tr> <td width="10%">{{ spec1.index }}. </td> <td width="40%"> <table width="100%"> <tr> <th>H298</th> <th>S298</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td>{% if spec1.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec1.thermo.getEnthalpy(298) / 4184) }} {% endif %}</td> <td> {% if spec1.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec1.thermo.getEntropy(298) / 4.184) }} {% endif %}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(300) / 4.184) }}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(500) / 4.184) }}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(1000) / 4.184) }}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(1500) / 4.184) }}</td> </tr> </table> </td> <td width="10%">{{ spec2.index }}.</td> <td width="40%"> <table width="100%"> <tr> <th>H298</th> <th>S298</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td>{% if spec2.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec2.thermo.getEnthalpy(298) / 4184) }} {% endif %}</td> <td>{% if spec2.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec2.thermo.getEntropy(298) / 4.184) }} {% endif %}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(300) / 4.184) }}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(500) / 4.184) }}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(1000) / 4.184) }}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(1500) / 4.184) }}</td> </tr> </table> </td> </tr> {% endif %} <tr> <td width="100%" colspan="4"><hr/></td> </tr> {% endfor %} </table> <table width=100%> <tr colspan="2"> <td width=50% valign="top"> <h2>Model 1: Unique Species ({{ speciesList1|length }})</h2> <table class="speciesList"> <tr><th>Index</th><th>Structure</th><th>Label</th><th>Mol. Wt. (g/mol)</th></tr> {% for spec in speciesList1 %} <tr class="species"> <td class="index"> {{ spec.index }}.</td> <td class="structure"><a href="{{ spec.molecule[0].getURL() }}"><img src="species1/{{ spec|replace('#','%23') }}.png" alt="{{ spec }}" title="{{ spec }}"></a></td> <td class="label">{{ spec.label }}</td> <td>{{ "%.2f"|format(spec.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> {% endfor %} </table> </td> <td width=50% valign="top"> <h2>Model 2: Unique Species ({{ speciesList2|length }})</h2> <table class="speciesList"> <tr><th>Index</th><th>Structure</th><th>Label</th><th>Mol. Wt. (g/mol)</th></tr> {% for spec in speciesList2 %} <tr class="species"> <td class="index"> {{ spec.index }}.</td> <td class="structure"><a href="{{ spec.molecule[0].getURL() }}"><img src="species2/{{ spec|replace('#','%23') }}.png" alt="{{ spec }}" title="{{ spec }}"></a></td> <td class="label">{{ spec.label }}</td> <td>{{ "%.2f"|format(spec.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> {% endfor %} </table> </td></tr> <tr colspan="2"> <td width=50% valign="top"> <h2>Model 1 Reactions ({{ commonReactions|length + uniqueReactions1|length}})</h2> <form id='familySelector' action=""> <h4>Reaction families:</h4> {% for family in families1 %} <input type="checkbox" id="{{ family|csssafe }}" name="family" value="{{ family|csssafe }}" checked="checked" onclick="updateFamily(this);"><label for="{{ family|csssafe }}">{{ family }} ({{ familyCount1[family] }} rxn{{ 's' if familyCount1[family] != 1 }})</label><br> {% endfor %} <a href="javascript:checkAllFamilies();" onclick="checkAllFamilies()">check all</a> <a href="javascript:uncheckAllFamilies();" onclick="uncheckAllFamilies();">uncheck all</a><br> <h4>Reaction Details:</h4> <input type="checkbox" id="kinetics" name="detail" value="kinetics" onclick="updateDetails(this);"><label for="kinetics">Kinetics</label><br> <input type="checkbox" id="comment" name="detail" value="comment" onclick="updateDetails(this);"><label for="comment">Comments</label><br> <input type="checkbox" id="chemkin" name="detail" value="chemkin" onclick="updateDetails(this);"><label for="chemkin">Chemkin strings</label><br> <a href="javascript:checkAllDetails();" onclick="checkAllDetails()">check all</a> <a href="javascript:uncheckAllDetails();" onclick="uncheckAllDetails();">uncheck all</a> </form> </td> <td width=50% valign="top"> <h2>Model 2 Reactions ({{ commonReactions|length +uniqueReactions2|length}})</h2> <form id='familySelector' action=""> <h4>Reaction families:</h4> {% for family in families2 %} <input type="checkbox" id="{{ family|csssafe }}" name="family" value="{{ family|csssafe }}" checked="checked" onclick="updateFamily(this);"><label for="{{ family|csssafe }}">{{ family }} ({{ familyCount2[family] }} rxn{{ 's' if familyCount2[family] != 1 }})</label><br> {% endfor %} <a href="javascript:checkAllFamilies();" onclick="checkAllFamilies()">check all</a> <a href="javascript:uncheckAllFamilies();" onclick="uncheckAllFamilies();">uncheck all</a><br> <h4>Reaction Details:</h4> <input type="checkbox" id="kinetics" name="detail" value="kinetics" onclick="updateDetails(this);"><label for="kinetics">Kinetics</label><br> <input type="checkbox" id="comment" name="detail" value="comment" onclick="updateDetails(this);"><label for="comment">Comments</label><br> <input type="checkbox" id="chemkin" name="detail" value="chemkin" onclick="updateDetails(this);"><label for="chemkin">Chemkin strings</label><br> <a href="javascript:checkAllDetails();" onclick="checkAllDetails()">check all</a> <a href="javascript:uncheckAllDetails();" onclick="uncheckAllDetails();">uncheck all</a> </form> </td> </tr> </table> <table width=100%> <tr><td width=100% align="center"> <h2>Common Reactions ({{ commonReactions|length}})</h2></td></tr> <tr colspan="1"><td width=100%> <table class="reactionList hide_comment hide_kinetics hide_chemkin" width=100% cellpadding="10"> <tr colspan="4" width=100%><th>Index.</th><th>Family</th><th>Index.</th><th>Family</th></tr> {% for rxn1, rxn2 in commonReactions %} <tr> <td width=100% colspan="4"><hr> <table align="center"> <tr> <td class="reactants" align="right">{% for reactant in rxn1.reactants %}<a href="{{reactant.molecule[0].getURL() }}"><img src="species1/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow" align="center">{% if rxn1.reversible %}⇔{% else %}→{% endif %}</td> <td class="products" align="left">{% for product in rxn1.products %}<a href="{{product.molecule[0].getURL()}}"><img src="species1/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> </tr> </table> </td> </tr> {% if rxn1.kinetics.isIdenticalTo(rxn2.kinetics) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="blue">IDENTICAL KINETICS WERE FOUND FOR THIS REACTION.</font></div> </tr> {% elif rxn1.kinetics.isSimilarTo(rxn2.kinetics) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="green">SIMILAR KINETICS WERE FOUND FOR THIS REACTION.</font></div> </tr> {% else %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="red">DIFFERENT KINETICS WERE FOUND FOR THIS REACTION.</font></div> </tr> {% endif%} <tr width=100%> <td class="index" width=10%><a href="{{ rxn1.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn1.index }}.</a></td> <td class="family" width=40%>{{ rxn1.getSource().label }}</td> <td class="index" width=10%><a href="{{ rxn2.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn2.index }}.</a></td> <td class="family" width=40%>{{ rxn2.getSource().label }}</td> </tr> <tr width=100%>{% if not rxn1.isIsomorphic(rxn2, eitherDirection=False) %} <td colspan="2" width=50%></td> <td colspan="2" width=50%>* Reaction was found in reverse {% if not rxn2.duplicate %} <P><b>Fitted Reverse Kinetics:</b> {{rxn2.generateReverseRateCoefficient().toHTML() }} {% endif %} <P><b>Original Kinetics:</b> {% endif %}</td> </tr> <tr width=100%> <td colspan="2" valign="top" width=50%> {{ rxn1.kinetics.toHTML() }}</td> <td colspan="2" valign="top" width=50%> {{ rxn2.kinetics.toHTML() }}</td> </tr> <tr width=100%> <td colspan="2" valign="top" width=50%><font size="1pt" face="courier">{{ rxn1.toChemkin(speciesList) }}</font></td> <td colspan="2" valign="top" width=50%><font size="1pt" face="courier">{{ rxn2.toChemkin(speciesList) }}</font></td> </tr> <tr width=100%> <td colspan="2" valign="top" width=50%><font size="1pt">{{ rxn1.kinetics.comment }}</font></td> <td colspan="2" valign="top" width=50%><font size="1pt">{{ rxn2.kinetics.comment }}</font></td> </tr> {% endfor %} </table> </td></tr></table> <table> <tr> <td width=50% valign="top"> <h2>Model 1: Unique Reactions ({{ uniqueReactions1|length}})</h2> <table class="reactionList hide_comment hide_kinetics hide_chemkin"> <tr><th>Index</th><th colspan="3" style="text-align: center;">Reaction</th><th>Family</th></tr> {% for rxn in uniqueReactions1 %} <tr class="reaction {{ rxn.getSource().label|csssafe }}"> <td class="index"><a href="{{ rxn.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn.index }}.</a></td> <td class="reactants">{% for reactant in rxn.reactants %}<a href="{{ reactant.molecule[0].getURL() }}"><img src="species1/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow">{% if rxn.reversible %}⇔{% else %}→{% endif %}</td> <td class="products">{% for product in rxn.products %}<a href="{{ product.molecule[0].getURL() }}"><img src="species1/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="family">{{ rxn.getSource().label }}</td> </tr> <tr class="kinetics {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.toHTML() }}</td> </tr> <tr class="chemkin {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.toChemkin(species) }}</td> </tr> <tr class="comment {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.comment }}</td> </tr> {% endfor %} </table> </td> <td width=50% valign="top"> <h2>Model 2: Unique Reactions ({{ uniqueReactions2|length}})</h2> <table class="reactionList hide_comment hide_kinetics hide_chemkin"> <tr><th>Index</th><th colspan="3" style="text-align: center;">Reaction</th><th>Family</th></tr> {% for rxn in uniqueReactions2 %} <tr class="reaction {{ rxn.getSource().label|csssafe }}"> <td class="index"><a href="{{ rxn.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn.index }}.</a></td> <td class="reactants">{% for reactant in rxn.reactants %}<a href="{{ reactant.molecule[0].getURL() }}"><img src="species2/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow">{% if rxn.reversible %}⇔{% else %}→{% endif %}</td> <td class="products">{% for product in rxn.products %}<a href="{{ product.molecule[0].getURL() }}"><img src="species2/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="family">{{ rxn.getSource().label }}</td> </tr> <tr class="kinetics {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.toHTML() }}</td> </tr> <tr class="chemkin {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.toChemkin(species) }}</td> </tr> <tr class="comment {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.comment }}</td> </tr> {% endfor %} </table> </td> </table> </body> </html> """) f = open(path, 'w') f.write(template.render(title=title, commonSpecies=commonSpeciesList, speciesList1=speciesList1, speciesList2 = speciesList2, commonReactions=commonReactions, uniqueReactions1=uniqueReactions1, uniqueReactions2=uniqueReactions2, families1=families1, families2=families2, familyCount1=familyCount1,familyCount2=familyCount2, speciesList=speciesList)) f.close()
def generateCanteraConditions(reactorType, reactionTime, molFracList, Tlist=None, Plist=None, Vlist=None): """ Creates a list of cantera conditions from from the arguments provided. ======================= ==================================================== Argument Description ======================= ==================================================== `reactorType` A string of the cantera reactor type. List of supported types below: IdealGasReactor: A constant volume, zero-dimensional reactor for ideal gas mixtures IdealGasConstPressureReactor: A homogeneous, constant pressure, zero-dimensional reactor for ideal gas mixtures `reactionTime` A tuple object giving the (reaction time, units) `molFracList` A list of molfrac dictionaries with either string or species object keys and mole fraction values To specify the system for an ideal gas, you must define 2 of the following 3 parameters: `T0` A tuple giving the ([list of initial temperatures], units) 'P0' A tuple giving the ([list of initial pressures], units) 'V0' A tuple giving the ([list of initial volumes], units) This saves all the reaction conditions into the Cantera class. """ # First translate the molFracList from species objects to species names if needed newMolFracList = [] for molFrac in molFracList: newMolFrac = {} for key, value in molFrac.iteritems(): if isinstance(key, Species): newkey = getSpeciesIdentifier(key) else: newkey = key newMolFrac[newkey] = value newMolFracList.append(newMolFrac) molFracList = newMolFracList # Create individual ScalarQuantity objects for Tlist, Plist, Vlist if Tlist: Tlist = Quantity( Tlist) # Be able to create a Quantity object from it first Tlist = [(Tlist.value[i], Tlist.units) for i in range(len(Tlist.value))] if Plist: Plist = Quantity(Plist) Plist = [(Plist.value[i], Plist.units) for i in range(len(Plist.value))] if Vlist: Vlist = Quantity(Vlist) Vlist = [(Vlist.value[i], Vlist.units) for i in range(len(Vlist.value))] conditions = [] if Tlist is None: for molFrac in molFracList: for P in Plist: for V in Vlist: conditions.append( CanteraCondition(reactorType, reactionTime, molFrac, P0=P, V0=V)) elif Plist is None: for molFrac in molFracList: for T in Tlist: for V in Vlist: conditions.append( CanteraCondition(reactorType, reactionTime, molFrac, T0=T, V0=V)) elif Vlist is None: for molFrac in molFracList: for T in Tlist: for P in Plist: conditions.append( CanteraCondition(reactorType, reactionTime, molFrac, T0=T, P0=P)) else: raise Exception( "Cantera conditions must leave one of T0, P0, and V0 state variables unspecified" ) return conditions
def generateCanteraConditions(reactorType, reactionTime, molFracList, Tlist=None, Plist=None, Vlist=None): """ Creates a list of cantera conditions from from the arguments provided. ======================= ==================================================== Argument Description ======================= ==================================================== `reactorType` A string of the cantera reactor type. List of supported types below: IdealGasReactor: A constant volume, zero-dimensional reactor for ideal gas mixtures IdealGasConstPressureReactor: A homogeneous, constant pressure, zero-dimensional reactor for ideal gas mixtures `reactionTime` A tuple object giving the (reaction time, units) `molFracList` A list of molfrac dictionaries with either string or species object keys and mole fraction values To specify the system for an ideal gas, you must define 2 of the following 3 parameters: `T0` A tuple giving the ([list of initial temperatures], units) 'P0' A tuple giving the ([list of initial pressures], units) 'V0' A tuple giving the ([list of initial volumes], units) This saves all the reaction conditions into the Cantera class. """ # First translate the molFracList from species objects to species names if needed newMolFracList = [] for molFrac in molFracList: newMolFrac = {} for key, value in molFrac.iteritems(): if isinstance(key, Species): newkey = getSpeciesIdentifier(key) else: newkey = key newMolFrac[newkey] = value newMolFracList.append(newMolFrac) molFracList = newMolFracList # Create individual ScalarQuantity objects for Tlist, Plist, Vlist if Tlist: Tlist = Quantity(Tlist) # Be able to create a Quantity object from it first Tlist = [(Tlist.value[i],Tlist.units) for i in range(len(Tlist.value))] if Plist: Plist = Quantity(Plist) Plist = [(Plist.value[i],Plist.units) for i in range(len(Plist.value))] if Vlist: Vlist = Quantity(Vlist) Vlist = [(Vlist.value[i],Vlist.units) for i in range(len(Vlist.value))] conditions=[] if Tlist is None: for molFrac in molFracList: for P in Plist: for V in Vlist: conditions.append(CanteraCondition(reactorType, reactionTime, molFrac, P0=P, V0=V)) elif Plist is None: for molFrac in molFracList: for T in Tlist: for V in Vlist: conditions.append(CanteraCondition(reactorType, reactionTime, molFrac, T0=T, V0=V)) elif Vlist is None: for molFrac in molFracList: for T in Tlist: for P in Plist: conditions.append(CanteraCondition(reactorType, reactionTime, molFrac, T0=T, P0=P)) else: raise Exception("Cantera conditions must leave one of T0, P0, and V0 state variables unspecified") return conditions
def simulate(self): """ Run all the conditions as a cantera simulation. Returns the data as a list of tuples containing: (time, [list of temperature, pressure, and species data]) for each reactor condition """ # Get all the cantera names for the species speciesNamesList = [ getSpeciesIdentifier(species) for species in self.speciesList ] allData = [] for condition in self.conditions: # First translate the molFrac from species objects to species names newMolFrac = {} for key, value in condition.molFrac.iteritems(): newkey = getSpeciesIdentifier(key) newMolFrac[newkey] = value # Set Cantera simulation conditions if condition.V0 is None: self.model.TPX = condition.T0.value_si, condition.P0.value_si, newMolFrac elif condition.P0 is None: self.model.TDX = condition.T0.value_si, 1.0 / condition.V0.value_si, newMolFrac else: raise Exception( "Cantera conditions in which T0 and P0 or T0 and V0 are not the specified state variables are not yet implemented." ) # Choose reactor if condition.reactorType == 'IdealGasReactor': canteraReactor = ct.IdealGasReactor(self.model) elif condition.reactorType == 'IdealGasConstPressureReactor': canteraReactor = ct.IdealConstPressureGasReactor(self.model) else: raise Exception( 'Other types of reactor conditions are currently not supported' ) # Run this individual condition as a simulation canteraSimulation = ct.ReactorNet([canteraReactor]) # Initialize the variables to be saved times = [] temperature = [] pressure = [] speciesData = [] # Begin integration time = 0.0 # Run the simulation over 100 time points while canteraSimulation.time < condition.reactionTime.value_si: # Advance the state of the reactor network in time from the current time to time t [s], taking as many integrator timesteps as necessary. canteraSimulation.step(condition.reactionTime.value_si) times.append(canteraSimulation.time) temperature.append(canteraReactor.T) pressure.append(canteraReactor.thermo.P) speciesData.append(canteraReactor.thermo[speciesNamesList].X) # Convert speciesData to a numpy array speciesData = np.array(speciesData) # Resave data into generic data objects time = GenericData(label='Time', data=times, units='s') temperature = GenericData(label='Temperature', data=temperature, units='K') pressure = GenericData(label='Pressure', data=pressure, units='Pa') conditionData = [] conditionData.append(temperature) conditionData.append(pressure) for index, species in enumerate(self.speciesList): # Create generic data object that saves the species object into the species object. To allow easier manipulate later. speciesGenericData = GenericData(label=speciesNamesList[index], species=species, data=speciesData[:, index], index=species.index) conditionData.append(speciesGenericData) allData.append((time, conditionData)) return allData
species_list, reactions_list = loadChemkinFile(chemkinPath, dictionaryPath) #generate species images mech_path = path + '/data/' + mech speciesPath = mech_path + '/species/' if not os.path.isdir(speciesPath): os.makedirs(speciesPath) species = species_list[:] re_index_search = re.compile(r'\((\d+)\)$').search for spec in species: match = re_index_search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary fstr = os.path.join(mech_path, 'species', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise OutputError("{0} species could not be drawn!".format(getSpeciesIdentifier(spec))) species_target = 'C=CC[CH]CCCCCCC' # search the target species in model mol_tgt = Molecule().fromSMILES(species_target) for spc in species_list: if spc.isIsomorphic(mol_tgt): print '{} is found in model with spc name {}'.format(mol_tgt, getSpeciesIdentifier(spc)) break
def simulate(self): """ Run all the conditions as a cantera simulation. Returns the data as a list of tuples containing: (time, [list of temperature, pressure, and species data]) for each reactor condition """ # Get all the cantera names for the species speciesNamesList = [ getSpeciesIdentifier(species) for species in self.speciesList ] inertIndexList = [ self.speciesList.index(species) for species in self.speciesList if species.index == -1 ] allData = [] for condition in self.conditions: # First translate the molFrac from species objects to species names newMolFrac = {} for key, value in condition.molFrac.iteritems(): newkey = getSpeciesIdentifier(key) newMolFrac[newkey] = value # Set Cantera simulation conditions if condition.V0 is None: self.model.TPX = condition.T0.value_si, condition.P0.value_si, newMolFrac elif condition.P0 is None: self.model.TDX = condition.T0.value_si, 1.0 / condition.V0.value_si, newMolFrac else: raise Exception( "Cantera conditions in which T0 and P0 or T0 and V0 are not the specified state variables are not yet implemented." ) # Choose reactor if condition.reactorType == 'IdealGasReactor': canteraReactor = ct.IdealGasReactor(self.model) elif condition.reactorType == 'IdealGasConstPressureReactor': canteraReactor = ct.IdealGasConstPressureReactor( contents=self.model) elif condition.reactorType == 'IdealGasConstPressureTemperatureReactor': canteraReactor = ct.IdealGasConstPressureReactor( contents=self.model, energy='off') else: raise Exception( 'Other types of reactor conditions are currently not supported' ) # Run this individual condition as a simulation canteraSimulation = ct.ReactorNet([canteraReactor]) numCtReactions = len(self.model.reactions()) if self.sensitiveSpecies: if ct.__version__ == '2.2.1': print 'Warning: Cantera version 2.2.1 may not support sensitivity analysis unless SUNDIALS was used during compilation.' print 'Warning: Upgrade to newer of Cantera in anaconda using the command "conda update -c rmg cantera"' # Add all the reactions as part of the analysis for i in range(numCtReactions): canteraReactor.add_sensitivity_reaction(i) # Set the tolerances for the sensitivity coefficients canteraSimulation.rtol_sensitivity = 1e-4 canteraSimulation.atol_sensitivity = 1e-6 # Initialize the variables to be saved times = [] temperature = [] pressure = [] speciesData = [] sensitivityData = [] # Begin integration time = 0.0 # Run the simulation over 100 time points while canteraSimulation.time < condition.reactionTime.value_si: # Advance the state of the reactor network in time from the current time to time t [s], taking as many integrator timesteps as necessary. canteraSimulation.step(condition.reactionTime.value_si) times.append(canteraSimulation.time) temperature.append(canteraReactor.T) pressure.append(canteraReactor.thermo.P) speciesData.append(canteraReactor.thermo[speciesNamesList].X) if self.sensitiveSpecies: # Cantera returns mass-based sensitivities rather than molar concentration or mole fraction based sensitivities. # The equation for converting between them is: # # d ln xi = d ln wi - sum_(species i) (dln wi) (xi) # # where xi is the mole fraction of species i and wi is the mass fraction of species i massFracSensitivityArray = canteraSimulation.sensitivities( ) if condition.reactorType == 'IdealGasReactor': # Row 0: mass, Row 1: volume, Row 2: internal energy or temperature, Row 3+: mass fractions of species massFracSensitivityArray = massFracSensitivityArray[ 3:, :] elif condition.reactorType == 'IdealGasConstPressureReactor' or condition.reactorType == 'IdealGasConstPressureTemperatureReactor': # Row 0: mass, Row 1: enthalpy or temperature, Row 2+: mass fractions of the species massFracSensitivityArray = massFracSensitivityArray[ 2:, :] else: raise Exception( 'Other types of reactor conditions are currently not supported' ) for i in range(len(massFracSensitivityArray)): massFracSensitivityArray[i] *= speciesData[-1][i] sensitivityArray = np.zeros( len(self.sensitiveSpecies) * len(self.model.reactions())) for index, species in enumerate(self.sensitiveSpecies): for j in range(numCtReactions): sensitivityArray[ numCtReactions * index + j] = canteraSimulation.sensitivity( species.toChemkin(), j) for i in range(len(massFracSensitivityArray)): if i not in inertIndexList: # massFracSensitivity for inerts are returned as nan in Cantera, so we must not include them here sensitivityArray[ numCtReactions * index + j] -= massFracSensitivityArray[i][j] sensitivityData.append(sensitivityArray) # Convert speciesData and sensitivityData to a numpy array speciesData = np.array(speciesData) sensitivityData = np.array(sensitivityData) # Resave data into generic data objects time = GenericData(label='Time', data=times, units='s') temperature = GenericData(label='Temperature', data=temperature, units='K') pressure = GenericData(label='Pressure', data=pressure, units='Pa') conditionData = [] conditionData.append(temperature) conditionData.append(pressure) for index, species in enumerate(self.speciesList): # Create generic data object that saves the species object into the species object. To allow easier manipulate later. speciesGenericData = GenericData(label=speciesNamesList[index], species=species, data=speciesData[:, index], index=species.index) conditionData.append(speciesGenericData) reactionSensitivityData = [] for index, species in enumerate(self.sensitiveSpecies): for j in range(numCtReactions): reactionSensitivityGenericData = GenericData( label='dln[{0}]/dln[k{1}]: {2}'.format( species.toChemkin(), j + 1, self.model.reactions()[j]), species=species, reaction=self.model.reactions()[j], data=sensitivityData[:, numCtReactions * index + j], index=j + 1, ) reactionSensitivityData.append( reactionSensitivityGenericData) allData.append((time, conditionData, reactionSensitivityData)) return allData
def extractROPData(ckcsvFile, species='', speciesList=[], reactionList=[]): """ Extract the ROP information from a chemkin run for a single species. Returns a list of SpeciesROP objects. Will only return a list containing a single SpeciesROP object if there are no continuations. Will also identify corresponding species and reaction objects from a loaded Chemkin file if speciesList and reactionList are given. """ from rmgpy.chemkin import getSpeciesIdentifier # Pre-convert the speciesList and reactionList to strings speciesStringList = [] if speciesList: for spec in speciesList: speciesStringList.append(getSpeciesIdentifier(spec)) reactionStringList = [] if reactionList: for rxn in reactionList: rxnString = rxn.toChemkin(kinetics=False) if rxn.reversible: rxnString = rxnString.replace('=', '<=>') reactionStringList.append(rxnString) timeData = {} distanceData = {} speciesROPData = {} with open(ckcsvFile, 'r') as stream: reader = csv.reader(stream) for row in reader: label = row[0].strip() tokens = label.split('_') if tokens[0] == 'Time': if 'Soln' in tokens[-1]: tsolnNumber = int(tokens[-1].split('#')[-1]) else: tsolnNumber = 0 tdata = numpy.array([float(r) for r in row[2:]], numpy.float) tunits = row[1].strip()[1:-1].lower() tdata *= { 'sec': 1.0, 'min': 60., 'hr': 3600., 'msec': 1e-3, 'microsec': 1e-6 }[tunits] timeData[tsolnNumber] = tdata continue if tokens[0] == 'Distance': if 'Soln' in tokens[-1]: dsolnNumber = int(tokens[-1].split('#')[-1]) else: dsolnNumber = 0 ddata = numpy.array([float(r) for r in row[2:]], numpy.float) dunits = row[1].strip()[1:-1].lower() ddata *= {'cm': 1.0, 'mm': 0.1, 'm': 100.}[dunits] distanceData[dsolnNumber] = ddata continue if len(tokens) > 1: if tokens[1] == 'ROP': species_string = tokens[0] if species == species_string: # Create a SpeciesROP object for this solution number if 'Soln' in tokens[-1]: solutionNumber = int(tokens[-1].split('#')[-1]) else: # Only 1 solution set solutionNumber = 0 if solutionNumber not in speciesROPData.keys(): speciesROPData[solutionNumber] = SpeciesROP( species, ropData=[]) if speciesList: speciesROPData[solutionNumber].identifySpecies( speciesList, speciesStringList) if tokens[3] == 'Total': # Total ROP rate speciesROPData[ solutionNumber].totalRopData = numpy.array( [float(r) for r in row[2:]], numpy.float) else: rxnString = tokens[2] rxnNumber = int(tokens[3].split('#')[-1]) rxnRopData = numpy.array( [float(r) for r in row[2:]], numpy.float) dataObject = ReactionROP(rxnString=rxnString, rxnNumber=rxnNumber, data=rxnRopData) if reactionList: dataObject.identifyReaction( reactionList, reactionStringList) speciesROPData[solutionNumber].ropData.append( dataObject) speciesROPList = [] if timeData: if len(timeData) != len(speciesROPData): raise Exception( 'Number of time and ROP data sets are mismatched. Something went wrong during parsing' ) else: for solutionNumber in sorted(speciesROPData.keys()): speciesROP = speciesROPData[solutionNumber] speciesROP.xvarData = timeData[solutionNumber] speciesROPList.append(speciesROP) elif distanceData: if len(distanceData) != len(speciesROPData): raise Exception( 'Number of time and ROP data sets are mismatched. Something went wrong during parsing' ) else: for solutionNumber in sorted(speciesROPData.keys()): speciesROP = speciesROPData[solutionNumber] speciesROP.xvarData = distanceData[solutionNumber] speciesROPList.append(speciesROP) else: raise Exception( 'Did not parse any time or distance data. Something went wrong during parsing or simulation' ) return speciesROPList
def saveDiffHTML(path, commonSpeciesList, speciesList1, speciesList2, commonReactions, uniqueReactions1, uniqueReactions2): """ This function outputs the species and reactions on an HTML page for the comparison of two RMG models. """ from model import PDepReaction from rmgpy.kinetics import Arrhenius, MultiArrhenius, MultiPDepArrhenius from rmgpy.molecule.draw import MoleculeDrawer try: import jinja2 except ImportError: logging.warning( "jinja package not found; HTML output will not be saved.") return path = os.path.abspath(path) dirname = os.path.dirname(path) # Prepare parameters to pass to jinja template title = 'RMG Model Comparison' speciesList = [spec1 for spec1, spec2 in commonSpeciesList] + [ spec2 for spec1, spec2 in commonSpeciesList ] + speciesList1 + speciesList2 re_index = re.compile(r'\((\d+)\)$') if not os.path.isdir(os.path.join(dirname, 'species1')): os.makedirs(os.path.join(dirname, 'species1')) if not os.path.isdir(os.path.join(dirname, 'species2')): os.makedirs(os.path.join(dirname, 'species2')) for spec1, spec2 in commonSpeciesList: # if the species dictionary came from an RMG-Java job, make them prettier # We use the presence of a trailing index on the label to discern this # (A single open parenthesis is not enough (e.g. when using SMILES strings as labels!) match1 = re_index.search(spec1.label) if match1: spec1.index = int(match1.group(0)[1:-1]) spec1.label = spec1.label[0:match1.start()] match2 = re_index.search(spec2.label) if match2: spec2.index = int(match2.group(0)[1:-1]) spec2.label = spec2.label[0:match2.start()] # Draw molecules if necessary fstr = os.path.join(dirname, 'species1', '{0}.png'.format(spec1)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec1.molecule[0], 'png', fstr) except IndexError: raise OutputError( '{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.' .format(getSpeciesIdentifier(spec1))) fstr = os.path.join(dirname, 'species2', '{0}.png'.format(spec2)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec2.molecule[0], 'png', fstr) except IndexError: raise OutputError( '{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.' .format(getSpeciesIdentifier(spec2))) for spec in speciesList1: match = re_index.search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary fstr = os.path.join(dirname, 'species1', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise OutputError( '{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.' .format(getSpeciesIdentifier(spec))) for spec in speciesList2: match = re_index.search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary fstr = os.path.join(dirname, 'species2', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise OutputError( '{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.' .format(getSpeciesIdentifier(spec))) familyCount1 = {} familyCount2 = {} for rxn1, rxn2 in commonReactions: if isinstance(rxn2.kinetics, (MultiArrhenius, MultiPDepArrhenius)): rxn2.duplicate = True if isinstance(rxn1, PDepReaction): family = "PDepNetwork" else: family = rxn1.getSource().label if family in familyCount1: familyCount1[family] += 1 familyCount2[family] += 1 else: familyCount1[family] = 1 familyCount2[family] = 1 for rxn in uniqueReactions1: if isinstance(rxn, PDepReaction): family = "PDepNetwork" else: family = rxn.getSource().label if family in familyCount1: familyCount1[family] += 1 else: familyCount1[family] = 1 for rxn in uniqueReactions2: if isinstance(rxn, PDepReaction): family = "PDepNetwork" else: family = rxn.getSource().label if family in familyCount2: familyCount2[family] += 1 else: familyCount2[family] = 1 families1 = familyCount1.keys() families2 = familyCount2.keys() families1.sort() families2.sort() ## jinja2 filters etc. to_remove_from_css_names = re.compile('[/.\-+,]') def csssafe(input): "Replace unsafe CSS class name characters with an underscore." return to_remove_from_css_names.sub('_', input) environment = jinja2.Environment() environment.filters['csssafe'] = csssafe # Make HTML file template = environment.from_string( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>{{ title }}</title> <style type="text/css"> body { font-family: sans-serif; } a { color: #993333; text-decoration: none; } a:visited { color: #993333; } a:hover { text-decoration: underline; } table.speciesList, table.reactionList { width: 100%; border-collapse: collapse; } table.speciesList th, table.reactionList th { text-align: left; } tr.reaction td { border-top: 1px solid #808080; } td.reactants { text-align: right; } td.products { text-align: left; } td.reactionArrow { text-align: center; font-size: 16px; } td.species img, td.reactants img, td.products img { vertical-align: middle; } tr.comment { font-size: small; } tr.kinetics { font-size: small; } .KineticsData { # border: 1px solid gray; } .KineticsData th { width: 15em; word-wrap: none; } .KineticsData td { width: 3em; } .chemkin, .KineticsData_repr { white-space: pre-wrap; font-size: x-small; font-family: "Andale Mono", monospace; } .hide_comment .comment{ display: none !important; } .hide_kinetics .kinetics{ display: none !important; } .hide_chemkin .chemkin{ display: none !important; } </style> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script> <script type="text/javascript" src="../../../external/jquery.min.js"></script> <script type="text/javascript"> function updateFamily(family) { if (family.checked) { $("."+family.value).show(); } else { $("."+family.value).hide(); } } function updateDetails(type) { if (type.checked) { $(".reactionList").removeClass("hide_"+type.value); } else { $(".reactionList").addClass("hide_"+type.value); } } function checkAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = true; updateFamily(this); }); return false; } function uncheckAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = false; updateFamily(this); }); return false; } function checkAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = true; updateDetails(this); }); return false; } function uncheckAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = false; updateDetails(this); }); return false; } $(document).ready(function() { checkAllFamilies(); uncheckAllDetails(); }); </script> </head> <body> <h1>{{ title }}</h1> <h2 align="center">Common Species ({{ commonSpecies|length }})</h2> <table width="100%"> {% for spec1, spec2 in commonSpecies %} <tr> <td width="100%" colspan="4"> <table align="center"> <tr class="species"> <td>{{ spec1.label }}</td> <td class="structure" align="center"><a href="{{spec1.molecule[0].getURL()}}"><img src="species1/{{ spec1|replace('#','%23') }}.png" alt="{{ spec1 }}" title="{{ spec1 }}"></a></td> <td>{{ "%.2f"|format(spec1.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> </table> </td> </tr> {% if spec1.thermo and spec2.thermo %} {% if spec1.thermo.isIdenticalTo(spec2.thermo) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="blue">IDENTICAL THERMO WAS FOUND FOR THIS SPECIES.</font></div> </tr> {% elif spec1.thermo.isSimilarTo(spec2.thermo) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="green">SIMILAR THERMO WAS FOUND FOR THIS SPECIES.</font></div> </tr> {% else %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="red">DIFFERENT THERMO WAS FOUND FOR THIS SPECIES.</font></div> </tr> {% endif%} <tr> <td width="10%">{{ spec1.index }}. </td> <td width="40%"> <table width="100%"> <tr> <th>H300</th> <th>S300</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td>{% if spec1.thermo.Tmin.value_si <= 300 %} {{ "%.2f"|format(spec1.thermo.getEnthalpy(300) / 4184) }} {% endif %}</td> <td> {% if spec1.thermo.Tmin.value_si <= 300 %} {{ "%.2f"|format(spec1.thermo.getEntropy(300) / 4.184) }} {% endif %}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(300) / 4.184) }}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(500) / 4.184) }}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(1000) / 4.184) }}</td> <td>{{ "%.2f"|format(spec1.thermo.getHeatCapacity(1500) / 4.184) }}</td> </tr> </table> </td> <td width="10%">{{ spec2.index }}.</td> <td width="40%"> <table width="100%"> <tr> <th>H300</th> <th>S300</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td>{% if spec2.thermo.Tmin.value_si <= 300 %} {{ "%.2f"|format(spec2.thermo.getEnthalpy(300) / 4184) }} {% endif %}</td> <td>{% if spec2.thermo.Tmin.value_si <= 300 %} {{ "%.2f"|format(spec2.thermo.getEntropy(300) / 4.184) }} {% endif %}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(300) / 4.184) }}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(500) / 4.184) }}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(1000) / 4.184) }}</td> <td>{{ "%.2f"|format(spec2.thermo.getHeatCapacity(1500) / 4.184) }}</td> </tr> </table> </td> </tr> {% endif %} <tr> <td width="100%" colspan="4"><hr/></td> </tr> {% endfor %} </table> <table width=100%> <tr colspan="2"> <td width=50% valign="top"> <h2>Model 1: Unique Species ({{ speciesList1|length }})</h2> <table class="speciesList"> <tr><th>Index</th><th>Structure</th><th>Label</th><th>Mol. Wt. (g/mol)</th></tr> {% for spec in speciesList1 %} <tr class="species"> <td class="index"> {{ spec.index }}.</td> <td class="structure"><a href="{{ spec.molecule[0].getURL() }}"><img src="species1/{{ spec|replace('#','%23') }}.png" alt="{{ spec }}" title="{{ spec }}"></a></td> <td class="label">{{ spec.label }}</td> <td>{{ "%.2f"|format(spec.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> {% endfor %} </table> </td> <td width=50% valign="top"> <h2>Model 2: Unique Species ({{ speciesList2|length }})</h2> <table class="speciesList"> <tr><th>Index</th><th>Structure</th><th>Label</th><th>Mol. Wt. (g/mol)</th></tr> {% for spec in speciesList2 %} <tr class="species"> <td class="index"> {{ spec.index }}.</td> <td class="structure"><a href="{{ spec.molecule[0].getURL() }}"><img src="species2/{{ spec|replace('#','%23') }}.png" alt="{{ spec }}" title="{{ spec }}"></a></td> <td class="label">{{ spec.label }}</td> <td>{{ "%.2f"|format(spec.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> {% endfor %} </table> </td></tr> <tr colspan="2"> <td width=50% valign="top"> <h2>Model 1 Reactions ({{ commonReactions|length + uniqueReactions1|length}})</h2> <form id='familySelector' action=""> <h4>Reaction families:</h4> {% for family in families1 %} <input type="checkbox" id="{{ family|csssafe }}" name="family" value="{{ family|csssafe }}" checked="checked" onclick="updateFamily(this);"><label for="{{ family|csssafe }}">{{ family }} ({{ familyCount1[family] }} rxn{{ 's' if familyCount1[family] != 1 }})</label><br> {% endfor %} <a href="javascript:checkAllFamilies();" onclick="checkAllFamilies()">check all</a> <a href="javascript:uncheckAllFamilies();" onclick="uncheckAllFamilies();">uncheck all</a><br> <h4>Reaction Details:</h4> <input type="checkbox" id="kinetics" name="detail" value="kinetics" onclick="updateDetails(this);"><label for="kinetics">Kinetics</label><br> <input type="checkbox" id="comment" name="detail" value="comment" onclick="updateDetails(this);"><label for="comment">Comments</label><br> <input type="checkbox" id="chemkin" name="detail" value="chemkin" onclick="updateDetails(this);"><label for="chemkin">Chemkin strings</label><br> <a href="javascript:checkAllDetails();" onclick="checkAllDetails()">check all</a> <a href="javascript:uncheckAllDetails();" onclick="uncheckAllDetails();">uncheck all</a> </form> </td> <td width=50% valign="top"> <h2>Model 2 Reactions ({{ commonReactions|length +uniqueReactions2|length}})</h2> <form id='familySelector' action=""> <h4>Reaction families:</h4> {% for family in families2 %} <input type="checkbox" id="{{ family|csssafe }}" name="family" value="{{ family|csssafe }}" checked="checked" onclick="updateFamily(this);"><label for="{{ family|csssafe }}">{{ family }} ({{ familyCount2[family] }} rxn{{ 's' if familyCount2[family] != 1 }})</label><br> {% endfor %} <a href="javascript:checkAllFamilies();" onclick="checkAllFamilies()">check all</a> <a href="javascript:uncheckAllFamilies();" onclick="uncheckAllFamilies();">uncheck all</a><br> <h4>Reaction Details:</h4> <input type="checkbox" id="kinetics" name="detail" value="kinetics" onclick="updateDetails(this);"><label for="kinetics">Kinetics</label><br> <input type="checkbox" id="comment" name="detail" value="comment" onclick="updateDetails(this);"><label for="comment">Comments</label><br> <input type="checkbox" id="chemkin" name="detail" value="chemkin" onclick="updateDetails(this);"><label for="chemkin">Chemkin strings</label><br> <a href="javascript:checkAllDetails();" onclick="checkAllDetails()">check all</a> <a href="javascript:uncheckAllDetails();" onclick="uncheckAllDetails();">uncheck all</a> </form> </td> </tr> </table> <table width=100%> <tr><td width=100% align="center"> <h2>Common Reactions ({{ commonReactions|length}})</h2></td></tr> <tr colspan="1"><td width=100%> <table class="reactionList hide_comment hide_kinetics hide_chemkin" width=100% cellpadding="10"> <tr colspan="4" width=100%><th>Index.</th><th>Family</th><th>Index.</th><th>Family</th></tr> {% for rxn1, rxn2 in commonReactions %} <tr> <td width=100% colspan="4"><hr> <table align="center"> <tr> <td class="reactants" align="right">{% for reactant in rxn1.reactants %}<a href="{{reactant.molecule[0].getURL() }}"><img src="species1/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow" align="center">{% if rxn1.reversible %}⇔{% else %}→{% endif %}</td> <td class="products" align="left">{% for product in rxn1.products %}<a href="{{product.molecule[0].getURL()}}"><img src="species1/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> </tr> </table> </td> </tr> {% if rxn1.kinetics.isIdenticalTo(rxn2.kinetics) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="blue">IDENTICAL KINETICS WERE FOUND FOR THIS REACTION.</font></div> </tr> {% elif rxn1.kinetics.isSimilarTo(rxn2.kinetics) %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="green">SIMILAR KINETICS WERE FOUND FOR THIS REACTION.</font></div> </tr> {% else %} <tr width=100%> <td colspan="4" valign="top" width=50%><div align="center"><font color="red">DIFFERENT KINETICS WERE FOUND FOR THIS REACTION.</font></div> </tr> {% endif%} <tr width=100%> <td class="index" width=10%><a href="{{ rxn1.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn1.index }}.</a></td> <td class="family" width=40%>{{ rxn1.getSource().label }}</td> <td class="index" width=10%><a href="{{ rxn2.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn2.index }}.</a></td> <td class="family" width=40%>{{ rxn2.getSource().label }}</td> </tr> <tr width=100%>{% if not rxn1.isIsomorphic(rxn2, eitherDirection=False) %} <td colspan="2" width=50%></td> <td colspan="2" width=50%>* Reaction was found in reverse {% if not rxn2.duplicate %} <P><b>Fitted Reverse Kinetics:</b> {% if not rxn2.kinetics.isPressureDependent() %} {{rxn2.generateReverseRateCoefficient().toHTML() }} {% else %} Pressure dependent {% endif %} {% endif %} <P><b>Original Kinetics:</b> {% endif %}</td> </tr> <tr width=100%> <td colspan="2" valign="top" width=50%> {{ rxn1.kinetics.toHTML() }}</td> <td colspan="2" valign="top" width=50%> {{ rxn2.kinetics.toHTML() }}</td> </tr> <tr width=100%> <td colspan="2" valign="top" width=50%><font size="1pt" face="courier">{{ rxn1.toChemkin(speciesList) }}</font></td> <td colspan="2" valign="top" width=50%><font size="1pt" face="courier">{{ rxn2.toChemkin(speciesList) }}</font></td> </tr> <tr width=100%> <td colspan="2" valign="top" width=50%><font size="1pt">{{ rxn1.kinetics.comment }}</font></td> <td colspan="2" valign="top" width=50%><font size="1pt">{{ rxn2.kinetics.comment }}</font></td> </tr> {% endfor %} </table> </td></tr></table> <table> <tr> <td width=50% valign="top"> <h2>Model 1: Unique Reactions ({{ uniqueReactions1|length}})</h2> <table class="reactionList hide_comment hide_kinetics hide_chemkin"> <tr><th>Index</th><th colspan="3" style="text-align: center;">Reaction</th><th>Family</th></tr> {% for rxn in uniqueReactions1 %} <tr class="reaction {{ rxn.getSource().label|csssafe }}"> <td class="index"><a href="{{ rxn.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn.index }}.</a></td> <td class="reactants">{% for reactant in rxn.reactants %}<a href="{{ reactant.molecule[0].getURL() }}"><img src="species1/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow">{% if rxn.reversible %}⇔{% else %}→{% endif %}</td> <td class="products">{% for product in rxn.products %}<a href="{{ product.molecule[0].getURL() }}"><img src="species1/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="family">{{ rxn.getSource().label }}</td> </tr> <tr class="kinetics {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.toHTML() }}</td> </tr> <tr class="chemkin {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.toChemkin(species) }}</td> </tr> <tr class="comment {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.comment }}</td> </tr> {% endfor %} </table> </td> <td width=50% valign="top"> <h2>Model 2: Unique Reactions ({{ uniqueReactions2|length}})</h2> <table class="reactionList hide_comment hide_kinetics hide_chemkin"> <tr><th>Index</th><th colspan="3" style="text-align: center;">Reaction</th><th>Family</th></tr> {% for rxn in uniqueReactions2 %} <tr class="reaction {{ rxn.getSource().label|csssafe }}"> <td class="index"><a href="{{ rxn.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn.index }}.</a></td> <td class="reactants">{% for reactant in rxn.reactants %}<a href="{{ reactant.molecule[0].getURL() }}"><img src="species2/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow">{% if rxn.reversible %}⇔{% else %}→{% endif %}</td> <td class="products">{% for product in rxn.products %}<a href="{{ product.molecule[0].getURL() }}"><img src="species2/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="family">{{ rxn.getSource().label }}</td> </tr> <tr class="kinetics {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.toHTML() }}</td> </tr> <tr class="chemkin {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.toChemkin(species) }}</td> </tr> <tr class="comment {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.comment }}</td> </tr> {% endfor %} </table> </td> </table> </body> </html> """) f = open(path, 'w') f.write( template.render(title=title, commonSpecies=commonSpeciesList, speciesList1=speciesList1, speciesList2=speciesList2, commonReactions=commonReactions, uniqueReactions1=uniqueReactions1, uniqueReactions2=uniqueReactions2, families1=families1, families2=families2, familyCount1=familyCount1, familyCount2=familyCount2, speciesList=speciesList)) f.close()
def saveFluxAnalysisHTML(path, idx, speciesROP, negativeROPList=[], positiveROPList=[], speciesList=[]): """ Save the flux analysis for a given species to HTML. """ import os from rmgpy.molecule.draw import MoleculeDrawer from rmgpy.chemkin import getSpeciesIdentifier from .html import renderSpecies, renderReactionROP, STYLE_HEADER try: import jinja2 except ImportError: print "jinja2 package not found; HTML output will not be saved." return path = os.path.abspath(path) dirname = os.path.dirname(path) if not os.path.isdir(os.path.join(dirname, 'species')): os.makedirs(os.path.join(dirname, 'species')) for spec in speciesList: # Draw molecules speciesLabel = getSpeciesIdentifier(spec) fstr = os.path.join(dirname, 'species', '{0}.png'.format(speciesLabel)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise Exception( "{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files." .format(speciesLabel)) #title = 'Flux Analysis for Species {label} at x = {x}'.format(label=getSpeciesIdentifier(species), x=speciesROP.xvarData[idx]) #self.totalNegativeRate[idx] #print '{num}. {rxnString}\t Actual flux = {flux}\t % of Total Positive ROP = {fluxpercent}'.format(num=i+1, rxnString=rxnRop.rxnString, flux=rxnRop.data[idx], fluxpercent=rxnRop.fluxPercentage[idx]) environment = jinja2.Environment() environment.filters['renderSpecies'] = renderSpecies environment.filters['renderReactionROP'] = renderReactionROP environment.filters['getSpeciesIdentifier'] = getSpeciesIdentifier # Make HTML file template = environment.from_string( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>Flux Analysis for Species {{speciesROP.speciesObject|getSpeciesIdentifier}} at x = {{speciesROP.xvarData[idx]}}</title> {{style}} </head> <body> <h1>Flux Analysis for Species {{speciesROP.speciesObject|getSpeciesIdentifier}} at x = {{speciesROP.xvarData[idx]}}</h1> <h2>Species</h2> <table class="speciesList"> <tr><th>Label</th><th>Structure</th><th>SMILES</th><th>Thermo</th> {{speciesROP.speciesObject|renderSpecies(thermo=True)}} </table> <hr> <h3>Net ROP = {{speciesROP.totalRopData[idx]}}</h3> <hr size=3> <h2>Top Negative Flux Reactions Depleting Species {{speciesROP.speciesObject|getSpeciesIdentifier}}</h2> <h3>Total Negative ROP = {{speciesROP.totalNegativeRate[idx]}}</h3> <table class="reactionList" hide_comment hide_kinetics hide_chemkin"> {% for rxnROP in negativeROPList %} {{rxnROP|renderReactionROP(speciesList, showROP=True, idx=idx)}} {% endfor %} </table> <hr size=3> <h2>Top Positive Flux Reactions Accumulating Species {{speciesROP.speciesObject|getSpeciesIdentifier}}</h2> <h3>Total Positive ROP = {{speciesROP.totalPositiveRate[idx]}}</h3> <table class="reactionList" hide_comment hide_kinetics hide_chemkin"> {% for rxnROP in positiveROPList %} {{rxnROP|renderReactionROP(speciesList, showROP=True, idx=idx)}} {% endfor %} </table> </body> </html> """) f = open(path, 'w') f.write( template.render(style=STYLE_HEADER, idx=idx, speciesROP=speciesROP, negativeROPList=negativeROPList, positiveROPList=positiveROPList, speciesList=speciesList)) f.close()
def saveOutputHTML(path, reactionModel): """ Save the current set of core species and reactions of `reactionModel` to an HTML file `path` on disk. As part of this process, drawings of all core species are created in the species folder (if they don't already exist) using the :mod:`rmgpy.molecule.draw` module. The :mod:`jinja` package is used to generate the HTML; if this package is not found, no HTML will be generated (but the program will carry on). """ from model import PDepReaction from rmgpy.molecule.draw import MoleculeDrawer from rmgpy.chemkin import getSpeciesIdentifier try: import jinja2 except ImportError: logging.warning("jinja package not found; HTML output will not be saved.") return path = os.path.abspath(path) dirname = os.path.dirname(path) # Prepare parameters to pass to jinja template title = 'RMG Output' species = reactionModel.core.species[:] + reactionModel.outputSpeciesList re_index = re.compile(r'\((\d+)\)$') if not os.path.isdir(os.path.join(dirname,'species')): os.makedirs(os.path.join(dirname,'species')) for spec in species: # if the species dictionary came from an RMG-Java job, make them prettier # We use the presence of a trailing index on the label to discern this # (A single open parenthesis is not enough (e.g. when using SMILES strings as labels!) match = re_index.search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary fstr = os.path.join(dirname, 'species', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except: raise OutputError("{0} species could not be drawn because it did not contain a molecular structure. Please recheck your files.".format(getSpeciesIdentifier(spec))) # We want to keep species sorted in the original order in which they were added to the RMG core. # Rather than ordered by index # species.sort(key=lambda x: x.index) reactions = [rxn for rxn in reactionModel.core.reactions ] + reactionModel.outputReactionList # We want to keep reactions sorted in original order in which they were added to core # rather than ordered by index #reactions.sort(key=lambda x: x.index) familyCount = {} for rxn in reactions: if isinstance(rxn, PDepReaction): family = "PDepNetwork" else: family = rxn.getSource().label if family in familyCount: familyCount[family] += 1 else: familyCount[family] = 1 families = familyCount.keys() families.sort() ## jinja2 filters etc. to_remove_from_css_names = re.compile('[/.\-+,]') def csssafe(input): "Replace unsafe CSS class name characters with an underscore." return to_remove_from_css_names.sub('_',input) environment = jinja2.Environment() environment.filters['csssafe'] = csssafe # Make HTML file template = environment.from_string( """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>{{ title }}</title> <style type="text/css"> body { font-family: sans-serif; } a { color: #993333; text-decoration: none; } a:visited { color: #993333; } a:hover { text-decoration: underline; } table.speciesList, table.reactionList { width: 100%; border-collapse: collapse; } table.speciesList th, table.reactionList th { text-align: left; } tr.reaction td { border-top: 1px solid #808080; } td.reactants { text-align: right; } td.products { text-align: left; } td.reactionArrow { text-align: center; font-size: 16px; } td.species img, td.reactants img, td.products img { vertical-align: middle; } tr.comment { font-size: small; } tr.kinetics { font-size: small; } .KineticsData { # border: 1px solid gray; } .KineticsData th { width: 15em; word-wrap: none; } .KineticsData td { width: 3em; } .chemkin, .KineticsData_repr { white-space: pre-wrap; font-size: x-small; font-family: "Andale Mono", monospace; } .hide_comment .comment{ display: none !important; } .hide_kinetics .kinetics{ display: none !important; } .hide_chemkin .chemkin{ display: none !important; } </style> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script> <script type="text/javascript" src="../../../external/jquery.min.js"></script> <script type="text/javascript"> function updateFamily(family) { if (family.checked) { $("."+family.value).show(); } else { $("."+family.value).hide(); } } function updateDetails(type) { if (type.checked) { $(".reactionList").removeClass("hide_"+type.value); } else { $(".reactionList").addClass("hide_"+type.value); } } function checkAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = true; updateFamily(this); }); return false; } function uncheckAllFamilies() { $("#familySelector").find("[name='family']").each(function() { this.checked = false; updateFamily(this); }); return false; } function checkAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = true; updateDetails(this); }); return false; } function uncheckAllDetails() { $("#familySelector").find("[name='detail']").each(function() { this.checked = false; updateDetails(this); }); return false; } $(document).ready(function() { checkAllFamilies(); uncheckAllDetails(); }); </script> </head> <body> <h1>{{ title }}</h1> <h2>Species ({{ species|length }})</h2> <table class="speciesList"> <tr><th>Index</th><th>Structure</th><th>Label</th><th>Mol. Wt. (g/mol)</th></tr> {% for spec in species %} <tr class="species"> <td class="index"> {{ spec.index }}.</td> <td class="structure"><a href={{ spec.molecule[0].getURL() }}><img src="species/{{ spec|replace('#','%23') }}.png" alt="{{ spec }}" title="{{ spec }}"></a></td> <td class="label">{{ spec.label }}</td> <td>{{ "%.2f"|format(spec.molecule[0].getMolecularWeight() * 1000) }}</td> </tr> {% if spec.thermo %} <tr> <td> <table align="center"> <tr> <th>H298</th> <th>S298</th> <th>Cp300</th> <th>Cp500</th> <th>Cp1000</th> <th>Cp1500</th> </tr> <tr> <td> {% if spec.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec.thermo.getEnthalpy(298) / 4184) }} {% endif %} </td> <td>{% if spec.thermo.Tmin.value_si <= 298 %} {{ "%.2f"|format(spec.thermo.getEntropy(298) / 4.184) }} {% endif %}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(300) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(500) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(1000) / 4.184) }}</td> <td>{{ "%.2f"|format(spec.thermo.getHeatCapacity(1500) / 4.184) }}</td> </tr> </table> </td></tr> {% endif %} {% endfor %} </table> <h2>Reactions ({{ reactions|length }})</h2> <form id='familySelector' action=""> <h4>Reaction families:</h4> {% for family in families %} <input type="checkbox" id="{{ family|csssafe }}" name="family" value="{{ family|csssafe }}" checked="checked" onclick="updateFamily(this);"><label for="{{ family|csssafe }}">{{ family }} ({{ familyCount[family] }} rxn{{ 's' if familyCount[family] != 1 }})</label><br> {% endfor %} <a href="javascript:checkAllFamilies();" onclick="checkAllFamilies()">check all</a> <a href="javascript:uncheckAllFamilies();" onclick="uncheckAllFamilies();">uncheck all</a><br> <h4>Reaction Details:</h4> <input type="checkbox" id="kinetics" name="detail" value="kinetics" onclick="updateDetails(this);"><label for="kinetics">Kinetics</label><br> <input type="checkbox" id="comment" name="detail" value="comment" onclick="updateDetails(this);"><label for="comment">Comments</label><br> <input type="checkbox" id="chemkin" name="detail" value="chemkin" onclick="updateDetails(this);"><label for="chemkin">Chemkin strings</label><br> <a href="javascript:checkAllDetails();" onclick="checkAllDetails()">check all</a> <a href="javascript:uncheckAllDetails();" onclick="uncheckAllDetails();">uncheck all</a> </form> <h4>Reaction List:</h4> <table class="reactionList hide_comment hide_kinetics hide_chemkin"> <tr><th>Index</th><th colspan="3" style="text-align: center;">Reaction</th><th>Family</th></tr> {% for rxn in reactions %} <tr class="reaction {{ rxn.getSource().label|csssafe }}"> <td class="index"><a href="{{ rxn.getURL() }}" title="Search on RMG website" class="searchlink">{{ rxn.index }}.</a></td> <td class="reactants">{% for reactant in rxn.reactants %}<a href="{{ reactant.molecule[0].getURL() }}"><img src="species/{{ reactant|replace('#','%23') }}.png" alt="{{ reactant }}" title="{{ reactant }}, MW = {{ "%.2f g/mol"|format(reactant.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="reactionArrow">{% if rxn.reversible %}⇔{% else %}→{% endif %}</td> <td class="products">{% for product in rxn.products %}<a href="{{ product.molecule[0].getURL() }}"><img src="species/{{ product|replace('#','%23') }}.png" alt="{{ product }}" title="{{ product }}, MW = {{ "%.2f g/mol"|format(product.molecule[0].getMolecularWeight() * 1000) }}"></a>{% if not loop.last %} + {% endif %}{% endfor %}</td> <td class="family">{{ rxn.getSource().label }}</td> </tr> <tr class="kinetics {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.toHTML() }}</td> </tr> <tr class="chemkin {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.toChemkin(species) }}</td> </tr> <tr class="comment {{ rxn.getSource().label|csssafe }}"> <td></td> <td colspan="4">{{ rxn.kinetics.comment }}</td> </tr> {% endfor %} </table> </body> </html> """) f = open(path, 'w') f.write(template.render(title=title, species=species, reactions=reactions, families=families, familyCount=familyCount)) f.close()
#generate species images mech_path = path + '/data/' + mech speciesPath = mech_path + '/species/' if not os.path.isdir(speciesPath): os.makedirs(speciesPath) species = species_list[:] re_index_search = re.compile(r'\((\d+)\)$').search for spec in species: match = re_index_search(spec.label) if match: spec.index = int(match.group(0)[1:-1]) spec.label = spec.label[0:match.start()] # Draw molecules if necessary fstr = os.path.join(mech_path, 'species', '{0}.png'.format(spec)) if not os.path.exists(fstr): try: MoleculeDrawer().draw(spec.molecule[0], 'png', fstr) except IndexError: raise OutputError("{0} species could not be drawn!".format( getSpeciesIdentifier(spec))) species_target = 'C=CC[CH]CCCCCCC' # search the target species in model mol_tgt = Molecule().fromSMILES(species_target) for spc in species_list: if spc.isIsomorphic(mol_tgt): print '{} is found in model with spc name {}'.format( mol_tgt, getSpeciesIdentifier(spc)) break