@@ -1126,6 +1126,21 @@ def plot_to_fig(x_data,y_data,**kwargs):
11261126 if isinstance (data_label , str ) and data_label .lower () == 'blank' :
11271127 data_label = None
11281128
1129+ # ------------------------------------------------------------------
1130+ # PRE-PATCH FOR LOG AXES — PREVENTS log(0) WARNINGS
1131+ # ------------------------------------------------------------------
1132+ xmin = kwargs .get ('x_min' )
1133+ xmax = kwargs .get ('x_max' )
1134+ ymin = kwargs .get ('y_min' )
1135+ ymax = kwargs .get ('y_max' )
1136+
1137+ if plot_type in ('loglog' , 'semilogx' , 'semilogy' ):
1138+ eps = 1e-12
1139+ if xmin is not None and xmin <= 0 : xmin = eps
1140+ if ymin is not None and ymin <= 0 : ymin = eps
1141+ if xmin is not None and xmax is not None : ax .set_xlim (xmin , xmax )
1142+ if ymin is not None and ymax is not None : ax .set_ylim (ymin , ymax )
1143+
11291144 # generate the main x,y plot
11301145 if plot_type == 'linear' :
11311146 ax .plot (x_data ,y_data ,
@@ -1225,13 +1240,11 @@ def plot_to_fig(x_data,y_data,**kwargs):
12251240 capthick = linewidth ,
12261241 )
12271242
1228-
12291243 ticklabel_fontsize = kwargs .get ('ticklabel_fontsize' ,default_ticklabel_fontsize )
12301244 plt .setp ( ax .xaxis .get_majorticklabels (), rotation = 0 , fontsize = ticklabel_fontsize )
12311245 plt .setp ( ax .yaxis .get_majorticklabels (), rotation = 0 , fontsize = ticklabel_fontsize )
12321246
12331247 axeslabel_fontsize = kwargs .get ('axeslabel_fontsize' ,default_axeslabel_fontsize )
1234-
12351248 if not using_existing_figure :
12361249 plt .xlabel (kwargs .get ('x_label' ), fontsize = axeslabel_fontsize )
12371250 plt .ylabel (kwargs .get ('y_label' ), fontsize = axeslabel_fontsize )
@@ -2141,7 +2154,7 @@ def _label_at(k, labels):
21412154
21422155 # --- Call histogram BEFORE filtering to match MATLAB (includes zeros/Infs) ---
21432156 try :
2144- hist_file , pval = statistics_histogram (
2157+ hist_file = statistics_histogram (
21452158 Measured_Values , Predicted_Values ,
21462159 Plot_Filename , Manuals_Dir , Scatter_Plot_Title
21472160 )
@@ -2164,16 +2177,23 @@ def _label_at(k, labels):
21642177 print (f"[scatplot] Skipping { Scatter_Plot_Title } (no valid data)" )
21652178 continue
21662179
2167- # --- Compute statistics ---
2180+ # --- Compute statistics (MATLAB logic restored) ---
21682181 weight = np .ones_like (Measured_Values )
21692182 log_E_bar = np .sum (np .log (Measured_Values ) * weight ) / np .sum (weight )
21702183 log_M_bar = np .sum (np .log (Predicted_Values ) * weight ) / np .sum (weight )
21712184 u2 = np .sum ((((np .log (Predicted_Values ) - np .log (Measured_Values ))
21722185 - (log_M_bar - log_E_bar )) ** 2 ) * weight ) / (np .sum (weight ) - 1 )
21732186 u = np .sqrt (u2 )
2174- Sigma_E = min (u / np .sqrt (2 ), Sigma_E_input / 100.0 )
2175- Sigma_M = np .sqrt (max (0.0 , u ** 2 - Sigma_E ** 2 ))
2176- delta = np .exp (log_M_bar - log_E_bar + 0.5 * Sigma_M ** 2 - 0.5 * Sigma_E ** 2 )
2187+
2188+ # Restore MATLAB logic:
2189+ # If no Sigma_E is supplied, experimental sigma = u/sqrt(2)
2190+ if Sigma_E_input > 0 :
2191+ Sigma_E = Sigma_E_input / 100.0
2192+ else :
2193+ Sigma_E = u / np .sqrt (2 )
2194+
2195+ Sigma_M = np .sqrt (max (0.0 , u ** 2 - Sigma_E ** 2 ))
2196+ delta = np .exp (log_M_bar - log_E_bar + 0.5 * Sigma_M ** 2 - 0.5 * Sigma_E ** 2 )
21772197
21782198 # --- Scatter Plot ---
21792199 fig = fdsplotlib .plot_to_fig (x_data = [- 1 ], y_data = [- 1 ],
@@ -2188,12 +2208,21 @@ def _label_at(k, labels):
21882208 legend_expand = row ["Paper_Width_Factor" ])
21892209
21902210 fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = [Plot_Min , Plot_Max ], line_style = "k-" , figure_handle = fig )
2191- if Sigma_E > 0 :
2192- fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * (1 + 2 * Sigma_E ), line_style = "k--" , figure_handle = fig )
2193- fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) / (1 + 2 * Sigma_E ), line_style = "k--" , figure_handle = fig )
2194- fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * delta , line_style = "r-" , figure_handle = fig )
2195- fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * delta * (1 + 2 * Sigma_M ), line_style = "r--" , figure_handle = fig )
2196- fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * delta / (1 + 2 * Sigma_M ), line_style = "r--" , figure_handle = fig )
2211+ fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * (1 + 2 * Sigma_E ), line_style = "k--" , figure_handle = fig )
2212+ fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) / (1 + 2 * Sigma_E ), line_style = "k--" , figure_handle = fig )
2213+ fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * delta , line_style = "r-" , figure_handle = fig )
2214+ fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * delta * (1 + 2 * Sigma_M ), line_style = "r--" , figure_handle = fig )
2215+ fdsplotlib .plot_to_fig (x_data = [Plot_Min , Plot_Max ], y_data = np .array ([Plot_Min , Plot_Max ]) * delta / (1 + 2 * Sigma_M ), line_style = "r--" , figure_handle = fig )
2216+
2217+ # --- Add statistics annotation ---
2218+ ax = fig .gca ()
2219+ xnorm = 0.03 # 3% from left
2220+ ynorm = 0.97 # 3% from top
2221+ dy = 0.05 # vertical spacing in normalized units
2222+ ax .text (xnorm , ynorm ,f"{ Scatter_Plot_Title } " ,fontsize = plot_style ["Scat_Title_Font_Size" ],ha = "left" , va = "top" ,transform = ax .transAxes )
2223+ ax .text (xnorm , ynorm - dy ,f"Exp. Rel. Std. Dev.: { Sigma_E :.2f} " ,fontsize = plot_style ["Scat_Label_Font_Size" ],ha = "left" , va = "top" ,transform = ax .transAxes )
2224+ ax .text (xnorm , ynorm - 2 * dy ,f"Model Rel. Std. Dev.: { Sigma_M :.2f} " ,fontsize = plot_style ["Scat_Label_Font_Size" ],ha = "left" , va = "top" ,transform = ax .transAxes )
2225+ ax .text (xnorm , ynorm - 3 * dy ,f"Model Bias Factor: { delta :.2f} " ,fontsize = plot_style ["Scat_Label_Font_Size" ],ha = "left" , va = "top" ,transform = ax .transAxes )
21972226
21982227 # --- Plot each dataset with its own marker and color ---
21992228 seen_labels = set ()
@@ -2528,30 +2557,6 @@ def histogram_output(Histogram_Tex_Output, Output_Histograms):
25282557 print (f"[histogram_output] Wrote LaTeX histogram file: { Histogram_Tex_Output } " )
25292558
25302559
2531- def spiegel_test (x ):
2532- """Exact translation of MATLAB spiegel_test.m with Inf/NaN handling identical to MATLAB."""
2533- import numpy as np
2534- from math import sqrt , erf
2535-
2536- x = np .asarray (x , dtype = float )
2537-
2538- # MATLAB arithmetic allows Inf and NaN to propagate but doesn't remove them
2539- xm = np .nanmean (x )
2540- xs = np .nanstd (x , ddof = 0 )
2541- xz = (x - xm ) / xs
2542- xz2 = xz ** 2
2543-
2544- # MATLAB behavior: Inf * 0 -> NaN, which is ignored by sum()
2545- with np .errstate (invalid = 'ignore' , divide = 'ignore' ):
2546- term = xz2 * np .log (xz2 )
2547-
2548- N = np .nansum (term )
2549- n = np .sum (np .isfinite (xz2 )) # count finite entries only
2550- ts = (N - 0.73 * n ) / (0.8969 * sqrt (n ))
2551- pval = 1 - abs (erf (ts / sqrt (2 ))) # 2-sided
2552- return pval
2553-
2554-
25552560def statistics_histogram (Measured_Values , Predicted_Values ,
25562561 Plot_Filename , Manuals_Dir , Scatter_Plot_Title ,
25572562 Figure_Visibility = 'off' , Paper_Width = 5.0 ,
@@ -2566,13 +2571,6 @@ def statistics_histogram(Measured_Values, Predicted_Values,
25662571 with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
25672572 ln_M_E = np .log (Predicted_Values ) - np .log (Measured_Values )
25682573
2569- # Normality test before filtering — this is the key
2570- if len (ln_M_E ) >= 4 :
2571- pval = spiegel_test (ln_M_E )
2572- else :
2573- print (f"[statistics_histogram] Not enough data for { Scatter_Plot_Title } " )
2574- return None , None
2575-
25762574 # MATLAB's hist() ignores NaN/Inf implicitly
25772575 valid = np .isfinite (ln_M_E )
25782576 ln_M_E = ln_M_E [valid ]
@@ -2600,8 +2598,6 @@ def statistics_histogram(Measured_Values, Predicted_Values,
26002598 ax .set_xticks (xcenters )
26012599 ax .set_xticklabels ([str (i ) for i in range (1 , len (xcenters ) + 1 )])
26022600 ax .text (0.03 , 0.90 , Scatter_Plot_Title , transform = ax .transAxes )
2603- ax .text (0.03 , 0.82 , "Normality Test" , transform = ax .transAxes )
2604- ax .text (0.03 , 0.74 , f"p-value = { pval :.2f} " , transform = ax .transAxes )
26052601
26062602 outpath = os .path .join (Manuals_Dir , f"{ Plot_Filename } _Histogram.pdf" )
26072603 os .makedirs (os .path .dirname (outpath ), exist_ok = True )
@@ -2610,8 +2606,7 @@ def statistics_histogram(Measured_Values, Predicted_Values,
26102606 plt .close (fig )
26112607 plt .figure ().clear ()
26122608
2613- print (f"[statistics_histogram] { Scatter_Plot_Title } : p = { pval :.2f} " )
2614- return f"{ os .path .basename (Plot_Filename )} _Histogram" , pval
2609+ return f"{ os .path .basename (Plot_Filename )} _Histogram"
26152610
26162611
26172612def set_ticks_like_matlab (fig ):
0 commit comments