Skip to content

Commit cafb499

Browse files
committed
Add surface mechanisms to regression tool
1 parent dc2da84 commit cafb499

File tree

3 files changed

+167
-40
lines changed

3 files changed

+167
-40
lines changed

rmgpy/tools/canteramodel.py

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class CanteraCondition(object):
6363
6464
"""
6565

66-
def __init__(self, reactor_type, reaction_time, mol_frac, T0=None, P0=None, V0=None):
66+
def __init__(self, reactor_type, reaction_time, mol_frac, surface_mol_frac=None, T0=None, P0=None, V0=None):
6767
self.reactor_type = reactor_type
6868
self.reaction_time = Quantity(reaction_time)
6969

@@ -75,6 +75,14 @@ def __init__(self, reactor_type, reaction_time, mol_frac, T0=None, P0=None, V0=N
7575

7676
self.mol_frac = mol_frac
7777

78+
if surface_mol_frac:
79+
# normalize surface mol fractions
80+
if sum(surface_mol_frac.values()) != 1.00:
81+
total = sum(surface_mol_frac.values())
82+
for species, value in surface_mol_frac.items():
83+
surface_mol_frac[species] = value / total
84+
self.surface_mol_frac = surface_mol_frac
85+
7886
# Check to see that one of the three attributes T0, P0, and V0 is less unspecified
7987
props = [T0, P0, V0]
8088
total = 0
@@ -121,7 +129,7 @@ def __str__(self):
121129
return string
122130

123131

124-
def generate_cantera_conditions(reactor_type_list, reaction_time_list, mol_frac_list, Tlist=None, Plist=None, Vlist=None):
132+
def generate_cantera_conditions(reactor_type_list, reaction_time_list, mol_frac_list, surface_mol_frac_list=[None], Tlist=None, Plist=None, Vlist=None):
125133
"""
126134
Creates a list of cantera conditions from from the arguments provided.
127135
@@ -137,6 +145,8 @@ def generate_cantera_conditions(reactor_type_list, reaction_time_list, mol_frac_
137145
`reaction_time_list` A tuple object giving the ([list of reaction times], units)
138146
`mol_frac_list` A list of molfrac dictionaries with species object keys
139147
and mole fraction values
148+
`surface_mol_frac_list` A list of molfrac dictionaries with surface species object keys
149+
and mole fraction values
140150
To specify the system for an ideal gas, you must define 2 of the following 3 parameters:
141151
`T0List` A tuple giving the ([list of initial temperatures], units)
142152
'P0List' A tuple giving the ([list of initial pressures], units)
@@ -167,25 +177,28 @@ def generate_cantera_conditions(reactor_type_list, reaction_time_list, mol_frac_
167177
for reactor_type in reactor_type_list:
168178
for reaction_time in reaction_time_list:
169179
for mol_frac in mol_frac_list:
170-
for P in Plist:
171-
for V in Vlist:
172-
conditions.append(CanteraCondition(reactor_type, reaction_time, mol_frac, P0=P, V0=V))
180+
for surface_mol_frac in surface_mol_frac_list:
181+
for P in Plist:
182+
for V in Vlist:
183+
conditions.append(CanteraCondition(reactor_type, reaction_time, mol_frac, surface_mol_frac=surface_mol_frac, P0=P, V0=V))
173184

174185
elif Plist is None:
175186
for reactor_type in reactor_type_list:
176187
for reaction_time in reaction_time_list:
177188
for mol_frac in mol_frac_list:
178-
for T in Tlist:
179-
for V in Vlist:
180-
conditions.append(CanteraCondition(reactor_type, reaction_time, mol_frac, T0=T, V0=V))
189+
for surface_mol_frac in surface_mol_frac_list:
190+
for T in Tlist:
191+
for V in Vlist:
192+
conditions.append(CanteraCondition(reactor_type, reaction_time, mol_frac, surface_mol_frac=surface_mol_frac, T0=T, V0=V))
181193

182194
elif Vlist is None:
183195
for reactor_type in reactor_type_list:
184196
for reaction_time in reaction_time_list:
185197
for mol_frac in mol_frac_list:
186-
for T in Tlist:
187-
for P in Plist:
188-
conditions.append(CanteraCondition(reactor_type, reaction_time, mol_frac, T0=T, P0=P))
198+
for surface_mol_frac in surface_mol_frac_list:
199+
for T in Tlist:
200+
for P in Plist:
201+
conditions.append(CanteraCondition(reactor_type, reaction_time, mol_frac, surface_mol_frac=surface_mol_frac, T0=T, P0=P))
189202

190203
else:
191204
raise Exception("Cantera conditions must leave one of T0, P0, and V0 state variables unspecified")
@@ -198,7 +211,7 @@ class Cantera(object):
198211
"""
199212

200213
def __init__(self, species_list=None, reaction_list=None, canteraFile='', output_directory='', conditions=None,
201-
sensitive_species=None, thermo_SA=False):
214+
sensitive_species=None, thermo_SA=False, surface=False, surface_species_list=None, surface_reaction_list=None):
202215
"""
203216
`species_list`: list of RMG species objects
204217
`reaction_list`: list of RMG reaction objects
@@ -208,24 +221,39 @@ def __init__(self, species_list=None, reaction_list=None, canteraFile='', output
208221
`sensitive_species`: a list of RMG species objects for conductng sensitivity analysis on
209222
`thermo_SA`: a boolean indicating whether or not to run thermo SA. By default, if sensitive_species is given,
210223
only kinetic_SA will be calculated and it must be additionally specified to perform thermo SA.
224+
`surface`: a boolean indicating whether or not to run a surface simulation
211225
"""
212226
self.species_list = species_list
213227
self.reaction_list = reaction_list
214228
self.reaction_map = {}
215229
self.model = ct.Solution(canteraFile) if canteraFile else None
230+
self.surface = surface
231+
self.surface_species_list = surface_species_list
216232
self.output_directory = output_directory if output_directory else os.getcwd()
217233
self.conditions = conditions if conditions else []
218234
self.sensitive_species = sensitive_species if sensitive_species else []
219235
self.thermo_SA = thermo_SA
220236

237+
# need a way to make a surface simulation... for now, we'll assume there's a Cantera surface yaml file
238+
if surface:
239+
# assert canteraFile, "Cantera file must be specified to run a surface simulation" # or we add the capability to load it later
240+
assert surface_species_list, "Surface species list must be specified to run a surface simulation"
241+
# TODO some kind of assertion for starting surface mol fractions
242+
# self.surface_species_list = surface_species_list
243+
244+
if canteraFile:
245+
self.surface = ct.Interface(canteraFile, 'surface1')
246+
self.model = self.surface.adjacent['gas']
247+
# also need starting surface mol fractions
248+
221249
# Make output directory if it does not yet exist:
222250
if not os.path.exists(self.output_directory):
223251
try:
224252
os.makedirs(self.output_directory)
225253
except:
226254
raise Exception('Cantera output directory could not be created.')
227255

228-
def generate_conditions(self, reactor_type_list, reaction_time_list, mol_frac_list, Tlist=None, Plist=None, Vlist=None):
256+
def generate_conditions(self, reactor_type_list, reaction_time_list, mol_frac_list, surface_mol_frac_list=[None], Tlist=None, Plist=None, Vlist=None):
229257
"""
230258
This saves all the reaction conditions into the Cantera class.
231259
======================= ====================================================
@@ -240,19 +268,21 @@ def generate_conditions(self, reactor_type_list, reaction_time_list, mol_frac_li
240268
`reaction_time_list` A tuple object giving the ([list of reaction times], units)
241269
`mol_frac_list` A list of molfrac dictionaries with species object keys
242270
and mole fraction values
271+
`surface_mol_frac_list` A list of molfrac dictionaries with surface species object keys and mole fraction values
243272
To specify the system for an ideal gas, you must define 2 of the following 3 parameters:
244273
`T0List` A tuple giving the ([list of initial temperatures], units)
245274
'P0List' A tuple giving the ([list of initial pressures], units)
246275
'V0List' A tuple giving the ([list of initial specific volumes], units)
247276
"""
248277

249-
self.conditions = generate_cantera_conditions(reactor_type_list, reaction_time_list, mol_frac_list, Tlist, Plist, Vlist)
278+
self.conditions = generate_cantera_conditions(reactor_type_list, reaction_time_list, mol_frac_list, surface_mol_frac_list, Tlist, Plist, Vlist)
250279

251280
def load_model(self):
252281
"""
253282
Load a cantera Solution model from the job's own species_list and reaction_list attributes
254283
"""
255-
284+
# TODO add surface version - this is limited by the rmgpy.kinetics.arrhenius.Arrhenius.to_cantera_kinetics()
285+
# function not being able to handle surface reactions yet
256286
ct_species = [spec.to_cantera(use_chemkin_identifier=True) for spec in self.species_list]
257287

258288
self.reaction_map = {}
@@ -292,6 +322,7 @@ def load_chemkin_model(self, chemkin_file, transport_file=None, **kwargs):
292322
Convert a chemkin mechanism chem.inp file to a cantera mechanism file chem.yaml
293323
and save it in the output_directory
294324
Then load it into self.model
325+
Note that if this is a surface mechanism, the calling function much include surface_file as a keyword argument
295326
"""
296327
from cantera import ck2yaml
297328

@@ -301,9 +332,14 @@ def load_chemkin_model(self, chemkin_file, transport_file=None, **kwargs):
301332
if os.path.exists(out_name):
302333
os.remove(out_name)
303334
parser = ck2yaml.Parser()
335+
304336
parser.convert_mech(chemkin_file, transport_file=transport_file, out_name=out_name, **kwargs)
305337
self.model = ct.Solution(out_name)
306338

339+
if self.surface:
340+
self.surface = ct.Interface(out_name, 'surface1')
341+
self.model = self.surface.adjacent['gas']
342+
307343
def modify_reaction_kinetics(self, rmg_reaction_index, rmg_reaction):
308344
"""
309345
Modify the corresponding cantera reaction's kinetics to match
@@ -385,6 +421,9 @@ def simulate(self):
385421
species_names_list = [get_species_identifier(species) for species in self.species_list]
386422
inert_index_list = [self.species_list.index(species) for species in self.species_list if species.index == -1]
387423

424+
if self.surface:
425+
surface_species_names_list = [get_species_identifier(species) for species in self.surface_species_list]
426+
388427
all_data = []
389428
for condition in self.conditions:
390429

@@ -394,6 +433,12 @@ def simulate(self):
394433
newkey = get_species_identifier(key)
395434
new_mol_frac[newkey] = value
396435

436+
if self.surface:
437+
new_surface_mol_frac = {}
438+
for key, value in condition.surface_mol_frac.items(): # this needs to be aspecies object
439+
newkey = get_species_identifier(key)
440+
new_surface_mol_frac[newkey] = value
441+
397442
# Set Cantera simulation conditions
398443
if condition.V0 is None:
399444
self.model.TPX = condition.T0.value_si, condition.P0.value_si, new_mol_frac
@@ -413,6 +458,11 @@ def simulate(self):
413458
else:
414459
raise Exception('Other types of reactor conditions are currently not supported')
415460

461+
# add the surface/gas adjacent thing...
462+
if self.surface:
463+
rsurf = ct.ReactorSurface(self.surface, cantera_reactor)
464+
self.surface.TPX = condition.T0.value_si, condition.P0.value_si, new_surface_mol_frac
465+
416466
# Run this individual condition as a simulation
417467
cantera_simulation = ct.ReactorNet([cantera_reactor])
418468

@@ -451,7 +501,12 @@ def simulate(self):
451501
times.append(cantera_simulation.time)
452502
temperature.append(cantera_reactor.T)
453503
pressure.append(cantera_reactor.thermo.P)
454-
species_data.append(cantera_reactor.thermo[species_names_list].X)
504+
505+
if self.surface:
506+
species_data.append(np.concatenate((cantera_reactor.thermo[species_names_list].X, rsurf.kinetics.coverages)))
507+
N_gas = len(cantera_reactor.thermo[species_names_list].X)
508+
else:
509+
species_data.append(cantera_reactor.thermo[species_names_list].X)
455510

456511
if self.sensitive_species:
457512
# Cantera returns mass-based sensitivities rather than molar concentration or mole fraction based sensitivities.
@@ -531,6 +586,15 @@ def simulate(self):
531586
index=species.index
532587
)
533588
condition_data.append(species_generic_data)
589+
if self.surface:
590+
for index, species in enumerate(self.surface_species_list):
591+
# Create generic data object that saves the surface species object into the species object.
592+
species_generic_data = GenericData(label=surface_species_names_list[index],
593+
species=species,
594+
data=species_data[:, index + N_gas],
595+
index=species.index + N_gas
596+
)
597+
condition_data.append(species_generic_data)
534598

535599
# save kinetic data as generic data object
536600
reaction_sensitivity_data = []

0 commit comments

Comments
 (0)