def setUp(self): """ A function run before each unit test in this class. """ from rmgpy.chemkin import loadChemkinFile folder = os.path.join(os.path.dirname(rmgpy.__file__),'tools/data/various_kinetics') chemkinPath = os.path.join(folder, 'chem_annotated.inp') dictionaryPath = os.path.join(folder, 'species_dictionary.txt') transportPath = os.path.join(folder, 'tran.dat') species, reactions = loadChemkinFile(chemkinPath, dictionaryPath,transportPath) self.rmg_ctSpecies = [spec.toCantera(useChemkinIdentifier = True) for spec in species] self.rmg_ctReactions = [] for rxn in reactions: convertedReactions = rxn.toCantera(species, useChemkinIdentifier = True) if isinstance(convertedReactions,list): self.rmg_ctReactions.extend(convertedReactions) else: self.rmg_ctReactions.append(convertedReactions) job = Cantera() job.loadChemkinModel(chemkinPath, transportFile=transportPath,quiet=True) self.ctSpecies = job.model.species() self.ctReactions = job.model.reactions()
def setUp(self): """ A function run before each unit test in this class. """ from rmgpy.chemkin import load_chemkin_file folder = os.path.join(os.path.dirname(rmgpy.__file__), 'tools/data/various_kinetics') chemkin_path = os.path.join(folder, 'chem_annotated.inp') dictionary_path = os.path.join(folder, 'species_dictionary.txt') transport_path = os.path.join(folder, 'tran.dat') species, reactions = load_chemkin_file(chemkin_path, dictionary_path, transport_path) self.rmg_ctSpecies = [ spec.to_cantera(use_chemkin_identifier=True) for spec in species ] self.rmg_ctReactions = [] for rxn in reactions: converted_reactions = rxn.to_cantera(species, use_chemkin_identifier=True) if isinstance(converted_reactions, list): self.rmg_ctReactions.extend(converted_reactions) else: self.rmg_ctReactions.append(converted_reactions) job = Cantera() job.load_chemkin_model(chemkin_path, transport_file=transport_path, quiet=True) self.ctSpecies = job.model.species() self.ctReactions = job.model.reactions()
def __init__(self, title='', old_dir='', new_dir='', observables=None, expt_data=None, ck2cti=True): self.title = title self.new_dir = new_dir self.old_dir = old_dir self.conditions = None self.expt_data = expt_data if expt_data else [] self.observables = observables if observables else {} # Detect if the transport file exists old_transport_path = None if os.path.exists(os.path.join(old_dir, 'tran.dat')): old_transport_path = os.path.join(old_dir, 'tran.dat') new_transport_path = None if os.path.exists(os.path.join(new_dir, 'tran.dat')): new_transport_path = os.path.join(new_dir, 'tran.dat') # load the species and reactions from each model old_species_list, old_reaction_list = load_chemkin_file( os.path.join(old_dir, 'chem_annotated.inp'), os.path.join(old_dir, 'species_dictionary.txt'), old_transport_path) new_species_list, new_reaction_list = load_chemkin_file( os.path.join(new_dir, 'chem_annotated.inp'), os.path.join(new_dir, 'species_dictionary.txt'), new_transport_path) self.old_sim = Cantera(species_list=old_species_list, reaction_list=old_reaction_list, output_directory=old_dir) self.new_sim = Cantera(species_list=new_species_list, reaction_list=new_reaction_list, output_directory=new_dir) # load each chemkin file into the cantera model if not ck2cti: self.old_sim.load_model() self.new_sim.load_model() else: self.old_sim.load_chemkin_model(os.path.join( old_dir, 'chem_annotated.inp'), transport_file=old_transport_path, quiet=True) self.new_sim.load_chemkin_model(os.path.join( new_dir, 'chem_annotated.inp'), transport_file=new_transport_path, quiet=True)
def __init__(self, title='', oldDir='', newDir='', observables={}, exptData=[], ck2cti=True): self.title = title self.newDir = newDir self.oldDir = oldDir self.conditions = None self.exptData = exptData self.observables = observables # Detect if the transport file exists oldTransportPath = None if os.path.exists(os.path.join(oldDir, 'tran.dat')): oldTransportPath = os.path.join(oldDir, 'tran.dat') newTransportPath = None if os.path.exists(os.path.join(newDir, 'tran.dat')): newTransportPath = os.path.join(newDir, 'tran.dat') # load the species and reactions from each model oldSpeciesList, oldReactionList = loadChemkinFile( os.path.join(oldDir, 'chem_annotated.inp'), os.path.join(oldDir, 'species_dictionary.txt'), oldTransportPath) newSpeciesList, newReactionList = loadChemkinFile( os.path.join(newDir, 'chem_annotated.inp'), os.path.join(newDir, 'species_dictionary.txt'), newTransportPath) self.oldSim = Cantera(speciesList=oldSpeciesList, reactionList=oldReactionList, outputDirectory=oldDir) self.newSim = Cantera(speciesList=newSpeciesList, reactionList=newReactionList, outputDirectory=newDir) # load each chemkin file into the cantera model if not ck2cti: self.oldSim.loadModel() self.newSim.loadModel() else: self.oldSim.loadChemkinModel(os.path.join(oldDir, 'chem_annotated.inp'), transportFile=oldTransportPath, quiet=True) self.newSim.loadChemkinModel(os.path.join(newDir, 'chem_annotated.inp'), transportFile=newTransportPath, quiet=True)
def __init__(self, title='', oldDir='', newDir='', observables = {}, exptData = [], ck2cti=True): self.title=title self.newDir=newDir self.oldDir=oldDir self.conditions=None self.exptData=exptData self.observables=observables # Detect if the transport file exists oldTransportPath = None if os.path.exists(os.path.join(oldDir,'tran.dat')): oldTransportPath = os.path.join(oldDir,'tran.dat') newTransportPath = None if os.path.exists(os.path.join(newDir,'tran.dat')): newTransportPath = os.path.join(newDir,'tran.dat') # load the species and reactions from each model oldSpeciesList, oldReactionList = loadChemkinFile(os.path.join(oldDir,'chem_annotated.inp'), os.path.join(oldDir,'species_dictionary.txt'), oldTransportPath) newSpeciesList, newReactionList = loadChemkinFile(os.path.join(newDir,'chem_annotated.inp'), os.path.join(newDir,'species_dictionary.txt'), newTransportPath) self.oldSim = Cantera(speciesList = oldSpeciesList, reactionList = oldReactionList, outputDirectory = oldDir) self.newSim = Cantera(speciesList = newSpeciesList, reactionList = newReactionList, outputDirectory = newDir) # load each chemkin file into the cantera model if not ck2cti: self.oldSim.loadModel() self.newSim.loadModel() else: self.oldSim.loadChemkinModel(os.path.join(oldDir,'chem_annotated.inp'), transportFile=oldTransportPath, quiet=True) self.newSim.loadChemkinModel(os.path.join(newDir,'chem_annotated.inp'), transportFile=newTransportPath, quiet=True)
def __init__(self, title='', oldDir='', newDir='', observables = {}, exptData = []): self.title=title self.newDir=newDir self.oldDir=oldDir self.conditions=None self.exptData=exptData self.observables=observables # load the species and reactions from each model oldSpeciesList, oldReactionList = loadChemkinFile(os.path.join(oldDir,'chem_annotated.inp'), os.path.join(oldDir,'species_dictionary.txt')) newSpeciesList, newReactionList = loadChemkinFile(os.path.join(newDir,'chem_annotated.inp'), os.path.join(newDir,'species_dictionary.txt')) self.oldSim = Cantera(speciesList = oldSpeciesList, reactionList = oldReactionList, outputDirectory = oldDir) self.newSim = Cantera(speciesList = newSpeciesList, reactionList = newReactionList, outputDirectory = newDir) # load each chemkin file into the cantera model self.oldSim.loadChemkinModel(os.path.join(oldDir,'chem_annotated.inp')) self.newSim.loadChemkinModel(os.path.join(newDir,'chem_annotated.inp'))
class ObservablesTestCase: """ We use this class to run regressive tests ======================= ============================================================================================== Attribute Description ======================= ============================================================================================== `title` A string describing the test. For regressive tests, should be same as example's name. `oldDir` A directory path containing the chem_annotated.inp and species_dictionary.txt of the old model `newDir` A directory path containing the chem_annotated.inp and species_dictionary.txt of the new model `conditions` A list of the :class: 'CanteraCondition' objects describing reaction conditions `observables` A dictionary of observables key: 'species', value: a list of the "class" 'Species' that correspond with species mole fraction observables key: 'variable', value: a list of state variable observables, i.e. ['Temperature'] or ['Temperature','Pressure'] key: 'ignitionDelay', value: a tuple containing (ignition metric, yVar) for example: ('maxDerivative','P') ('maxHalfConcentration', '[OH]') ('maxSpeciesConcentrations',['[CH2]','[O]']) see findIgnitionDelay function for more details 'exptData' An array of GenericData objects 'ck2tci' Indicates whether to convert chemkin to cti mechanism. If set to False, RMG will convert the species and reaction objects to Cantera objects internally ======================= ============================================================================================== """ def __init__(self, title='', oldDir='', newDir='', observables = {}, exptData = [], ck2cti=True): self.title=title self.newDir=newDir self.oldDir=oldDir self.conditions=None self.exptData=exptData self.observables=observables # Detect if the transport file exists oldTransportPath = None if os.path.exists(os.path.join(oldDir,'tran.dat')): oldTransportPath = os.path.join(oldDir,'tran.dat') newTransportPath = None if os.path.exists(os.path.join(newDir,'tran.dat')): newTransportPath = os.path.join(newDir,'tran.dat') # load the species and reactions from each model oldSpeciesList, oldReactionList = loadChemkinFile(os.path.join(oldDir,'chem_annotated.inp'), os.path.join(oldDir,'species_dictionary.txt'), oldTransportPath) newSpeciesList, newReactionList = loadChemkinFile(os.path.join(newDir,'chem_annotated.inp'), os.path.join(newDir,'species_dictionary.txt'), newTransportPath) self.oldSim = Cantera(speciesList = oldSpeciesList, reactionList = oldReactionList, outputDirectory = oldDir) self.newSim = Cantera(speciesList = newSpeciesList, reactionList = newReactionList, outputDirectory = newDir) # load each chemkin file into the cantera model if not ck2cti: self.oldSim.loadModel() self.newSim.loadModel() else: self.oldSim.loadChemkinModel(os.path.join(oldDir,'chem_annotated.inp'), transportFile=oldTransportPath, quiet=True) self.newSim.loadChemkinModel(os.path.join(newDir,'chem_annotated.inp'), transportFile=newTransportPath, quiet=True) def __str__(self): """ Return a string representation of this test case, using its title'. """ return 'Observables Test Case: {0}'.format(self.title) def generateConditions(self, reactorTypeList, reactionTimeList, molFracList, Tlist=None, Plist=None, Vlist=None): """ Creates a list of conditions from from the lists provided. ======================= ==================================================== Argument Description ======================= ==================================================== `reactorTypeList` A list of strings 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 `reactionTimeList` A tuple object giving the ([list of reaction times], units) `molFracList` A list of molfrac dictionaries with species object keys and mole fraction values To specify the system for an ideal gas, you must define 2 of the following 3 parameters: `T0List` A tuple giving the ([list of initial temperatures], units) 'P0List' A tuple giving the ([list of initial pressures], units) 'V0List' A tuple giving the ([list of initial specific volumes], units) This saves all the reaction conditions into both the old and new cantera jobs. """ # Store the conditions in the observables test case, for bookkeeping self.conditions = generateCanteraConditions(reactorTypeList, reactionTimeList, molFracList, Tlist=Tlist, Plist=Plist, Vlist=Vlist) # Map the mole fractions dictionaries to species objects from the old and new models oldMolFracList = [] newMolFracList = [] for molFracCondition in molFracList: oldCondition = {} newCondition = {} oldSpeciesDict = getRMGSpeciesFromUserSpecies(molFracCondition.keys(), self.oldSim.speciesList) newSpeciesDict = getRMGSpeciesFromUserSpecies(molFracCondition.keys(), self.newSim.speciesList) for smiles, molfrac in molFracCondition.iteritems(): if oldSpeciesDict[smiles] is None: raise Exception('SMILES {0} was not found in the old model!'.format(smiles)) if newSpeciesDict[smiles] is None: raise Exception('SMILES {0} was not found in the new model!'.format(smiles)) oldCondition[oldSpeciesDict[smiles]] = molfrac newCondition[newSpeciesDict[smiles]] = molfrac oldMolFracList.append(oldCondition) newMolFracList.append(newCondition) # Generate the conditions in each simulation self.oldSim.generateConditions(reactorTypeList, reactionTimeList, oldMolFracList, Tlist=Tlist, Plist=Plist, Vlist=Vlist) self.newSim.generateConditions(reactorTypeList, reactionTimeList, newMolFracList, Tlist=Tlist, Plist=Plist, Vlist=Vlist) def compare(self, tol, plot=False): """ Compare the old and new model 'tol': average error acceptable between old and new model for variables `plot`: if set to True, it will comparison plots of the two models comparing their species. Returns a list of variables failed in a list of tuples in the format: (CanteraCondition, variable label, variableOld, variableNew) """ # Ignore Inerts inertList = ['[Ar]','[He]','[N#N]','[Ne]'] oldConditionData, newConditionData = self.runSimulations() conditionsBroken=[] variablesFailed=[] print '' print '{0} Comparison'.format(self) print '================' # Check the species profile observables if 'species' in self.observables: oldSpeciesDict = getRMGSpeciesFromUserSpecies(self.observables['species'], self.oldSim.speciesList) newSpeciesDict = getRMGSpeciesFromUserSpecies(self.observables['species'], self.newSim.speciesList) # Check state variable observables implementedVariables = ['temperature','pressure'] if 'variable' in self.observables: for item in self.observables['variable']: if item.lower() not in implementedVariables: print 'Observable variable {0} not yet implemented'.format(item) failHeader='\nThe following observables did not match:\n' failHeaderPrinted=False for i in range(len(oldConditionData)): timeOld, dataListOld = oldConditionData[i] timeNew, dataListNew = newConditionData[i] # Compare species observables if 'species' in self.observables: smilesList=[] #This is to make sure we don't have species with duplicate smiles multiplicityList=['','(S)','(D)','(T)','(Q)'] #list ot add multiplcity for species in self.observables['species']: smiles=species.molecule[0].toSMILES() #For purpose of naming the plot only if smiles in smilesList: smiles=smiles+multiplicityList[species.molecule[0].multiplicity] smilesList.append(smiles) fail = False oldRmgSpecies = oldSpeciesDict[species] newRmgSpecies = newSpeciesDict[species] if oldRmgSpecies: variableOld = next((data for data in dataListOld if data.species == oldRmgSpecies), None) else: print 'No RMG species found for observable species {0} in old model.'.format(smiles) fail = True if newRmgSpecies: variableNew = next((data for data in dataListNew if data.species == newRmgSpecies), None) else: print 'No RMG species found for observable species {0} in new model.'.format(smiles) fail = True if fail is False: if not curvesSimilar(timeOld.data, variableOld.data, timeNew.data, variableNew.data, tol): fail = True # Try plotting only when species are found in both models if plot: oldSpeciesPlot = SimulationPlot(xVar=timeOld, yVar=variableOld) newSpeciesPlot = SimulationPlot(xVar=timeNew, yVar=variableNew) oldSpeciesPlot.comparePlot(newSpeciesPlot, title='Observable Species {0} Comparison'.format(smiles), ylabel='Mole Fraction', filename='condition_{0}_species_{1}.png'.format(i+1,smiles)) # Append to failed variables or conditions if this test failed if fail: if not failHeaderPrinted: print failHeader failHeaderPrinted=True if i not in conditionsBroken: conditionsBroken.append(i) print "Observable species {0} varied by more than {1:.3f} on average between old model {2} and \ new model {3} in condition {4:d}.".format(smiles, tol, variableOld.label, variableNew.label, i+1) variablesFailed.append((self.conditions[i], smiles, variableOld, variableNew)) # Compare state variable observables if 'variable' in self.observables: for varName in self.observables['variable']: variableOld = next((data for data in dataListOld if data.label == varName), None) variableNew = next((data for data in dataListNew if data.label == varName), None) if not curvesSimilar(timeOld.data, variableOld.data, timeNew.data, variableNew.data, 0.05): if i not in conditionsBroken: conditionsBroken.append(i) if not failHeaderPrinted: failHeaderPrinted=True print failHeader print "Observable variable {0} varied by more than {1:.3f} on average between old model and \ new model in condition {2:d}.".format(variableOld.label, i+1) variablesFailed.append((self.conditions[i], tol, varName, variableOld, variableNew)) if plot: oldVarPlot = GenericPlot(xVar=timeOld, yVar=variableOld) newVarPlot = GenericPlot(xVar=timeNew, yVar=variableNew) oldVarPlot.comparePlot(newSpeciesPlot, title='Observable Variable {0} Comparison'.format(varName), filename='condition_{0}_variable_{1}.png'.format(i+1, varName)) # Compare ignition delay observables if 'ignitionDelay' in self.observables: print 'Ignition delay observable comparison not implemented yet.' if failHeaderPrinted: print '' print 'The following reaction conditions were had some discrepancies:' print '' for index in conditionsBroken: print "Condition {0:d}:".format(index+1) print str(self.conditions[index]) print '' return variablesFailed else: print '' print 'All Observables varied by less than {0:.3f} on average between old model and \ new model in all conditions!'.format(tol) print '' def runSimulations(self): """ Run a selection of conditions in Cantera and return generic data objects containing the time, pressure, temperature, and mole fractions from the simulations. Returns (oldConditionData, newConditionData) where conditionData is a list of of tuples: (time, dataList) for each condition in the same order as conditions time is a GenericData object which gives the time domain for each profile dataList is a list of GenericData objects for the temperature, profile, and mole fraction of major species """ oldConditionData = self.oldSim.simulate() newConditionData = self.newSim.simulate() return (oldConditionData, newConditionData)
def run_cantera_job( smiles_dictionary, specie_initial_mol_frac, final_time, temp_initial, initial_p, chemkin_file='', species_dictionary_file='', transport_file=None, reactor_type='IdealGasConstPressureTemperatureReactor', time_units='s', temp_units='K', p_units='atm', species_list=None, reaction_list=None, ): """General function for running Cantera jobs from chemkin files with common defaults =========================== ======================================================================= Input (Required) Description =========================== ======================================================================= smiles_dictionary A dictionary with user names as keys and SMILES strings as values specie_initial_mol_frac A dictionary with user specie names as keys and mol fractions as values final_time Termination time for the simulation temp_initial Initial temperature for the simulation initial_p Initial pressure for the simulation =========================== ======================================================================= Inputs with Defaults Description =========================== ======================================================================= chemkin_file String relative path of the chem.inp or chem_annotated.inp file species_dictionary_file String relative path of species_dictionary file reactor_type String with Cantera reactor type time_units Default is s (min and h are also supported) temp_units Default is K (C is also supported) p_units Default is atm (bar and Pa are also supported) =========================== ======================================================================= Optional Inputs Description =========================== ======================================================================= transport_file String relative path of trans.dat file species_list Output from loadChemkinFile for faster simulation (otherwise generated) reaction_list Output from loadChemkinFile for faster simulation (otherwise generated) =================================================================================================== =========================== ======================================================================= Output Description =========================== ======================================================================= all_data Cantera Simulation Data Object [time, [temp, pressure, spc1, spc2,..]] =================================================================================================== """ logging.info( 'Running a cantera job using the chemkin file {}'.format(chemkin_file)) logging.debug('loading chemkin and species dictionary file') cwd = os.getcwd() if chemkin_file == '': chemkin_file = os.path.join(cwd, 'chem_annotated.inp') if species_dictionary_file == '': species_dictionary_file = os.path.join(cwd, 'species_dictionary.txt') user_species_dictionary = create_species_from_smiles(smiles_dictionary) specie_initial_mol_frac = set_species_mol_fractions( specie_initial_mol_frac, user_species_dictionary) if (not species_list) or (not reaction_list): (species_list, reaction_list) = loadChemkinFile(chemkin_file, species_dictionary_file) name_dictionary = getRMGSpeciesFromUserSpecies( user_species_dictionary.values(), species_list) mol_fractions = {} for (user_name, chemkin_name) in name_dictionary.iteritems(): try: mol_fractions[chemkin_name] = specie_initial_mol_frac[user_name] except KeyError: logging.debug( '{} initial mol fractions set to 0'.format(user_name)) if temp_units == 'C': temp_initial += 273.0 temp_initial = ([temp_initial], 'K') initial_p = ([initial_p], p_units) job = Cantera(speciesList=species_list, reactionList=reaction_list, outputDirectory='') job.loadChemkinModel(chemkin_file, transportFile=transport_file) job.generateConditions([reactor_type], ([final_time], time_units), [mol_fractions], temp_initial, initial_p) logging.debug('Starting Cantera Simulation') all_data = job.simulate() all_data = all_data[0] logging.info('Cantera Simulation Complete') logging.debug('Setting labels to user defined species labels') species_index = {} for i in range(len(species_list)): species_index[species_list[i]] = i + 2 user_index = {} for (user_name, specie) in user_species_dictionary.iteritems(): try: user_index[species_index[name_dictionary[specie]]] = user_name except KeyError: logging.info('{0} is not in the model for {1}'.format( user_name, chemkin_file)) for (indices, user_label) in user_index.iteritems(): try: all_data[1][indices].label = user_label except KeyError: pass return all_data
class ObservablesTestCase(object): """ We use this class to run regressive tests ======================= ============================================================================================== Attribute Description ======================= ============================================================================================== `title` A string describing the test. For regressive tests, should be same as example's name. `old_dir` A directory path containing the chem_annotated.inp and species_dictionary.txt of the old model `new_dir` A directory path containing the chem_annotated.inp and species_dictionary.txt of the new model `conditions` A list of the :class: 'CanteraCondition' objects describing reaction conditions `observables` A dictionary of observables key: 'species', value: a list of the "class" 'Species' that correspond with species mole fraction observables key: 'variable', value: a list of state variable observables, i.e. ['Temperature'] or ['Temperature','Pressure'] key: 'ignitionDelay', value: a tuple containing (ignition metric, y_var) for example: ('maxDerivative','P') ('maxHalfConcentration', '[OH]') ('maxSpeciesConcentrations',['[CH2]','[O]']) see find_ignition_delay function for more details 'expt_data' An array of GenericData objects 'ck2tci' Indicates whether to convert chemkin to cti mechanism. If set to False, RMG will convert the species and reaction objects to Cantera objects internally ======================= ============================================================================================== """ def __init__(self, title='', old_dir='', new_dir='', observables=None, expt_data=None, ck2cti=True): self.title = title self.new_dir = new_dir self.old_dir = old_dir self.conditions = None self.expt_data = expt_data if expt_data else [] self.observables = observables if observables else {} # Detect if the transport file exists old_transport_path = None if os.path.exists(os.path.join(old_dir, 'tran.dat')): old_transport_path = os.path.join(old_dir, 'tran.dat') new_transport_path = None if os.path.exists(os.path.join(new_dir, 'tran.dat')): new_transport_path = os.path.join(new_dir, 'tran.dat') # load the species and reactions from each model old_species_list, old_reaction_list = load_chemkin_file( os.path.join(old_dir, 'chem_annotated.inp'), os.path.join(old_dir, 'species_dictionary.txt'), old_transport_path) new_species_list, new_reaction_list = load_chemkin_file( os.path.join(new_dir, 'chem_annotated.inp'), os.path.join(new_dir, 'species_dictionary.txt'), new_transport_path) self.old_sim = Cantera(species_list=old_species_list, reaction_list=old_reaction_list, output_directory=old_dir) self.new_sim = Cantera(species_list=new_species_list, reaction_list=new_reaction_list, output_directory=new_dir) # load each chemkin file into the cantera model if not ck2cti: self.old_sim.load_model() self.new_sim.load_model() else: self.old_sim.load_chemkin_model(os.path.join( old_dir, 'chem_annotated.inp'), transport_file=old_transport_path, quiet=True) self.new_sim.load_chemkin_model(os.path.join( new_dir, 'chem_annotated.inp'), transport_file=new_transport_path, quiet=True) def __str__(self): """ Return a string representation of this test case, using its title'. """ return 'Observables Test Case: {0}'.format(self.title) def generate_conditions(self, reactor_type_list, reaction_time_list, mol_frac_list, Tlist=None, Plist=None, Vlist=None): """ Creates a list of conditions from from the lists provided. ======================= ==================================================== Argument Description ======================= ==================================================== `reactor_type_list` A list of strings 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 `reaction_time_list` A tuple object giving the ([list of reaction times], units) `mol_frac_list` A list of molfrac dictionaries with species object keys and mole fraction values To specify the system for an ideal gas, you must define 2 of the following 3 parameters: `T0List` A tuple giving the ([list of initial temperatures], units) 'P0List' A tuple giving the ([list of initial pressures], units) 'V0List' A tuple giving the ([list of initial specific volumes], units) This saves all the reaction conditions into both the old and new cantera jobs. """ # Store the conditions in the observables test case, for bookkeeping self.conditions = generate_cantera_conditions(reactor_type_list, reaction_time_list, mol_frac_list, Tlist=Tlist, Plist=Plist, Vlist=Vlist) # Map the mole fractions dictionaries to species objects from the old and new models old_mol_frac_list = [] new_mol_frac_list = [] for mol_frac in mol_frac_list: old_condition = {} new_condition = {} old_species_dict = get_rmg_species_from_user_species( list(mol_frac.keys()), self.old_sim.species_list) new_species_dict = get_rmg_species_from_user_species( list(mol_frac.keys()), self.new_sim.species_list) for smiles, molfrac in mol_frac.items(): if old_species_dict[smiles] is None: raise Exception( 'SMILES {0} was not found in the old model!'.format( smiles)) if new_species_dict[smiles] is None: raise Exception( 'SMILES {0} was not found in the new model!'.format( smiles)) old_condition[old_species_dict[smiles]] = molfrac new_condition[new_species_dict[smiles]] = molfrac old_mol_frac_list.append(old_condition) new_mol_frac_list.append(new_condition) # Generate the conditions in each simulation self.old_sim.generate_conditions(reactor_type_list, reaction_time_list, old_mol_frac_list, Tlist=Tlist, Plist=Plist, Vlist=Vlist) self.new_sim.generate_conditions(reactor_type_list, reaction_time_list, new_mol_frac_list, Tlist=Tlist, Plist=Plist, Vlist=Vlist) def compare(self, tol, plot=False): """ Compare the old and new model 'tol': average error acceptable between old and new model for variables `plot`: if set to True, it will comparison plots of the two models comparing their species. Returns a list of variables failed in a list of tuples in the format: (CanteraCondition, variable label, variable_old, variable_new) """ # Ignore Inerts inert_list = ['[Ar]', '[He]', '[N#N]', '[Ne]'] old_condition_data, new_condition_data = self.run_simulations() conditions_broken = [] variables_failed = [] print('') print('{0} Comparison'.format(self)) print('================') # Check the species profile observables if 'species' in self.observables: old_species_dict = get_rmg_species_from_user_species( self.observables['species'], self.old_sim.species_list) new_species_dict = get_rmg_species_from_user_species( self.observables['species'], self.new_sim.species_list) # Check state variable observables implemented_variables = ['temperature', 'pressure'] if 'variable' in self.observables: for item in self.observables['variable']: if item.lower() not in implemented_variables: print('Observable variable {0} not yet implemented'.format( item)) fail_header = '\nThe following observables did not match:\n' fail_header_printed = False for i in range(len(old_condition_data)): time_old, data_list_old, reaction_sensitivity_data_old = old_condition_data[ i] time_new, data_list_new, reaction_sensitivity_data_new = new_condition_data[ i] # Compare species observables if 'species' in self.observables: smiles_list = [ ] # This is to make sure we don't have species with duplicate smiles multiplicity_list = ['', '(S)', '(D)', '(T)', '(Q)'] # list ot add multiplcity for species in self.observables['species']: smiles = species.molecule[0].to_smiles( ) # For purpose of naming the plot only if smiles in smiles_list: smiles = smiles + multiplicity_list[ species.molecule[0].multiplicity] smiles_list.append(smiles) fail = False old_rmg_species = old_species_dict[species] new_rmg_species = new_species_dict[species] if old_rmg_species: variable_old = next( (data for data in data_list_old if data.species == old_rmg_species), None) else: print( 'No RMG species found for observable species {0} in old model.' .format(smiles)) fail = True if new_rmg_species: variable_new = next( (data for data in data_list_new if data.species == new_rmg_species), None) else: print( 'No RMG species found for observable species {0} in new model.' .format(smiles)) fail = True if fail is False: if not curves_similar(time_old.data, variable_old.data, time_new.data, variable_new.data, tol): fail = True # Try plotting only when species are found in both models if plot: old_species_plot = SimulationPlot( x_var=time_old, y_var=variable_old) new_species_plot = SimulationPlot( x_var=time_new, y_var=variable_new) old_species_plot.compare_plot( new_species_plot, title='Observable Species {0} Comparison'. format(smiles), ylabel='Mole Fraction', filename='condition_{0}_species_{1}.png'. format(i + 1, smiles)) # Append to failed variables or conditions if this test failed if fail: if not fail_header_printed: print(fail_header) fail_header_printed = True if i not in conditions_broken: conditions_broken.append(i) print( "Observable species {0} varied by more than {1:.3f} on average between old model {2} and " "new model {3} in condition {4:d}.".format( smiles, tol, variable_old.label, variable_new.label, i + 1)) variables_failed.append((self.conditions[i], smiles, variable_old, variable_new)) # Compare state variable observables if 'variable' in self.observables: for varName in self.observables['variable']: variable_old = next( (data for data in data_list_old if data.label == varName), None) variable_new = next( (data for data in data_list_new if data.label == varName), None) if not curves_similar(time_old.data, variable_old.data, time_new.data, variable_new.data, 0.05): if i not in conditions_broken: conditions_broken.append(i) if not fail_header_printed: fail_header_printed = True print(fail_header) print( "Observable variable {0} varied by more than {1:.3f} on average between old model and " "new model in condition {2:d}.".format( variable_old.label, tol, i + 1)) variables_failed.append((self.conditions[i], varName, variable_old, variable_new)) if plot: old_var_plot = GenericPlot(x_var=time_old, y_var=variable_old) new_var_plot = GenericPlot(x_var=time_new, y_var=variable_new) old_var_plot.compare_plot( new_var_plot, title='Observable Variable {0} Comparison'.format( varName), filename='condition_{0}_variable_{1}.png'.format( i + 1, varName)) # Compare ignition delay observables if 'ignitionDelay' in self.observables: print( 'Ignition delay observable comparison not implemented yet.' ) if fail_header_printed: print('') print( 'The following reaction conditions were had some discrepancies:' ) print('') for index in conditions_broken: print("Condition {0:d}:".format(index + 1)) print(str(self.conditions[index])) print('') return variables_failed else: print('') print( 'All Observables varied by less than {0:.3f} on average between old model and ' 'new model in all conditions!'.format(tol)) print('') def run_simulations(self): """ Run a selection of conditions in Cantera and return generic data objects containing the time, pressure, temperature, and mole fractions from the simulations. Returns (old_condition_data, new_condition_data) where conditionData is a list of of tuples: (time, dataList) for each condition in the same order as conditions time is a GenericData object which gives the time domain for each profile dataList is a list of GenericData objects for the temperature, profile, and mole fraction of major species """ old_condition_data = self.old_sim.simulate() new_condition_data = self.new_sim.simulate() return (old_condition_data, new_condition_data)
class ObservablesTestCase: """ We use this class to run regressive tests ======================= ============================================================================================== Attribute Description ======================= ============================================================================================== `title` A string describing the test. For regressive tests, should be same as example's name. `oldDir` A directory path containing the chem_annotated.inp and species_dictionary.txt of the old model `newDir` A directory path containing the chem_annotated.inp and species_dictionary.txt of the new model `conditions` A list of the :class: 'Condition' objects describing reaction conditions `observables` A dictionary of observables key: 'species', value: a list of smiles that correspond with species mole fraction observables key: 'variable', value: a list of state variable observables, i.e. ['Temperature'] or ['Temperature','Pressure'] key: 'ignitionDelay', value: a tuple containing (ignition metric, yVar) for example: ('maxDerivative','P') ('maxHalfConcentration', '[OH]') ('maxSpeciesConcentrations',['[CH2]','[O]']) see findIgnitionDelay function for more details 'exptData' An array of GenericData objects ======================= ============================================================================================== """ def __init__(self, title='', oldDir='', newDir='', observables = {}, exptData = []): self.title=title self.newDir=newDir self.oldDir=oldDir self.conditions=None self.exptData=exptData self.observables=observables # load the species and reactions from each model oldSpeciesList, oldReactionList = loadChemkinFile(os.path.join(oldDir,'chem_annotated.inp'), os.path.join(oldDir,'species_dictionary.txt')) newSpeciesList, newReactionList = loadChemkinFile(os.path.join(newDir,'chem_annotated.inp'), os.path.join(newDir,'species_dictionary.txt')) self.oldSim = Cantera(speciesList = oldSpeciesList, reactionList = oldReactionList, outputDirectory = oldDir) self.newSim = Cantera(speciesList = newSpeciesList, reactionList = newReactionList, outputDirectory = newDir) # load each chemkin file into the cantera model self.oldSim.loadChemkinModel(os.path.join(oldDir,'chem_annotated.inp')) self.newSim.loadChemkinModel(os.path.join(newDir,'chem_annotated.inp')) def __str__(self): """ Return a string representation of this test case, using its title'. """ return 'Observables Test Case: {0}'.format(self.title) def generateConditions(self, reactorType, reactionTime, molFracList, Tlist=None, Plist=None, Vlist=None): """ Creates a list of conditions from from the lists provided. `reactorType`: a string indicating the Cantera reactor type `reactionTime`: ScalarQuantity object for time `molFracList`: a list of dictionaries containing species smiles and their mole fraction values `Tlist`: ArrayQuantity object of temperatures `Plist`: ArrayQuantity object of pressures `Vlist`: ArrayQuantity object of volumes This saves all the reaction conditions into both the old and new cantera jobs. """ # Store the conditions in the observables test case, for bookkeeping self.conditions = generateCanteraConditions(reactorType, reactionTime, molFracList, Tlist=Tlist, Plist=Plist, Vlist=Vlist) # Map the mole fractions dictionaries to species objects from the old and new models oldMolFracList = [] newMolFracList = [] for molFracCondition in molFracList: oldCondition = {} newCondition = {} oldSpeciesDict = getRMGSpeciesFromSMILES(molFracCondition.keys(), self.oldSim.speciesList) newSpeciesDict = getRMGSpeciesFromSMILES(molFracCondition.keys(), self.newSim.speciesList) for smiles, molfrac in molFracCondition.iteritems(): if oldSpeciesDict[smiles] is None: raise Exception('SMILES {0} was not found in the old model!'.format(smiles)) if newSpeciesDict[smiles] is None: raise Exception('SMILES {0} was not found in the new model!'.format(smiles)) oldCondition[oldSpeciesDict[smiles]] = molfrac newCondition[newSpeciesDict[smiles]] = molfrac oldMolFracList.append(oldCondition) newMolFracList.append(newCondition) # Generate the conditions in each simulation self.oldSim.generateConditions(reactorType, reactionTime, oldMolFracList, Tlist=Tlist, Plist=Plist, Vlist=Vlist) self.newSim.generateConditions(reactorType, reactionTime, newMolFracList, Tlist=Tlist, Plist=Plist, Vlist=Vlist) def compare(self, plot=False): """ Compare the old and new model `plot`: if set to True, it will comparison plots of the two models comparing their species. Returns a list of variables failed in a list of tuples in the format: (CanteraCondition, variable label, variableOld, variableNew) """ # Ignore Inerts inertList = ['[Ar]','[He]','[N#N]','[Ne]'] oldConditionData, newConditionData = self.runSimulations() conditionsBroken=[] variablesFailed=[] print '' print '{0} Comparison'.format(self) print '================' # Check the species profile observables if 'species' in self.observables: oldSpeciesDict = getRMGSpeciesFromSMILES(self.observables['species'], self.oldSim.speciesList) newSpeciesDict = getRMGSpeciesFromSMILES(self.observables['species'], self.newSim.speciesList) # Check state variable observables implementedVariables = ['temperature','pressure'] if 'variable' in self.observables: for item in self.observables['variable']: if item.lower() not in implementedVariables: print 'Observable variable {0} not yet implemented'.format(item) print '' print 'The following observables did not match:' print '' for i in range(len(oldConditionData)): timeOld, dataListOld = oldConditionData[i] timeNew, dataListNew = newConditionData[i] # Compare species observables if 'species' in self.observables: for smiles in self.observables['species']: fail = False oldRmgSpecies = oldSpeciesDict[smiles] newRmgSpecies = newSpeciesDict[smiles] if oldRmgSpecies: variableOld = next((data for data in dataListOld if data.species == oldRmgSpecies), None) else: print 'No RMG species found for observable species {0} in old model.'.format(smiles) fail = True if newRmgSpecies: variableNew = next((data for data in dataListNew if data.species == newRmgSpecies), None) else: print 'No RMG species found for observable species {0} in new model.'.format(smiles) fail = True if fail is False: if not curvesSimilar(timeOld.data, variableOld.data, timeNew.data, variableNew.data, 0.05): fail = True # Try plotting only when species are found in both models if plot: oldSpeciesPlot = SimulationPlot(xVar=timeOld, yVar=variableOld) newSpeciesPlot = SimulationPlot(xVar=timeNew, yVar=variableNew) oldSpeciesPlot.comparePlot(newSpeciesPlot, title='Observable Species {0} Comparison'.format(smiles), ylabel='Mole Fraction', filename='condition_{0}_species_{1}.png'.format(i+1,smiles)) # Append to failed variables or conditions if this test failed if fail: if i not in conditionsBroken: conditionsBroken.append(i) print "Observable species {0} does not match between old model {1} and \ new model {2} in condition {3:d}.".format(smiles, variableOld.label, variableNew.label, i+1) variablesFailed.append((self.conditions[i], smiles, variableOld, variableNew)) # Compare state variable observables if 'variable' in self.observables: for varName in self.observables['variable']: variableOld = next((data for data in dataListOld if data.label == varName), None) variableNew = next((data for data in dataListNew if data.label == varName), None) if not curvesSimilar(timeOld.data, variableOld.data, timeNew.data, variableNew.data, 0.05): if i not in conditionsBroken: conditionsBroken.append(i) print "Observable variable {0} does not match between old model and \ new model in condition {1:d}.".format(variableOld.label, i+1) variablesFailed.append((self.conditions[i], varName, variableOld, variableNew)) if plot: oldVarPlot = GenericPlot(xVar=timeOld, yVar=variableOld) newVarPlot = GenericPlot(xVar=timeNew, yVar=variableNew) oldVarPlot.comparePlot(newSpeciesPlot, title='Observable Variable {0} Comparison'.format(varName), filename='condition_{0}_variable_{1}.png'.format(i+1, varName)) # Compare ignition delay observables if 'ignitionDelay' in self.observables: print 'Ignition delay observable comparison not implemented yet.' print '' print 'The following reaction conditions were broken:' print '' for index in conditionsBroken: print "Condition {0:d}:".format(index+1) print str(self.conditions[index]) print '' return variablesFailed def runSimulations(self): """ Run a selection of conditions in Cantera and return generic data objects containing the time, pressure, temperature, and mole fractions from the simulations. Returns (oldConditionData, newConditionData) where conditionData is a list of of tuples: (time, dataList) for each condition in the same order as conditions time is a GenericData object which gives the time domain for each profile dataList is a list of GenericData objects for the temperature, profile, and mole fraction of major species """ oldConditionData = self.oldSim.simulate() newConditionData = self.newSim.simulate() return (oldConditionData, newConditionData)