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 } \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 ) } { round (T ,2 )}  )
136+                             self .idt_dict [equivalence_ratios [i ]][ P ][ T ] =  \
137+                                 self .simulate_idt (fig_name = f'R{ r } { equivalence_ratios [i ]} { P :.2f } { round (T ,2 )}  )
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 } \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 )} { round ( T , 2 ) }  )
145+                             self .idt_dict [0 ][ P ][ T ] =  \
146+                                 self .simulate_idt (fig_name = f'R{ r } { round (P ,2 )} { T :.2f }  )
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 ) } { round ( T , 2 ) }  )
148+                             self .idt_dict [equivalence_ratios [i ]][ P ][ T ] =  \
149+                                 self .simulate_idt (fig_name = f'R{ r } { equivalence_ratios [i ]} { P :.2f } { T :.2f }  )
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 }  )
408+             continue 
393409        for  p , phi_p_data  in  phi_data .items ():
394-             fig_name  =  f'R{ reactor_index } { phi } { round (p ,2 )}  
410+             if  exp_data  is  not None  and  p  not  in exp_data [phi ]:
411+                 print (f'p { p }  )
412+                 continue 
413+             fig_name  =  f'R{ reactor_index } { phi } { p :.2f}  
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 }  )
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 :.2f }  )
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