Skip to content

Commit d8b316d

Browse files
authored
Merge pull request #15534 from rmcdermo/master
Python: fix model plotting
2 parents 3d70877 + 2310089 commit d8b316d

File tree

1 file changed

+103
-39
lines changed

1 file changed

+103
-39
lines changed

Utilities/Python/fdsplotlib.py

Lines changed: 103 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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 modelexp 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

Comments
 (0)