lehuaaa commited on
Commit
5a89823
·
verified ·
1 Parent(s): 9e335dc

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +155 -187
src/streamlit_app.py CHANGED
@@ -31,35 +31,22 @@ try:
31
  except ImportError:
32
  TENSORFLOW_AVAILABLE = False
33
 
34
- # STABLE CSS with responsive feedback - No trembling but shows progress
35
  st.markdown("""
36
  <style>
37
- /* FORCE HEADER TO BE COMPLETELY FIXED AND STABLE */
38
  .main-header {
39
- font-size: 2.5rem !important;
40
- color: #1e88e5 !important;
41
- text-align: center !important;
42
- margin-bottom: 2rem !important;
43
- position: fixed !important;
44
  top: 0 !important;
45
- left: 0 !important;
46
- right: 0 !important;
47
- width: 100% !important;
48
  background: white !important;
49
- z-index: 99999 !important;
50
  padding: 1rem 0 !important;
51
- border-bottom: 3px solid #1e88e5 !important;
52
- box-shadow: 0 4px 8px rgba(0,0,0,0.15) !important;
53
- transform: translateZ(0) !important;
54
- will-change: auto !important;
55
  }
56
-
57
- /* ADD TOP MARGIN TO MAIN CONTENT TO AVOID OVERLAP */
58
- .main > .block-container {
59
- margin-top: 120px !important;
60
- padding-top: 20px !important;
61
- }
62
-
63
  .section-header {
64
  font-size: 1.5rem;
65
  color: #424242;
@@ -84,41 +71,24 @@ st.markdown("""
84
  padding: 1rem;
85
  margin: 1rem 0;
86
  }
87
-
88
- /* DISABLE PROBLEMATIC ANIMATIONS - Allow safe text updates */
89
- .stProgress, .stProgress > div, .stProgress * {
90
  transition: none !important;
91
  animation: none !important;
92
- transform: none !important;
93
  }
94
-
95
- .stSpinner, .stSpinner > div, .stSpinner * {
96
  transition: none !important;
97
- animation: none !important;
98
- transform: none !important;
99
- position: relative !important;
100
  }
101
-
102
- /* STABILIZE CONTAINERS - Allow content updates but prevent header shifts */
103
- .element-container, .stButton {
104
  transition: none !important;
105
- animation: none !important;
106
- transform: none !important;
107
  }
108
-
109
- /* FORCE GPU ACCELERATION FOR HEADER STABILITY */
110
- .main-header {
111
- transform: translate3d(0,0,0) !important;
112
- backface-visibility: hidden !important;
113
- perspective: 1000px !important;
114
  }
115
-
116
- /* HIDE STREAMLIT'S RERUN INDICATOR */
117
- .stAppViewMain > .main > .block-container > div:first-child {
118
- visibility: hidden !important;
119
- height: 0 !important;
120
- margin: 0 !important;
121
- padding: 0 !important;
122
  }
123
  </style>
124
  """, unsafe_allow_html=True)
@@ -132,10 +102,16 @@ if 'model_loaded' not in st.session_state:
132
  st.session_state.model_loaded = False
133
 
134
 
135
- # ULTRA-OPTIMIZED BubbleSimulation class - ZERO UI UPDATES during simulation
136
  class OptimizedBubbleSimulation:
137
  """
138
- ULTRA-OPTIMIZED version - ZERO UI updates during simulation to prevent any trembling
 
 
 
 
 
 
139
  """
140
 
141
  def __init__(self):
@@ -169,8 +145,10 @@ class OptimizedBubbleSimulation:
169
  self.NT = 100 # Reduced from 500 to 100 (5x faster, still accurate)
170
  self.RelTol = 1e-5 # Relaxed from 1e-7 to 1e-5 (faster convergence)
171
 
172
- def run_optimized_simulation(self, G, mu, lambda_max_mean=None, status_callback=None):
173
- """OPTIMIZED simulation with minimal but responsive UI updates"""
 
 
174
  from scipy.integrate import solve_ivp
175
 
176
  # Set lambdamax from loaded data or use default
@@ -199,7 +177,9 @@ class OptimizedBubbleSimulation:
199
  self.Rc = self.Rmax
200
  self.Uc = np.sqrt(self.P_inf / self.rho)
201
  self.tc = self.Rmax / self.Uc
202
- self.tspan = 3 * self.tc # Reduced for speed
 
 
203
 
204
  # Calculate parameters (same as original)
205
  self.Pv = self.P_ref * np.exp(-self.T_ref / self.T_inf)
@@ -245,58 +225,55 @@ class OptimizedBubbleSimulation:
245
 
246
  X0 = np.concatenate([[R0_star, U0_star, P0_star, S0], Theta0, k0])
247
 
248
- print(f"State vector size: {len(X0)} (4 + {self.NT} + {self.NT})")
249
- print(f"Time span: 0 to {self.tspan_star:.4f}")
250
 
251
- # SAFE STATUS UPDATE: Use callback to update UI safely
252
- if status_callback:
253
- status_callback("🔄 Initializing bubble physics solver...")
254
 
255
- # OPTIMIZED ODE solving with minimal updates
256
  try:
257
  sol = solve_ivp(
258
- self.bubble_optimized,
259
  [0, self.tspan_star],
260
  X0,
261
  method='BDF',
262
- rtol=self.RelTol,
263
- atol=1e-8,
264
- max_step=self.tspan_star / 200,
265
- dense_output=False
266
  )
267
 
268
- if status_callback:
269
- status_callback("⚙️ Processing simulation results...")
270
 
271
  except Exception as e:
272
  print(f"BDF failed: {str(e)}, trying LSODA...")
273
- if status_callback:
274
- status_callback("🔄 Trying backup solver (LSODA)...")
275
  try:
276
  sol = solve_ivp(
277
  self.bubble_optimized,
278
  [0, self.tspan_star],
279
  X0,
280
  method='LSODA',
281
- rtol=1e-4,
282
  atol=1e-7,
283
- max_step=self.tspan_star / 100,
284
  )
285
  except Exception as e2:
286
  print(f"All solvers failed: {str(e2)}")
287
- if status_callback:
288
- status_callback("⚠️ Using analytical fallback...")
289
  return self.fast_fallback()
290
 
291
  if not sol.success:
292
  print(f"Solver failed: {sol.message}")
293
- if status_callback:
294
- status_callback("⚠️ Solver failed, using fallback...")
295
  return self.fast_fallback()
296
 
297
  # Extract solution
298
  t_nondim = sol.t
299
  X_nondim = sol.y.T
 
300
  R_nondim = X_nondim[:, 0]
301
 
302
  # Filter valid solutions
@@ -306,8 +283,7 @@ class OptimizedBubbleSimulation:
306
 
307
  if len(t_nondim) < 10:
308
  print("Too few valid points, using fast fallback")
309
- if status_callback:
310
- status_callback("⚠️ Using analytical approximation...")
311
  return self.fast_fallback()
312
 
313
  # Back to physical units
@@ -319,10 +295,12 @@ class OptimizedBubbleSimulation:
319
  t_newunit = t * scale
320
  R_newunit = R * scale
321
 
322
- if status_callback:
323
- status_callback("✅ Simulation completed successfully!")
 
 
324
 
325
- print(f"OPTIMIZED simulation completed in {len(t_newunit)} points!")
326
  print(f"Time range: {t_newunit[0]:.3f} to {t_newunit[-1]:.3f} (0.1 ms)")
327
  print(f"Radius range: {np.min(R_newunit):.3f} to {np.max(R_newunit):.3f} (0.1 mm)")
328
 
@@ -330,7 +308,8 @@ class OptimizedBubbleSimulation:
330
 
331
  def bubble_optimized(self, t, x):
332
  """
333
- OPTIMIZED bubble physics function - same physics, no UI updates
 
334
  """
335
  # Extract parameters (same as original)
336
  NT = int(self.params[0])
@@ -516,7 +495,7 @@ class OptimizedBubbleSimulation:
516
 
517
  # Main Streamlit App
518
  def main():
519
- # Header - ULTRA-STABLE with fixed positioning
520
  st.markdown('<h1 class="main-header">🫧 Bubble Dynamics Transformer</h1>', unsafe_allow_html=True)
521
 
522
  # Initialize current page in session state
@@ -1392,7 +1371,7 @@ Ready for validation simulation!"""
1392
 
1393
 
1394
  def show_validation():
1395
- """STABLE Validation interface with responsive feedback"""
1396
  st.markdown('<h2 class="section-header">✅ Validation</h2>', unsafe_allow_html=True)
1397
 
1398
  if not st.session_state.processed_data:
@@ -1427,101 +1406,90 @@ def show_validation():
1427
  if 'lambda_max_mean' in st.session_state:
1428
  st.write(f"**λ_max:** {st.session_state.lambda_max_mean:.3f}")
1429
 
1430
- # STABLE: Single button with immediate response
1431
- if st.button("🚀 Run Validation Simulation", type="primary", key="validation_button"):
1432
- # Check required data (exactly like desktop GUI)
1433
- if st.session_state.lambda_max_mean is None:
1434
- st.error("No lambda_max_mean loaded. Please load data first.")
1435
- return
1436
-
1437
- # Create status display for user feedback
1438
- status_placeholder = st.empty()
1439
- results_container = st.container()
1440
-
1441
- # Define status update callback
1442
- def update_status(message):
1443
- status_placeholder.info(f"**Status:** {message}")
1444
-
1445
- # Initial status
1446
- update_status("🔄 Starting validation simulation...")
1447
-
1448
- try:
1449
- # Extract values exactly like desktop GUI
1450
- G_value = st.session_state.pred_G[0][0] if st.session_state.pred_G.ndim > 1 else \
1451
- st.session_state.pred_G[0]
1452
- mu_value = st.session_state.pred_mu[0][0] if st.session_state.pred_mu.ndim > 1 else \
1453
- st.session_state.pred_mu[0]
1454
-
1455
- # Initialize simulation
1456
- bubble_sim = OptimizedBubbleSimulation()
1457
-
1458
- # Run simulation with status callback
1459
- start_time = time.time()
1460
- t_sim, R_sim = bubble_sim.run_optimized_simulation(
1461
- G_value, mu_value, st.session_state.lambda_max_mean,
1462
- status_callback=update_status
1463
- )
1464
- simulation_time = time.time() - start_time
1465
 
1466
- # Clear status and show results
1467
- status_placeholder.empty()
1468
-
1469
- # Store simulation results
1470
- st.session_state.t_sim = t_sim
1471
- st.session_state.R_sim = R_sim
1472
-
1473
- with results_container:
1474
- # Create comparison plot (exactly like desktop GUI)
1475
- fig, ax = plt.subplots(figsize=(10, 6))
1476
-
1477
- ax.plot(st.session_state.t_interp_newunit, st.session_state.R_interp_newunit,
1478
- 'ro', markersize=4, label='Interpolated (Experimental)', alpha=0.7)
1479
- ax.plot(t_sim, R_sim, 'b-', linewidth=2, label='Simulated (Predicted G & μ)')
1480
-
1481
- ax.set_xlabel('Time (0.1 ms)')
1482
- ax.set_ylabel('Radius (0.1 mm)')
1483
- ax.set_title('Validation: Experimental vs Simulated R-t Curves (Optimized)')
1484
- ax.grid(True, alpha=0.3)
1485
- ax.legend()
1486
-
1487
- # Calculate error metrics (exactly like desktop GUI)
1488
- if len(t_sim) > 0 and len(st.session_state.t_interp_newunit) > 0:
1489
- t_min = max(st.session_state.t_interp_newunit[0], t_sim[0])
1490
- t_max = min(st.session_state.t_interp_newunit[-1], t_sim[-1])
1491
-
1492
- if t_max > t_min:
1493
- f_sim = interp1d(t_sim, R_sim, kind='linear', bounds_error=False, fill_value='extrapolate')
1494
-
1495
- mask = (st.session_state.t_interp_newunit >= t_min) & (
1496
- st.session_state.t_interp_newunit <= t_max)
1497
- t_common = st.session_state.t_interp_newunit[mask]
1498
- R_exp_common = st.session_state.R_interp_newunit[mask]
1499
- R_sim_common = f_sim(t_common)
1500
-
1501
- if len(R_exp_common) > 0:
1502
- rmse = np.sqrt(np.mean((R_exp_common - R_sim_common) ** 2))
1503
- mae = np.mean(np.abs(R_exp_common - R_sim_common))
1504
- max_error = np.max(np.abs(R_exp_common - R_sim_common))
1505
-
1506
- error_text = f'RMSE: {rmse:.3f}\nMAE: {mae:.3f}\nMax Error: {max_error:.3f}'
1507
- ax.text(0.02, 0.98, error_text, transform=ax.transAxes,
1508
- verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
1509
-
1510
- plt.tight_layout()
1511
- st.pyplot(fig)
1512
 
1513
- # Display validation metrics
1514
- if 'rmse' in locals():
1515
- col1, col2, col3 = st.columns(3)
1516
- with col1:
1517
- st.metric("RMSE", f"{rmse:.3f}")
1518
- with col2:
1519
- st.metric("MAE", f"{mae:.3f}")
1520
- with col3:
1521
- st.metric("Max Error", f"{max_error:.3f}")
1522
 
1523
- # Show detailed results (matching desktop GUI message)
1524
- validation_results = f"""**Validation Results (Optimized):**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1525
 
1526
  **Predicted Values:**
1527
  - Shear Modulus (G): {G_value:.2e} Pa
@@ -1529,25 +1497,25 @@ def show_validation():
1529
  - Lambda Max: {st.session_state.lambda_max_mean:.3f} (from .mat file)
1530
 
1531
  **Simulation Performance:**
1532
- - Simulation Time: {simulation_time:.2f} seconds (optimized!)
1533
  - Simulated Points: {len(R_sim)}
1534
  - Time Range: {t_sim[0]:.3f} to {t_sim[-1]:.3f} (0.1 ms)
1535
 
1536
  The plot shows comparison between experimental (dots) and
1537
- simulated (line) R-t curves using predicted material properties."""
 
1538
 
1539
- st.success("✅ Validation simulation completed!")
1540
- st.info(validation_results)
1541
 
1542
- except Exception as e:
1543
- status_placeholder.empty()
1544
- st.error(f"Simulation failed: {str(e)}")
1545
 
1546
- with st.expander("🔍 Debug Information"):
1547
- st.write(f"**Error:** {str(e)}")
1548
- st.write(f"**G value:** {G_value if 'G_value' in locals() else 'N/A'}")
1549
- st.write(f"**μ value:** {mu_value if 'mu_value' in locals() else 'N/A'}")
1550
- st.write(f"**Lambda:** {st.session_state.get('lambda_max_mean', 'N/A')}")
1551
 
1552
 
1553
  def show_results():
 
31
  except ImportError:
32
  TENSORFLOW_AVAILABLE = False
33
 
34
+ # FIXED CSS for stable header and no trembling
35
  st.markdown("""
36
  <style>
 
37
  .main-header {
38
+ font-size: 2.5rem;
39
+ color: #1e88e5;
40
+ text-align: center;
41
+ margin-bottom: 2rem;
42
+ position: sticky !important;
43
  top: 0 !important;
 
 
 
44
  background: white !important;
45
+ z-index: 9999 !important;
46
  padding: 1rem 0 !important;
47
+ border-bottom: 2px solid #1e88e5 !important;
48
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
 
 
49
  }
 
 
 
 
 
 
 
50
  .section-header {
51
  font-size: 1.5rem;
52
  color: #424242;
 
71
  padding: 1rem;
72
  margin: 1rem 0;
73
  }
74
+ /* Prevent all animation/transition effects that cause trembling */
75
+ * {
 
76
  transition: none !important;
77
  animation: none !important;
 
78
  }
79
+ /* Stabilize progress elements */
80
+ .stProgress > div {
81
  transition: none !important;
 
 
 
82
  }
83
+ .stSpinner > div {
 
 
84
  transition: none !important;
 
 
85
  }
86
+ /* Prevent layout shifts */
87
+ .stProgress {
88
+ transition: none !important;
 
 
 
89
  }
90
+ .element-container {
91
+ transition: none !important;
 
 
 
 
 
92
  }
93
  </style>
94
  """, unsafe_allow_html=True)
 
102
  st.session_state.model_loaded = False
103
 
104
 
105
+ # Complete OptimizedBubbleSimulation class - FIXED with stable UI updates
106
  class OptimizedBubbleSimulation:
107
  """
108
+ OPTIMIZED version of BubbleSimulation class - matches GUI implementation
109
+ Key optimizations while preserving physics:
110
+ 1. Reduced mesh resolution (NT = 100 instead of 500)
111
+ 2. Shorter simulation time for validation
112
+ 3. Optimized ODE solver settings
113
+ 4. Simplified some less critical calculations
114
+ 5. FIXED: Minimal UI updates to prevent header trembling
115
  """
116
 
117
  def __init__(self):
 
145
  self.NT = 100 # Reduced from 500 to 100 (5x faster, still accurate)
146
  self.RelTol = 1e-5 # Relaxed from 1e-7 to 1e-5 (faster convergence)
147
 
148
+ # Note: lambdamax will be set dynamically from .mat file in run_optimized_simulation
149
+
150
+ def run_optimized_simulation(self, G, mu, lambda_max_mean=None):
151
+ """Run optimized simulation - FIXED with minimal UI updates"""
152
  from scipy.integrate import solve_ivp
153
 
154
  # Set lambdamax from loaded data or use default
 
177
  self.Rc = self.Rmax
178
  self.Uc = np.sqrt(self.P_inf / self.rho)
179
  self.tc = self.Rmax / self.Uc
180
+
181
+ # OPTIMIZATION: Shorter simulation time for validation (3x instead of 6x)
182
+ self.tspan = 3 * self.tc # Reduced from 6*tc to 3*tc (2x faster)
183
 
184
  # Calculate parameters (same as original)
185
  self.Pv = self.P_ref * np.exp(-self.T_ref / self.T_inf)
 
225
 
226
  X0 = np.concatenate([[R0_star, U0_star, P0_star, S0], Theta0, k0])
227
 
228
+ print(f"Optimized state vector size: {len(X0)} (4 + {self.NT} + {self.NT})")
229
+ print(f"Time span: 0 to {self.tspan_star:.4f} (reduced for speed)")
230
 
231
+ # FIXED: Use single status placeholder instead of progress bar to prevent trembling
232
+ status_placeholder = st.empty()
233
+ status_placeholder.text("🔄 Starting simulation...")
234
 
235
+ # OPTIMIZED ODE solving with relaxed tolerances and larger time steps
236
  try:
237
  sol = solve_ivp(
238
+ self.bubble_optimized, # Optimized bubble physics
239
  [0, self.tspan_star],
240
  X0,
241
  method='BDF',
242
+ rtol=self.RelTol, # Relaxed tolerance for speed
243
+ atol=1e-8, # Relaxed tolerance
244
+ max_step=self.tspan_star / 200, # Larger steps (was 500, now 200) for speed
245
+ dense_output=False # Disable dense output for speed
246
  )
247
 
248
+ status_placeholder.text("⚙️ Processing results...")
 
249
 
250
  except Exception as e:
251
  print(f"BDF failed: {str(e)}, trying LSODA...")
252
+ status_placeholder.text("🔄 Trying backup solver...")
 
253
  try:
254
  sol = solve_ivp(
255
  self.bubble_optimized,
256
  [0, self.tspan_star],
257
  X0,
258
  method='LSODA',
259
+ rtol=1e-4, # Further relaxed for speed
260
  atol=1e-7,
261
+ max_step=self.tspan_star / 100, # Even larger steps for speed
262
  )
263
  except Exception as e2:
264
  print(f"All solvers failed: {str(e2)}")
265
+ status_placeholder.empty()
 
266
  return self.fast_fallback()
267
 
268
  if not sol.success:
269
  print(f"Solver failed: {sol.message}")
270
+ status_placeholder.empty()
 
271
  return self.fast_fallback()
272
 
273
  # Extract solution
274
  t_nondim = sol.t
275
  X_nondim = sol.y.T
276
+
277
  R_nondim = X_nondim[:, 0]
278
 
279
  # Filter valid solutions
 
283
 
284
  if len(t_nondim) < 10:
285
  print("Too few valid points, using fast fallback")
286
+ status_placeholder.empty()
 
287
  return self.fast_fallback()
288
 
289
  # Back to physical units
 
295
  t_newunit = t * scale
296
  R_newunit = R * scale
297
 
298
+ # FIXED: Minimal final update and quick cleanup
299
+ status_placeholder.text("✅ Simulation complete!")
300
+ time.sleep(0.1) # Reduced from 0.5 to 0.1 for faster UX
301
+ status_placeholder.empty()
302
 
303
+ print(f"Optimized simulation completed in {len(t_newunit)} points!")
304
  print(f"Time range: {t_newunit[0]:.3f} to {t_newunit[-1]:.3f} (0.1 ms)")
305
  print(f"Radius range: {np.min(R_newunit):.3f} to {np.max(R_newunit):.3f} (0.1 mm)")
306
 
 
308
 
309
  def bubble_optimized(self, t, x):
310
  """
311
+ OPTIMIZED version of bubble physics function - COMPLETE IMPLEMENTATION matching GUI
312
+ Same physics but with computational optimizations
313
  """
314
  # Extract parameters (same as original)
315
  NT = int(self.params[0])
 
495
 
496
  # Main Streamlit App
497
  def main():
498
+ # Header - FIXED with stable CSS
499
  st.markdown('<h1 class="main-header">🫧 Bubble Dynamics Transformer</h1>', unsafe_allow_html=True)
500
 
501
  # Initialize current page in session state
 
1371
 
1372
 
1373
  def show_validation():
1374
+ """FIXED Validation interface with stable containers"""
1375
  st.markdown('<h2 class="section-header">✅ Validation</h2>', unsafe_allow_html=True)
1376
 
1377
  if not st.session_state.processed_data:
 
1406
  if 'lambda_max_mean' in st.session_state:
1407
  st.write(f"**λ_max:** {st.session_state.lambda_max_mean:.3f}")
1408
 
1409
+ # FIXED: Use stable containers to prevent layout shifts and header trembling
1410
+ button_container = st.container()
1411
+ results_container = st.empty()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1412
 
1413
+ with button_container:
1414
+ if st.button("🚀 Run Validation Simulation", type="primary"):
1415
+ # Check required data (exactly like desktop GUI)
1416
+ if st.session_state.lambda_max_mean is None:
1417
+ st.error("No lambda_max_mean loaded. Please load data first.")
1418
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1419
 
1420
+ # Use the results container for all dynamic content to prevent header movement
1421
+ with results_container.container():
1422
+ with st.spinner("Running optimized bubble simulation..."):
1423
+ try:
1424
+ # Extract values exactly like desktop GUI
1425
+ G_value = st.session_state.pred_G[0][0] if st.session_state.pred_G.ndim > 1 else \
1426
+ st.session_state.pred_G[0]
1427
+ mu_value = st.session_state.pred_mu[0][0] if st.session_state.pred_mu.ndim > 1 else \
1428
+ st.session_state.pred_mu[0]
1429
 
1430
+ # Initialize simulation
1431
+ bubble_sim = OptimizedBubbleSimulation()
1432
+
1433
+ # Run simulation (exactly like desktop GUI)
1434
+ start_time = time.time()
1435
+ t_sim, R_sim = bubble_sim.run_optimized_simulation(G_value, mu_value, st.session_state.lambda_max_mean)
1436
+ simulation_time = time.time() - start_time
1437
+
1438
+ # Store simulation results
1439
+ st.session_state.t_sim = t_sim
1440
+ st.session_state.R_sim = R_sim
1441
+
1442
+ # Create comparison plot (exactly like desktop GUI)
1443
+ fig, ax = plt.subplots(figsize=(10, 6))
1444
+
1445
+ ax.plot(st.session_state.t_interp_newunit, st.session_state.R_interp_newunit,
1446
+ 'ro', markersize=4, label='Interpolated (Experimental)', alpha=0.7)
1447
+ ax.plot(t_sim, R_sim, 'b-', linewidth=2, label='Simulated (Predicted G & μ)')
1448
+
1449
+ ax.set_xlabel('Time (0.1 ms)')
1450
+ ax.set_ylabel('Radius (0.1 mm)')
1451
+ ax.set_title('Validation: Experimental vs Simulated R-t Curves (Optimized)')
1452
+ ax.grid(True, alpha=0.3)
1453
+ ax.legend()
1454
+
1455
+ # Calculate error metrics (exactly like desktop GUI)
1456
+ if len(t_sim) > 0 and len(st.session_state.t_interp_newunit) > 0:
1457
+ t_min = max(st.session_state.t_interp_newunit[0], t_sim[0])
1458
+ t_max = min(st.session_state.t_interp_newunit[-1], t_sim[-1])
1459
+
1460
+ if t_max > t_min:
1461
+ f_sim = interp1d(t_sim, R_sim, kind='linear', bounds_error=False, fill_value='extrapolate')
1462
+
1463
+ mask = (st.session_state.t_interp_newunit >= t_min) & (
1464
+ st.session_state.t_interp_newunit <= t_max)
1465
+ t_common = st.session_state.t_interp_newunit[mask]
1466
+ R_exp_common = st.session_state.R_interp_newunit[mask]
1467
+ R_sim_common = f_sim(t_common)
1468
+
1469
+ if len(R_exp_common) > 0:
1470
+ rmse = np.sqrt(np.mean((R_exp_common - R_sim_common) ** 2))
1471
+ mae = np.mean(np.abs(R_exp_common - R_sim_common))
1472
+ max_error = np.max(np.abs(R_exp_common - R_sim_common))
1473
+
1474
+ error_text = f'RMSE: {rmse:.3f}\nMAE: {mae:.3f}\nMax Error: {max_error:.3f}'
1475
+ ax.text(0.02, 0.98, error_text, transform=ax.transAxes,
1476
+ verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
1477
+
1478
+ plt.tight_layout()
1479
+ st.pyplot(fig)
1480
+
1481
+ # Display validation metrics
1482
+ if 'rmse' in locals():
1483
+ col1, col2, col3 = st.columns(3)
1484
+ with col1:
1485
+ st.metric("RMSE", f"{rmse:.3f}")
1486
+ with col2:
1487
+ st.metric("MAE", f"{mae:.3f}")
1488
+ with col3:
1489
+ st.metric("Max Error", f"{max_error:.3f}")
1490
+
1491
+ # Show detailed results (matching desktop GUI message)
1492
+ validation_results = f"""**Validation Results (Optimized):**
1493
 
1494
  **Predicted Values:**
1495
  - Shear Modulus (G): {G_value:.2e} Pa
 
1497
  - Lambda Max: {st.session_state.lambda_max_mean:.3f} (from .mat file)
1498
 
1499
  **Simulation Performance:**
1500
+ - Simulation Time: {simulation_time:.2f} seconds (much faster!)
1501
  - Simulated Points: {len(R_sim)}
1502
  - Time Range: {t_sim[0]:.3f} to {t_sim[-1]:.3f} (0.1 ms)
1503
 
1504
  The plot shows comparison between experimental (dots) and
1505
+ simulated (line) R-t curves using predicted material properties.
1506
+ Note: This uses an optimized simulation for faster results."""
1507
 
1508
+ st.success("✅ Validation simulation completed!")
1509
+ st.info(validation_results)
1510
 
1511
+ except Exception as e:
1512
+ st.error(f"Simulation failed: {str(e)}")
 
1513
 
1514
+ with st.expander("🔍 Debug Information"):
1515
+ st.write(f"**Error:** {str(e)}")
1516
+ st.write(f"**G value:** {G_value if 'G_value' in locals() else 'N/A'}")
1517
+ st.write(f"**μ value:** {mu_value if 'mu_value' in locals() else 'N/A'}")
1518
+ st.write(f"**Lambda:** {st.session_state.get('lambda_max_mean', 'N/A')}")
1519
 
1520
 
1521
  def show_results():