99from quantiphy import Quantity
1010import sys
1111import matplotlib .pyplot as plt
12+ from scipy .interpolate import interp1d
1213import math
14+ import pandas as pd
1315
1416#begin/end tags
1517parameter_begin_tag = "**parameter_sweep_begin"
4648 os .remove (final_result_file_sorted )
4749
4850#get nr_workers from netlist
49- match = re .search (r"^\s*( \*\*nr_workers)\s*=\s*(.+) $" , netlist , re .MULTILINE )
51+ match = re .search (r"^[ \t]* \*\*nr_workers[ \t]*=[ \t]*(\d+)[ \t]* $" , netlist , re .MULTILINE )
5052if match :
51- nr_workers = int (match .group (2 ).strip ())
53+ nr_workers = int (match .group (1 ).strip ())
5254else :
53- print ("Info: **nr_workers statement not found, setting it to 10." )
55+ print ("Info: **nr_workers statement not found or erroneous , setting it to 10." )
5456 nr_workers = 10
5557
5658#get sort_results_index from netlist
57- match = re .search (r"^\s*( \*\*sort_results_index)\s*=\s*(.+) $" , netlist , re .MULTILINE )
59+ match = re .search (r"^[ \t]* \*\*sort_results_index[ \t]*=[ \t]*(\d+)[ \t]* $" , netlist , re .MULTILINE )
5860if match :
59- sort_results_index = int (match .group (2 ).strip ())
61+ sort_results_index = int (match .group (1 ).strip ())
6062else :
61- print ("Info: **sort_results_index statement not found, setting it to index 0." )
63+ print ("Info: **sort_results_index statement not found or erroneous , setting it to index 0." )
6264 sort_results_index = 0
6365
6466#get results_plot_contour_index from netlist
65- match = re .search (r"^\s*( \*\*results_plot_contour_index)\s*=\s*(.+) $" , netlist , re .MULTILINE )
67+ match = re .search (r"^[ \t]* \*\*results_plot_contour_index[ \t]*=[ \t]*(\d+(?:,[ \t]*\d+)*)[ \t]* $" , netlist , re .MULTILINE )
6668if match :
67- results_plot_contour_index_str = match .group (2 ).strip ()
69+ results_plot_contour_index_str = match .group (1 ).strip ()
6870 results_plot_contour_index = [int (i .strip ()) for i in results_plot_contour_index_str .split (',' ) if i .strip ().isdigit ()]
6971else :
70- print ("Info: **results_plot_contour_index statement not found, setting it to be empty." )
72+ print ("Info: **results_plot_contour_index statement not found or erroneous , setting it to be empty." )
7173 results_plot_contour_index = []
7274
7375#get results_plot_logx_index from netlist
74- match = re .search (r"^\s*( \*\*results_plot_logx_index)\s*=\s*(.+) $" , netlist , re .MULTILINE )
76+ match = re .search (r"^[ \t]* \*\*results_plot_logx_index[ \t]*=[ \t]*(\d+(?:,[ \t]*\d+)*)[ \t]* $" , netlist , re .MULTILINE )
7577if match :
76- results_plot_logx_index_str = match .group (2 ).strip ()
78+ results_plot_logx_index_str = match .group (1 ).strip ()
7779 results_plot_logx_index = [int (i .strip ()) for i in results_plot_logx_index_str .split (',' ) if i .strip ().isdigit ()]
7880else :
79- print ("Info: **results_plot_logx_index statement not found, setting it to be empty." )
81+ print ("Info: **results_plot_logx_index statement not found or erroneous , setting it to be empty." )
8082 results_plot_logx_index = []
8183
8284#get results_plot_logy_index from netlist
83- match = re .search (r"^\s*( \*\*results_plot_logy_index)\s*=\s*(.+) $" , netlist , re .MULTILINE )
85+ match = re .search (r"^[ \t]* \*\*results_plot_logy_index[ \t]*=[ \t]*(\d+(?:,[ \t]*\d+)*)[ \t]* $" , netlist , re .MULTILINE )
8486if match :
85- results_plot_logy_index_str = match .group (2 ).strip ()
87+ results_plot_logy_index_str = match .group (1 ).strip ()
8688 results_plot_logy_index = [int (i .strip ()) for i in results_plot_logy_index_str .split (',' ) if i .strip ().isdigit ()]
8789else :
88- print ("Info: **results_plot_logy_index statement not found, setting it to be empty." )
90+ print ("Info: **results_plot_logy_index statement not found or erroneous , setting it to be empty." )
8991 results_plot_logy_index = []
9092
9193#get sweep parameters from netlist
115117 if list_type == "auto" :
116118 sweep_list = np .linspace (Quantity (values_splited [1 ]), Quantity (values_splited [3 ]), int (values_splited [2 ]))
117119 elif list_type == "lin" :
118- sweep_list = np .arange (Quantity (values_splited [1 ]), Quantity (values_splited [3 ]) + Quantity (values_splited [2 ]), Quantity (values_splited [2 ]))
120+ sweep_list = np .arange (Quantity (values_splited [1 ]), ( Quantity (values_splited [3 ]) + Quantity (values_splited [2 ]) * 0.1 ), Quantity (values_splited [2 ]))
119121 elif list_type == "dec" :
120122 total_points = int (int (values_splited [2 ]) * (np .log10 (Quantity (values_splited [3 ]))- np .log10 (Quantity (values_splited [1 ])))) + 1
121123 sweep_list = np .logspace (np .log10 (Quantity (values_splited [1 ])), np .log10 (Quantity (values_splited [3 ])), total_points )
@@ -184,15 +186,19 @@ def run_worker(args):
184186 result [result_name .strip ()] = float (result_value .strip ())
185187 # write in csv file
186188 if writeHeadings :
187- writer .writerow (param_name_list + list (result .keys ()))
189+ writer .writerow (param_name_list + list (result .keys ()))
188190 writeHeadings = False
189191 writer .writerow ([str (x ) for x in param_set ] + list (result .values ()))
190192 else :
191- writer .writerow ([param_name_list , 'N/A' , 'ERROR in receiving results' ])
193+ if writeHeadings :
194+ writer .writerow (param_name_list )
195+ writeHeadings = False
196+ writer .writerow (["," .join (str (x ) for x in param_set ), 'N/A' , 'ERROR in receiving results' ])
192197 except subprocess .CalledProcessError :
193198 if writeHeadings :
194- writer .writerow ([param_name_list , 'N/A' , 'ERROR' ])
195- writer .writerow ([param_name_list , 'N/A' , 'ERROR in process handling' ])
199+ writer .writerow (param_name_list )
200+ writeHeadings = False
201+ writer .writerow (["," .join (str (x ) for x in param_set ), 'N/A' , 'ERROR in process handling' ])
196202 os .remove (cir_file )
197203
198204if __name__ == "__main__" :
@@ -214,36 +220,19 @@ def run_worker(args):
214220 out .writelines ([line for line in lines if "ERROR" not in line ])
215221 else : # skip the header
216222 out .writelines ([line for line in lines [1 :] if "ERROR" not in line ])
223+ error_lines = [line for line in lines [1 :] if "ERROR" in line ]
224+ if error_lines :
225+ print (f"Errors found in worker { i } results:" )
226+ print ("" .join (error_lines ))
217227 os .remove (part_file )
218-
219- with open (final_result_file , newline = '' ) as csvfile :
220- reader = csv .reader (csvfile , delimiter = ';' )
221- header = next (reader )
222- data = list (reader )
223-
224- data .sort (key = lambda x : float (x [sort_results_index ]), reverse = False )
225228
226- # create new file with sorted data
227- with open (final_result_file_sorted , mode = 'w' , newline = '' ) as csvfile :
228- writer = csv .writer (csvfile , delimiter = ';' )
229- writer .writerow (header )
230- writer .writerows (data )
229+ df = pd .read_csv (final_result_file , delimiter = ';' )
230+ df_sorted = df .sort_values (by = df .columns [sort_results_index ], ascending = True )
231+ df_sorted .to_csv (final_result_file_sorted , sep = ';' , index = False )
231232
232233 # plot results
233234 if len (results_plot_list ) > 0 :
234- # make dictionary with results
235- results_dict = {}
236- with open (final_result_file_sorted , mode = 'r' , newline = '' ) as csvfile :
237- reader = csv .DictReader (csvfile , delimiter = ';' )
238-
239- # Initialize dictionary with keys from the header
240- for header in reader .fieldnames :
241- results_dict [header ] = []
242-
243- # Fill the dictionary
244- for row in reader :
245- for header in reader .fieldnames :
246- results_dict [header ].append (float (row [header ]))
235+ df_sorted = df .sort_values (by = param_name_list )
247236
248237 # Set the number of columns for subplot grid
249238 n_cols = 2 # Change this to 1, 2, 3, etc.
@@ -256,20 +245,25 @@ def run_worker(args):
256245 i = 0
257246 if len (param_name_list ) == 1 :
258247 for var in results_plot_list :
248+ actual_size = len (df_sorted [var ])
249+ expected_size = len (next (iter (param_list .values ())))
250+ if actual_size != expected_size :
251+ print (f"Error with variable { var } : Expected { expected_size } data points but got { actual_size } ." )
252+ print (f"Hint: This could be due to missing or failed simulations." )
259253 axs [i ].set_title (var )
260254 axs [i ].grid (True , which = 'both' , linestyle = '--' , linewidth = 0.5 )
261255 axs [i ].minorticks_on ()
262256 axs [i ].set_xlabel (f"{ param_name_list [0 ]} " )
263257 axs [i ].set_ylabel (f"{ var } " )
264258 var = var .lower ()
265259 if i in results_plot_logx_index and i in results_plot_logy_index :
266- axs [i ].loglog (results_dict [param_name_list [0 ]], results_dict [var ])
260+ axs [i ].loglog (df_sorted [param_name_list [0 ]], df_sorted [var ])
267261 elif i in results_plot_logx_index :
268- axs [i ].semilogx (results_dict [param_name_list [0 ]], results_dict [var ])
262+ axs [i ].semilogx (df_sorted [param_name_list [0 ]], df_sorted [var ])
269263 elif i in results_plot_logy_index :
270- axs [i ].semilogy (results_dict [param_name_list [0 ]], results_dict [var ])
264+ axs [i ].semilogy (df_sorted [param_name_list [0 ]], df_sorted [var ])
271265 else :
272- axs [i ].plot (results_dict [param_name_list [0 ]], results_dict [var ])
266+ axs [i ].plot (df_sorted [param_name_list [0 ]], df_sorted [var ])
273267 i = i + 1
274268 # Hide unused subplots
275269 for j in range (i , len (axs )):
@@ -282,18 +276,41 @@ def run_worker(args):
282276 axs [i ].set_xlabel (f"{ param_name_list [0 ]} " )
283277 axs [i ].grid (True , which = 'both' , linestyle = '--' , alpha = 0.5 )
284278 axs [i ].minorticks_on ()
285- x = np .unique (np .array (results_dict [param_name_list [0 ]]))
286- y = np .unique (np .array (results_dict [param_name_list [1 ]]))
287- Z = np .array (results_dict [var .lower ()]).reshape (len (x ), len (y )).T # shape: (len(y), len(x))
279+ x_raw = np .array (df_sorted [param_name_list [0 ]])
280+ y_raw = np .array (df_sorted [param_name_list [1 ]])
281+ z_raw = np .array (df_sorted [var .lower ()])
282+ x = np .unique (x_raw )
283+ y = np .unique (y_raw )
284+ expected_size = len (x ) * len (y )
285+ if z_raw .size != expected_size :
286+ print (f"Error with variable { var } : Expected { expected_size } data points but got { z_raw .size } ." )
287+ print (f"Hint: This could be due to missing or failed simulations. Interpolating misssing points!" )
288+ Z = np .full ((len (x ), len (y )), np .nan )
289+ for yi_index , yi in enumerate (y ):
290+ mask = (y_raw == yi )
291+ xi_vals = x_raw [mask ]
292+ zi_vals = z_raw [mask ]
293+ if len (xi_vals ) < 2 :
294+ print (f"Skipping interpolation at y={ yi :.3g} : Not enough points." )
295+ continue
296+ try :
297+ interp_fn = interp1d (xi_vals , zi_vals , kind = 'linear' , bounds_error = False , fill_value = np .nan )
298+ zi_interp = interp_fn (x )
299+ except Exception as e :
300+ print (f"Interpolation failed at y={ yi :.3g} : { e } " )
301+ zi_interp = np .full_like (x , np .nan )
302+ Z [:, yi_index ] = zi_interp
303+ else :
304+ Z = z_raw .reshape (len (x ), len (y ))
305+ Z_plot = Z .T # shape (len(y), len(x))
288306 if i in results_plot_contour_index :
289307 axs [i ].set_ylabel (f"{ param_name_list [1 ]} " )
290308 X , Y = np .meshgrid (x ,y )
291- contour = axs [i ].contourf (X , Y , Z , levels = 10 , cmap = 'viridis' )
309+ contour = axs [i ].contourf (X , Y , Z_plot , levels = 10 , cmap = 'viridis' )
292310 cbar = fig .colorbar (contour , ax = axs [i ])
293311 cbar .set_label (var )
294312 else :
295313 axs [i ].set_ylabel (var )
296- Z = Z .T
297314 if i in results_plot_logx_index and i in results_plot_logy_index :
298315 for l , yl in enumerate (y ):
299316 axs [i ].loglog (x , Z [:, l ], label = f"{ yl :.2g} " )
0 commit comments