1414
1515from arc .common import save_yaml_file
1616
17- from t3 .common import determine_concentrations_by_equivalence_ratios
17+ from t3 .common import determine_concentrations_by_equivalence_ratios , remove_numeric_parentheses
1818from t3 .logger import Logger
1919from t3 .simulate .adapter import SimulateAdapter
2020from t3 .simulate .factory import register_simulate_adapter
@@ -84,12 +84,11 @@ def __init__(self,
8484 self .spc_identifier_lookup , self .rxn_identifier_lookup = dict (), dict ()
8585 self .num_ct_reactions = None
8686 self .num_ct_species = None
87- self .T_list , self .P_list , self .reaction_time_list = list (), list (), list ()
87+ self .T_list , self .P_list , self .reaction_time_list , self . species_names_without_indices = list (), list (), list (), list ()
8888 self .idt_dict = dict ()
8989
9090 self .set_up ()
9191 self .radical_label = self .determine_radical_label ()
92- print (f'radical label: { self .radical_label } ' )
9392
9493 def set_up (self ):
9594 """
@@ -106,7 +105,8 @@ def set_up(self):
106105 self .spc_identifier_lookup [spc .name ] = i
107106 for i , rxn in enumerate (self .model .reactions ()):
108107 self .rxn_identifier_lookup [rxn .equation ] = i
109- self .species_names_without_indices = [self .model .species ()[i ].name .split ('(' )[0 ] for i in range (self .num_ct_species )] # Todo: what about HNO(T)(21)? make more robust
108+ self .species_names_without_indices = [remove_numeric_parentheses (self .model .species ()[i ].name )
109+ for i in range (self .num_ct_species )]
110110
111111 self .T_list = ([self .rmg ['reactors' ][0 ]['T' ]], 'K' )
112112 self .P_list = ([self .rmg ['reactors' ][0 ]['P' ]], 'bar' )
@@ -119,29 +119,34 @@ def simulate(self):
119119 self .logger .info ('Running a simulation using CanteraIDT...' )
120120
121121 equivalence_ratios , concentration_combinations = self .get_concentration_combinations ()
122+ print (f'equivalence_ratios: { equivalence_ratios } \n concentration_combinations: { concentration_combinations } ' )
122123 reactor_idt_dict = dict ()
123124 for r , reactor in enumerate (self .rmg ['reactors' ]):
124125 T_list , P_list = get_t_and_p_lists (reactor )
125126 if equivalence_ratios is not None and concentration_combinations is not None :
126127 for i , X in enumerate (concentration_combinations ):
128+ if self .idt_dict .get (equivalence_ratios [i ]) is None :
129+ self .idt_dict [equivalence_ratios [i ]] = dict ()
127130 for P in P_list :
131+ if self .idt_dict [equivalence_ratios [i ]].get (P ) is None :
132+ self .idt_dict [equivalence_ratios [i ]][P ] = dict ()
128133 for T in T_list :
129134 print (f'T: { T } , P: { P } ,\n X: { X } ' )
130135 self .model .TPX = T , P * 1e5 , X
131- self .idt_dict [( equivalence_ratios [i ], P , T ) ] = \
132- self .simulate_idt (fig_name = f'R{ r } _{ equivalence_ratios [i ]} _{ round ( P , 2 ) } _bar_{ round (T ,2 )} _K.png' )
136+ self .idt_dict [equivalence_ratios [i ]][ P ][ T ] = \
137+ self .simulate_idt (fig_name = f'R{ r } _{ equivalence_ratios [i ]} _{ P :.2f } _bar_{ round (T ,2 )} _K.png' )
133138 else :
134139 X = {spc ['label' ]: spc ['concentration' ] for spc in self .rmg ['species' ] if spc ['concentration' ]}
135140 for P in P_list :
136141 for T in T_list :
137142 print (f'T: { T } , P: { P } ,\n X: { X } ' )
138143 self .model .TPX = T , P * 1e5 , X
139144 if equivalence_ratios is None :
140- self .idt_dict [( 0 , P , T ) ] = \
141- self .simulate_idt (fig_name = f'R{ r } _{ round (P ,2 )} _bar_{ round ( T , 2 ) } _K.png' )
145+ self .idt_dict [0 ][ P ][ T ] = \
146+ self .simulate_idt (fig_name = f'R{ r } _{ round (P ,2 )} _bar_{ T :.2f } _K.png' )
142147 else :
143- self .idt_dict [( equivalence_ratios [i ], P , T ) ] = \
144- self .simulate_idt (fig_name = f'R{ r } _{ equivalence_ratios [i ]} _{ round ( P , 2 ) } _bar_{ round ( T , 2 ) } _K.png' )
148+ self .idt_dict [equivalence_ratios [i ]][ P ][ T ] = \
149+ self .simulate_idt (fig_name = f'R{ r } _{ equivalence_ratios [i ]} _{ P :.2f } _bar_{ T :.2f } _K.png' )
145150 if len (T_list ) >= 3 :
146151 plot_idt_vs_temperature (self .idt_dict , figs_path = self .paths ['figs' ], reactor_index = r )
147152 reactor_idt_dict [r ] = self .idt_dict
@@ -212,6 +217,8 @@ def get_cantera_species_label(self, rmg_label: str) -> Optional[str]:
212217 for i , label in enumerate (self .species_names_without_indices ):
213218 if label == rmg_label :
214219 return self .model .species ()[i ].name
220+ if label == remove_numeric_parentheses (rmg_label ):
221+ return self .model .species ()[i ].name
215222 return None
216223
217224 def get_concentration_combinations (self ) -> Tuple [Optional [List [float ]], Optional [List [dict ]]]:
@@ -236,8 +243,8 @@ def get_concentration_combinations(self) -> Tuple[Optional[List[float]], Optiona
236243 concentration_dict = dict ()
237244 for spc in self .rmg ['species' ]:
238245 if spc ['role' ] is None :
239- cantera_label = self .get_cantera_species_label (spc ['label' ])
240- if cantera_label is not None :
246+ cantera_label = self .get_cantera_species_label (spc ['label' ]) # can take out of loop
247+ if cantera_label is not None and spc [ 'concentration' ] != 0 :
241248 concentration_dict [cantera_label ] = spc ['concentration' ]
242249 if spc ['role' ] == 'fuel' :
243250 cantera_label = self .get_cantera_species_label (spc ['label' ])
@@ -307,7 +314,7 @@ def compute_idt(time_history: ct.SolutionArray,
307314 Optional[float]: The IDT in seconds.
308315
309316 Todo:
310- - solve possible noice in dc/dt. ** add IDT(1000/T) figure.
317+ - solve possible noise in dc/dt. ** add IDT(1000/T) figure.
311318 """
312319 figs_path = os .path .join (figs_path , 'IDTs' )
313320 if not os .path .isdir (figs_path ):
@@ -317,7 +324,7 @@ def compute_idt(time_history: ct.SolutionArray,
317324 dc_dt = np .diff (concentration ) / np .diff (times )
318325 idt_index_dc_dt = np .argmax (dc_dt )
319326 idt_index_c = np .argmax (concentration )
320- idt = times [idt_index_dc_dt ]
327+ idt = float ( times [idt_index_dc_dt ])
321328 if idt_index_dc_dt > len (times ) - 10 or idt < 1e-12 or max (concentration ) < concentration [0 ] * 100 :
322329 return None
323330 try :
@@ -354,80 +361,72 @@ def get_t_and_p_lists(reactor: dict) -> Tuple[List[float], List[float]]:
354361 """
355362 if isinstance (reactor ['T' ], (int , float )):
356363 T_list = [reactor ['T' ]]
357- else :
364+ elif len ( reactor [ 'T' ]) == 2 :
358365 inverse_ts = np .linspace (1 / reactor ['T' ][1 ],
359- 1 / reactor ['T' ][0 ], num = 100 ) # 15 inverse T points
366+ 1 / reactor ['T' ][0 ], num = min ( int ( abs ( reactor [ 'T' ][ 1 ] - reactor [ 'T' ][ 0 ]) / 10 ), 50 ))
360367 T_list = [1 / inverse_t for inverse_t in inverse_ts [::- 1 ]]
368+ else :
369+ T_list = [float (t ) for t in reactor ['T' ]]
361370 if isinstance (reactor ['P' ], (int , float )):
362371 P_list = [reactor ['P' ]]
363- else :
372+ elif len ( reactor [ 'P' ]) == 2 :
364373 base = 10
365374 log_p = np .linspace (math .log (reactor ['P' ][0 ], base ),
366375 math .log (reactor ['P' ][1 ], base ), num = 3 ) # 3 pressure in log10 space
367376 P_list = [base ** p for p in log_p ]
377+ else :
378+ P_list = [float (p ) for p in reactor ['P' ]]
379+ T_list = [float (t ) for t in T_list ]
380+ P_list = [float (p ) for p in P_list ]
368381 return T_list , P_list
369382
370383
371384def plot_idt_vs_temperature (idt_dict : dict ,
372385 figs_path : str ,
373386 reactor_index : int = 0 ,
374- exp_data : tuple = None ,
387+ exp_data : dict = None ,
375388 ) -> None :
376389 """
377390 Plot IDT vs. 1000/T per phi and P condition combination.
391+ If exp_data is provided, plot the experimental data as well and only consider phi and P values that appear in it.
378392
379- idt_dict[( equivalence_ratios[i], P, T) ]
393+ idt_dict[equivalence_ratios[i]][P][T ]
380394
381395 Args:
382396 idt_dict (dict): A dictionary containing IDT values.
383397 figs_path (str): The path to the figures' directory.
384398 reactor_index (int, optional): The reactor index.
385- exp_data (tuple ): Experimental data in lists for IDT [sec] and 1000/T [K]
399+ exp_data (dict ): Experimental data in the same format as idt_dict, IDT units are in s.
386400 """
387401 figs_path = os .path .join (figs_path , 'IDT_vs_T' )
388402 if not os .path .isdir (figs_path ):
389403 os .makedirs (figs_path )
390- data = get_idt_per_phi_p_condition (idt_dict )
391-
392- for phi , phi_data in data .items ():
404+ for phi , phi_data in idt_dict .items ():
405+ print (f'phi: { phi } ' )
406+ if exp_data is not None and phi not in exp_data :
407+ print (f'phi { phi } not in exp_data' )
408+ continue
393409 for p , phi_p_data in phi_data .items ():
394- fig_name = f'R{ reactor_index } _{ phi } _{ round (p ,2 )} _bar.png'
410+ if exp_data is not None and p not in exp_data [phi ]:
411+ print (f'p { p } not in exp_data' )
412+ continue
413+ fig_name = f'R{ reactor_index } _{ phi } _{ p :.2f} _bar.png'
414+ print (f'fig_name: { fig_name } ' )
395415 try :
396416 fig , ax = plt .subplots ()
397417 ax .set_xlabel ('1000/T (1/K)' )
398418 ax .set_ylabel ('IDT (s)' )
399- ax .set_title (f'IDT vs. 1000/T, phi = { phi } , P = { p } bar' )
400- ax .scatter (phi_p_data .keys (), phi_p_data .values (), label = 'simulation' , color = 'blue' , marker = "o" )
419+ ax .set_title (f'IDT vs. 1000/T, phi = { phi } , P = { p :.2f } bar' )
420+ ax .scatter ([ 1000 / t for t in phi_p_data .keys ()] , phi_p_data .values (), label = 'simulation' , color = 'blue' , marker = 'o' , linestyle = '-' )
401421 ax .set_yscale ('log' )
402422 if exp_data is not None :
403- ax .scatter (exp_data [0 ], exp_data [1 ], label = 'experiment' , color = 'orange' , marker = "D" )
423+ ax .scatter ([ 1000 / t for t in exp_data [phi ][ p ]. keys ()], [ e * 1e-6 for e in exp_data [phi ][ p ]. values () ], label = 'experiment' , color = 'orange' , marker = "D" )
404424 ax .set_yscale ('log' )
405425 ax .legend (loc = 'lower right' )
406426 fig .savefig (os .path .join (figs_path , fig_name ))
407427 except (AttributeError , ValueError ):
428+ raise
408429 pass
409430
410431
411- def get_idt_per_phi_p_condition (idt_dict : dict ) -> dict :
412- """
413- Get IDT values per phi, P, and 1000/T condition combination.
414-
415- Args:
416- idt_dict (dict): A dictionary containing IDT values.
417-
418- Returns:
419- dict: A dictionary containing IDT values per phi and P condition combination corresponding to the 1000/T list.
420- """
421- data = dict ()
422- for triple_key in idt_dict .keys ():
423- phi , p , _ = triple_key
424- if phi not in data .keys ():
425- data [phi ] = dict ()
426- if p not in data [phi ].keys ():
427- data [phi ][p ] = dict ()
428- for triple_key , value in idt_dict .items ():
429- data [triple_key [0 ]][triple_key [1 ]][1000 / triple_key [2 ]] = value
430- return data
431-
432-
433432register_simulate_adapter ("CanteraIDT" , CanteraIDT )
0 commit comments