@@ -268,8 +268,14 @@ def read_csv_cached(path, **kwargs):
268268 }
269269 safe_na_values = default_na
270270
271- df = pd .read_csv (configdir + config_filename , sep = ',' , engine = 'python' ,
272- quotechar = '"' , na_values = safe_na_values , keep_default_na = False )
271+ df = pd .read_csv (
272+ configdir + config_filename ,
273+ sep = ',' ,
274+ engine = 'python' ,
275+ quotechar = '"' ,
276+ na_values = safe_na_values ,
277+ keep_default_na = False
278+ )
273279 df ["__orig_index__" ] = df .index
274280 C = df .where (pd .notnull (df ), None )
275281
@@ -355,6 +361,7 @@ def read_csv_cached(path, **kwargs):
355361 start_idx = int (pp .d1_Data_Row - pp .d1_Col_Name_Row - 1 )
356362 x , _ = get_data (E , pp .d1_Ind_Col_Name , start_idx )
357363 y , _ = get_data (E , pp .d1_Dep_Col_Name , start_idx )
364+
358365 flip_axis = str (pp .Flip_Axis ).strip ().lower () in ['yes' , 'true' , '1' ]
359366 x_scale = float (pp .Scale_Ind or 1.0 )
360367 y_scale = float (pp .Scale_Dep or 1.0 )
@@ -396,15 +403,22 @@ def read_csv_cached(path, **kwargs):
396403 else :
397404 continue
398405
406+ # --- Styles and keys (EXP d1) ---
407+ d1_raw_styles = [c .strip () for c in (pp .d1_Style or '' ).split ('|' )] if pp .d1_Style else []
408+ d1_styles = (d1_raw_styles + [None ] * len (y_plot_list ))[:len (y_plot_list )]
409+ d1_raw_keys = [c .strip () for c in (pp .d1_Key or '' ).split ('|' )] if pp .d1_Key else []
410+ d1_key_labels = (d1_raw_keys + [None ] * len (y_plot_list ))[:len (y_plot_list )]
411+
412+ # --- Plot Exp curves ---
399413 for i , (x_i , y_i ) in enumerate (zip (x_plot_list , y_plot_list )):
400414 f = plot_to_fig (
401415 x_data = y_i if flip_axis else x_i ,
402416 y_data = x_i if flip_axis else y_i ,
403417 figure_handle = None if (first_plot and i == 0 ) else f ,
404- data_label = key_labels [i ],
418+ data_label = d1_key_labels [i ],
405419 x_label = pp .Dep_Title if flip_axis else pp .Ind_Title ,
406420 y_label = pp .Ind_Title if flip_axis else pp .Dep_Title ,
407- marker_style = styles [i ],
421+ marker_style = d1_styles [i ],
408422 x_min = pp .Min_Dep if flip_axis else pp .Min_Ind ,
409423 x_max = pp .Max_Dep if flip_axis else pp .Max_Ind ,
410424 y_min = pp .Min_Ind if flip_axis else pp .Min_Dep ,
@@ -420,7 +434,8 @@ def read_csv_cached(path, **kwargs):
420434 qty_meas_list = []
421435 if y .ndim == 2 and x .ndim == 2 and y .shape [1 ] == x .shape [1 ]:
422436 for j in range (y .shape [1 ]):
423- xj = np .ravel (x [:, j ]); yj = np .ravel (y [:, j ])
437+ xj = np .ravel (x [:, j ])
438+ yj = np .ravel (y [:, j ])
424439 mask = np .isfinite (xj ) & np .isfinite (yj )
425440 xj , yj = xj [mask ], yj [mask ]
426441 if len (xj ) > 0 and len (yj ) > 0 :
@@ -451,7 +466,8 @@ def read_csv_cached(path, **kwargs):
451466 Save_Measured_Quantity [- 1 ] = np .array (qty_meas_list , dtype = object )
452467 except Exception as e :
453468 print (f"[dataplot] Error computing measured metric for { pp .Dataname } : { e } " )
454- Save_Measured_Metric [- 1 ] = np .array ([]); Save_Measured_Quantity [- 1 ] = []
469+ Save_Measured_Metric [- 1 ] = np .array ([])
470+ Save_Measured_Quantity [- 1 ] = []
455471
456472 # ---------------------- LOAD MODEL ----------------------
457473 M = read_csv_cached (cmpdir + pp .d2_Filename ,
@@ -461,18 +477,24 @@ def read_csv_cached(path, **kwargs):
461477 M = M .loc [:M .dropna (how = 'all' ).last_valid_index ()]
462478 M .columns = M .columns .str .strip ()
463479 start_idx = int (pp .d2_Data_Row - pp .d2_Col_Name_Row - 1 )
480+
464481 version_string = revision
465482 if pp .VerStr_Filename :
466483 try :
467484 with open (cmpdir + pp .VerStr_Filename , "r" ) as fver :
468485 Lines = fver .readlines ()
469- if Lines : version_string = Lines [0 ].strip ()
486+ if Lines :
487+ version_string = Lines [0 ].strip ()
470488 except Exception as e :
471489 print (f"[dataplot] Warning: could not read version string: { e } " )
490+
472491 x , _ = get_data (M , pp .d2_Ind_Col_Name , start_idx )
473492 y , _ = get_data (M , pp .d2_Dep_Col_Name , start_idx )
474- x_scale = float (pp .Scale_Ind or 1.0 ); y_scale = float (pp .Scale_Dep or 1.0 )
475- x_scaled = np .asarray (x ) / x_scale ; y_scaled = np .asarray (y ) / y_scale
493+
494+ x_scale = float (pp .Scale_Ind or 1.0 )
495+ y_scale = float (pp .Scale_Dep or 1.0 )
496+ x_scaled = np .asarray (x ) / x_scale
497+ y_scaled = np .asarray (y ) / y_scale
476498
477499 if x_scaled .ndim == 2 and y_scaled .ndim == 2 and x_scaled .shape [1 ] == y_scaled .shape [1 ]:
478500 x_plot_list = [x_scaled [:, i ] for i in range (x_scaled .shape [1 ])]
@@ -481,19 +503,46 @@ def read_csv_cached(path, **kwargs):
481503 x_plot_list = [x_scaled for _ in range (y_scaled .shape [1 ])]
482504 y_plot_list = [y_scaled [:, i ] for i in range (y_scaled .shape [1 ])]
483505 else :
484- x_plot_list = [np .ravel (x_scaled )]; y_plot_list = [np .ravel (y_scaled )]
506+ x_plot_list = [np .ravel (x_scaled )]
507+ y_plot_list = [np .ravel (y_scaled )]
485508
486509 for xi , yi in zip (x_plot_list , y_plot_list ):
487510 if len (xi ) != len (yi ):
488511 print (f"[dataplot] Pair length mismatch in { pp .Dataname } : x={ len (xi )} , y={ len (yi )} " )
489512
513+ # --- Styles and keys (MODEL d2) ---
514+ d2_raw_styles = [c .strip () for c in (pp .d2_Style or '' ).split ('|' )] if pp .d2_Style else []
515+ d2_styles = (d2_raw_styles + [None ] * len (y_plot_list ))[:len (y_plot_list )]
516+ d2_raw_keys = [c .strip () for c in (pp .d2_Key or '' ).split ('|' )] if pp .d2_Key else []
517+ d2_key_labels = (d2_raw_keys + [None ] * len (y_plot_list ))[:len (y_plot_list )]
518+
519+ # --- Plot model curves ---
520+ for i , (x_i , y_i ) in enumerate (zip (x_plot_list , y_plot_list )):
521+ f = plot_to_fig (
522+ x_data = y_i if flip_axis else x_i ,
523+ y_data = x_i if flip_axis else y_i ,
524+ revision_label = version_string ,
525+ figure_handle = f , # keep same figure
526+ data_label = d2_key_labels [i ],
527+ line_style = d2_styles [i ],
528+ x_label = pp .Dep_Title if flip_axis else pp .Ind_Title ,
529+ y_label = pp .Ind_Title if flip_axis else pp .Dep_Title ,
530+ x_min = pp .Min_Dep if flip_axis else pp .Min_Ind ,
531+ x_max = pp .Max_Dep if flip_axis else pp .Max_Ind ,
532+ y_min = pp .Min_Ind if flip_axis else pp .Min_Dep ,
533+ y_max = pp .Max_Ind if flip_axis else pp .Max_Dep ,
534+ legend_location = matlab_legend_to_matplotlib (pp .Key_Position ),
535+ plot_type = plot_type ,
536+ plot_title = pp .Plot_Title ,
537+ )
538+
490539 # --- Interpolated, metric-aware model logic ---
491540 if not gtest :
492541 try :
493542 metric_str = str (pp .Metric or '' ).strip ().lower ()
494543 meas_list , pred_list , qty_pred_list = [], [], []
495544
496- # --- Load experimental data for alignment ---
545+ # Load experimental again for alignment (safe; cached)
497546 E = read_csv_cached (expdir + pp .d1_Filename ,
498547 header = int (pp .d1_Col_Name_Row - 1 ),
499548 sep = ',' , engine = 'python' , quotechar = '"' ,
@@ -503,7 +552,7 @@ def read_csv_cached(path, **kwargs):
503552 x_exp , _ = get_data (E , pp .d1_Ind_Col_Name , start_idx_exp )
504553 y_exp , _ = get_data (E , pp .d1_Dep_Col_Name , start_idx_exp )
505554
506- # Normalize shapes
555+ # Normalize shapes to 2D (col-major semantics)
507556 x_exp = np .atleast_2d (x_exp )
508557 y_exp = np .atleast_2d (y_exp )
509558 x_mod = np .atleast_2d (x )
@@ -512,6 +561,7 @@ def read_csv_cached(path, **kwargs):
512561 ncols = min (y_exp .shape [1 ], y_mod .shape [1 ])
513562
514563 for j in range (ncols ):
564+ # Cull NaNs per series keeping pairs aligned
515565 xj_e = np .ravel (x_exp [:, j ] if x_exp .shape [1 ] > 1 else x_exp )
516566 yj_e = np .ravel (y_exp [:, j ])
517567 m_e = np .isfinite (xj_e ) & np .isfinite (yj_e )
@@ -523,7 +573,7 @@ def read_csv_cached(path, **kwargs):
523573 xj_m , yj_m = xj_m [m_m ], yj_m [m_m ]
524574
525575 if metric_str == 'all' :
526- # interpolate model→ exp grid
576+ # align by interpolating model to exp x
527577 if xj_m .size < 2 or xj_e .size == 0 :
528578 continue
529579 yj_m_i = np .interp (xj_e , xj_m , yj_m , left = np .nan , right = np .nan )
@@ -533,38 +583,52 @@ def read_csv_cached(path, **kwargs):
533583 x_use = xj_e [mask_pair ]
534584 y_exp_use = yj_e [mask_pair ]
535585 y_mod_use = yj_m_i [mask_pair ]
586+ # compute both on the same x grid
587+ v_meas , _ , _ = _compute_metrics_block (
588+ x = x_use , Y = y_exp_use , metric = metric_str ,
589+ initial_value = float (pp .d1_Initial_Value or 0.0 ),
590+ comp_start = float (pp .d1_Comp_Start or np .nan ),
591+ comp_end = float (pp .d1_Comp_End or np .nan ),
592+ dep_comp_start = float (pp .d1_Dep_Comp_Start or np .nan ),
593+ dep_comp_end = float (pp .d1_Dep_Comp_End or np .nan ),
594+ variant_side = "d1" ,
595+ )
596+ v_pred , qty_pred , _ = _compute_metrics_block (
597+ x = x_use , Y = y_mod_use , metric = metric_str ,
598+ initial_value = float (pp .d2_Initial_Value or 0.0 ),
599+ comp_start = float (pp .d2_Comp_Start or np .nan ),
600+ comp_end = float (pp .d2_Comp_End or np .nan ),
601+ dep_comp_start = float (pp .d2_Dep_Comp_Start or np .nan ),
602+ dep_comp_end = float (pp .d2_Dep_Comp_End or np .nan ),
603+ variant_side = "d2" ,
604+ )
536605 else :
537- # no interpolation; operate independently
538- x_use , y_exp_use , y_mod_use = xj_e , yj_e , yj_m
539-
540- if y_exp_use .size == 0 or y_mod_use .size == 0 :
541- continue
542-
543- v_meas , _ , _ = _compute_metrics_block (
544- x = x_use , Y = y_exp_use , metric = metric_str ,
545- initial_value = float (pp .d1_Initial_Value or 0.0 ),
546- comp_start = float (pp .d1_Comp_Start or np .nan ),
547- comp_end = float (pp .d1_Comp_End or np .nan ),
548- dep_comp_start = float (pp .d1_Dep_Comp_Start or np .nan ),
549- dep_comp_end = float (pp .d1_Dep_Comp_End or np .nan ),
550- variant_side = "d1" ,
551- )
552- v_pred , qty_pred , _ = _compute_metrics_block (
553- x = x_use if metric_str == 'all' else xj_m ,
554- Y = y_mod_use , metric = metric_str ,
555- initial_value = float (pp .d2_Initial_Value or 0.0 ),
556- comp_start = float (pp .d2_Comp_Start or np .nan ),
557- comp_end = float (pp .d2_Comp_End or np .nan ),
558- dep_comp_start = float (pp .d2_Dep_Comp_Start or np .nan ),
559- dep_comp_end = float (pp .d2_Dep_Comp_End or np .nan ),
560- variant_side = "d2" ,
561- )
606+ # aggregate metrics: NO interpolation; operate independently
607+ if yj_e .size == 0 or yj_m .size == 0 :
608+ continue
609+ v_meas , _ , _ = _compute_metrics_block (
610+ x = xj_e , Y = yj_e , metric = metric_str ,
611+ initial_value = float (pp .d1_Initial_Value or 0.0 ),
612+ comp_start = float (pp .d1_Comp_Start or np .nan ),
613+ comp_end = float (pp .d1_Comp_End or np .nan ),
614+ dep_comp_start = float (pp .d1_Dep_Comp_Start or np .nan ),
615+ dep_comp_end = float (pp .d1_Dep_Comp_End or np .nan ),
616+ variant_side = "d1" ,
617+ )
618+ v_pred , qty_pred , _ = _compute_metrics_block (
619+ x = xj_m , Y = yj_m , metric = metric_str ,
620+ initial_value = float (pp .d2_Initial_Value or 0.0 ),
621+ comp_start = float (pp .d2_Comp_Start or np .nan ),
622+ comp_end = float (pp .d2_Comp_End or np .nan ),
623+ dep_comp_start = float (pp .d2_Dep_Comp_Start or np .nan ),
624+ dep_comp_end = float (pp .d2_Dep_Comp_End or np .nan ),
625+ variant_side = "d2" ,
626+ )
562627
563628 meas_list .append (np .atleast_1d (v_meas ))
564629 pred_list .append (np .atleast_1d (v_pred ))
565630 qty_pred_list .append (qty_pred )
566631
567- # flatten appropriately
568632 flat_meas = np .concatenate (meas_list ) if meas_list else np .array ([])
569633 flat_pred = np .concatenate (pred_list ) if pred_list else np .array ([])
570634
0 commit comments