FDM: heatmap.html con due grafici indipendenti, statico e striscia animata
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+95
-25
@@ -27,52 +27,122 @@ def visualize_fdm(T_matrix, x_vals, t_vals):
|
|||||||
os.makedirs(out_dir, exist_ok=True)
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# 1. Heatmap
|
# 1. heatmap.html: plot statico T(x,t) + striscia 1D animata
|
||||||
|
# Due figure Plotly indipendenti nello stesso file HTML per
|
||||||
|
# evitare conflitti tra animazioni e assi multipli.
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
zmax = float(np.max(np.abs(T_matrix - config.T0)))
|
zmin_val = float(np.min(T_matrix))
|
||||||
# Use symmetric range centred on T0 if there is any variation,
|
zmax_val = float(np.max(T_matrix))
|
||||||
# otherwise fall back to the raw range.
|
|
||||||
z_data = T_matrix.T # shape (NT, NX) — rows=time, cols=space
|
|
||||||
|
|
||||||
if zmax > 0:
|
# Subsample frames (shared with animation below)
|
||||||
|
n_frames = len(t_vals)
|
||||||
|
max_frames = 200
|
||||||
|
step = max(1, n_frames // max_frames)
|
||||||
|
frame_indices = list(range(0, n_frames, step))
|
||||||
|
|
||||||
|
# --- Figura A: heatmap 2D statica (identica all'originale) ---
|
||||||
|
z_data = T_matrix.T # (NT, NX)
|
||||||
z_center = float(np.mean(T_matrix))
|
z_center = float(np.mean(T_matrix))
|
||||||
z_abs = float(np.max(np.abs(T_matrix - z_center)))
|
z_abs = float(np.max(np.abs(T_matrix - z_center))) or 1.0
|
||||||
zmin_sym = z_center - z_abs
|
fig_static = go.Figure(go.Heatmap(
|
||||||
zmax_sym = z_center + z_abs
|
|
||||||
else:
|
|
||||||
zmin_sym = float(np.min(T_matrix))
|
|
||||||
zmax_sym = float(np.max(T_matrix))
|
|
||||||
|
|
||||||
fig_heatmap = go.Figure(go.Heatmap(
|
|
||||||
z=z_data,
|
z=z_data,
|
||||||
x=x_vals,
|
x=x_vals,
|
||||||
y=t_vals,
|
y=t_vals,
|
||||||
colorscale='RdBu_r',
|
colorscale='RdBu_r',
|
||||||
zmin=zmin_sym,
|
zmin=z_center - z_abs,
|
||||||
zmax=zmax_sym,
|
zmax=z_center + z_abs,
|
||||||
colorbar=dict(title='T [°C]'),
|
colorbar=dict(title='T [°C]'),
|
||||||
))
|
))
|
||||||
fig_heatmap.update_layout(
|
fig_static.update_layout(
|
||||||
title='FDM — Heat Equation T(x,t)',
|
title='FDM — Heat Equation T(x,t)',
|
||||||
xaxis_title='x [m]',
|
xaxis_title='x [m]',
|
||||||
yaxis_title='t [s]',
|
yaxis_title='t [s]',
|
||||||
height=500,
|
height=480,
|
||||||
|
margin=dict(t=50, b=50, l=70, r=90),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# --- Figura B: striscia 1D animata ---
|
||||||
|
strip_frames = [
|
||||||
|
go.Frame(
|
||||||
|
data=[go.Heatmap(
|
||||||
|
z=T_matrix[:, idx].reshape(1, -1),
|
||||||
|
x=x_vals,
|
||||||
|
y=[0],
|
||||||
|
colorscale='Jet',
|
||||||
|
zmin=zmin_val,
|
||||||
|
zmax=zmax_val,
|
||||||
|
showscale=True,
|
||||||
|
colorbar=dict(title='T [°C]', thickness=18),
|
||||||
|
)],
|
||||||
|
name=str(idx),
|
||||||
|
layout=go.Layout(title_text=f't = {t_vals[idx]:.3f} s'),
|
||||||
|
)
|
||||||
|
for idx in frame_indices
|
||||||
|
]
|
||||||
|
|
||||||
|
fig_strip = go.Figure(
|
||||||
|
data=[go.Heatmap(
|
||||||
|
z=T_matrix[:, frame_indices[0]].reshape(1, -1),
|
||||||
|
x=x_vals,
|
||||||
|
y=[0],
|
||||||
|
colorscale='Jet',
|
||||||
|
zmin=zmin_val,
|
||||||
|
zmax=zmax_val,
|
||||||
|
showscale=True,
|
||||||
|
colorbar=dict(title='T [°C]', thickness=18),
|
||||||
|
)],
|
||||||
|
frames=strip_frames,
|
||||||
|
layout=go.Layout(
|
||||||
|
title=f't = {t_vals[frame_indices[0]]:.3f} s',
|
||||||
|
xaxis=dict(title='x [m]'),
|
||||||
|
yaxis=dict(showticklabels=False, showgrid=False,
|
||||||
|
zeroline=False, fixedrange=True),
|
||||||
|
height=300,
|
||||||
|
margin=dict(t=80, b=130, l=70, r=110),
|
||||||
|
updatemenus=[dict(
|
||||||
|
type='buttons',
|
||||||
|
showactive=False,
|
||||||
|
y=1.22, x=0.5, xanchor='center',
|
||||||
|
buttons=[
|
||||||
|
dict(label='▶ Play', method='animate',
|
||||||
|
args=[None, dict(frame=dict(duration=40, redraw=True),
|
||||||
|
fromcurrent=True, mode='immediate')]),
|
||||||
|
dict(label='⏸ Pause', method='animate',
|
||||||
|
args=[[None], dict(frame=dict(duration=0, redraw=False),
|
||||||
|
mode='immediate')]),
|
||||||
|
],
|
||||||
|
)],
|
||||||
|
sliders=[dict(
|
||||||
|
steps=[
|
||||||
|
dict(method='animate',
|
||||||
|
args=[[str(idx)], dict(mode='immediate',
|
||||||
|
frame=dict(duration=0, redraw=True))],
|
||||||
|
label=f'{t_vals[idx]:.2f}')
|
||||||
|
for idx in frame_indices
|
||||||
|
],
|
||||||
|
transition=dict(duration=0),
|
||||||
|
x=0.05, y=0, len=0.9,
|
||||||
|
currentvalue=dict(prefix='t = ', font=dict(size=14)),
|
||||||
|
)],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Scrivi entrambe le figure in un unico file HTML
|
||||||
|
html_static = fig_static.to_html(full_html=False, include_plotlyjs='cdn')
|
||||||
|
html_strip = fig_strip.to_html(full_html=False, include_plotlyjs=False)
|
||||||
heatmap_path = os.path.join(out_dir, 'heatmap.html')
|
heatmap_path = os.path.join(out_dir, 'heatmap.html')
|
||||||
fig_heatmap.write_html(heatmap_path)
|
with open(heatmap_path, 'w', encoding='utf-8') as _f:
|
||||||
|
_f.write('<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>\n')
|
||||||
|
_f.write(html_static)
|
||||||
|
_f.write('\n')
|
||||||
|
_f.write(html_strip)
|
||||||
|
_f.write('\n</body></html>')
|
||||||
print(f"Heatmap saved → {heatmap_path}")
|
print(f"Heatmap saved → {heatmap_path}")
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# 2. Animated profile T(x) evolving in time
|
# 2. Animated profile T(x) evolving in time
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
L = config.L
|
L = config.L
|
||||||
n_frames = len(t_vals)
|
|
||||||
|
|
||||||
# Subsample frames for a manageable animation (max ~200 frames)
|
|
||||||
max_frames = 200
|
|
||||||
step = max(1, n_frames // max_frames)
|
|
||||||
frame_indices = list(range(0, n_frames, step))
|
|
||||||
|
|
||||||
y_min = float(np.min(T_matrix)) - 1.0
|
y_min = float(np.min(T_matrix)) - 1.0
|
||||||
y_max = float(np.max(T_matrix)) + 1.0
|
y_max = float(np.max(T_matrix)) + 1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user