Spaces:
Runtime error
Runtime error
File size: 5,818 Bytes
6a43f79 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import tempfile
import gradio as gr
def solve_and_animate_2d(Lx: float,
Ly: float,
t_max: float,
Gamma: float = 0.1,
Nx: int = 50,
Ny: int = 50,
initial: str = "gaussian",
bc: str = "dirichlet",
frame_skip: int = 1):
"""
Solve the 2D heat equation u_t = Gamma*(u_xx + u_yy) and return a GIF animation.
Initial conditions: {"gaussian", "random", "sinusoidal", "step"}
Boundary conditions: {"dirichlet", "neumann", "periodic"}
"""
# Spatial grid
x = np.linspace(0, Lx, Nx)
y = np.linspace(0, Ly, Ny)
dx, dy = x[1] - x[0], y[1] - y[0]
if dx == 0 or dy == 0:
raise ValueError("Nx and Ny must be > 1.")
# Time stepping for stability
dt = 1.0 / (2 * Gamma * (1/dx**2 + 1/dy**2))
Nt = int(np.ceil(t_max / dt)) + 1
rx, ry = Gamma * dt / dx**2, Gamma * dt / dy**2
# Initial condition
X, Y = np.meshgrid(x, y, indexing='ij')
if initial == "gaussian":
u = np.exp(-(((X - Lx/2)**2 + (Y - Ly/2)**2) / (2*(Lx/10)**2)))
elif initial == "random":
u = np.random.rand(Nx, Ny)
elif initial == "sinusoidal":
kx, ky = 2 * np.pi / Lx, 2 * np.pi / Ly
u = np.sin(kx * X) * np.sin(ky * Y)
elif initial == "step":
u = np.where((X < Lx/2) & (Y < Ly/2), 1.0, 0.0)
else:
raise ValueError(f"Unknown initial condition: {initial}")
# Storage for solution
U = np.zeros((Nt, Nx, Ny))
U[0] = u.copy()
# Time-stepping loop
for n in range(1, Nt):
un = u.copy()
# Interior update
u[1:-1, 1:-1] = (
un[1:-1, 1:-1]
+ rx * (un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[:-2, 1:-1])
+ ry * (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, :-2])
)
# Boundary conditions
if bc == "dirichlet":
u[0, :] = u[-1, :] = u[:, 0] = u[:, -1] = 0.0
elif bc == "neumann":
u[0, :] = u[1, :]
u[-1, :] = u[-2, :]
u[:, 0] = u[:, 1]
u[:, -1] = u[:, -2]
elif bc == "periodic":
u[0, :] = un[-2, :]
u[-1, :] = un[1, :]
u[:, 0] = un[:, -2]
u[:, -1] = un[:, 1]
else:
raise ValueError(f"Unknown bc: {bc}")
U[n] = u.copy()
# Create animation with Matplotlib
fig, ax = plt.subplots()
im = ax.imshow(U[0].T, cmap='viridis', origin='lower', extent=[0, Lx, 0, Ly], vmin=U.min(), vmax=U.max())
ax.set_title(f"2D Heat Eq — init={initial}, bc={bc}, Gamma={Gamma:.2f}")
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.colorbar(im, ax=ax, label="u")
# Animation update function
def update(frame):
im.set_data(U[frame].T)
return [im]
# Subsample frames
idx = list(range(0, Nt, frame_skip))
if idx[-1] != Nt - 1:
idx.append(Nt - 1)
# Generate animation
ani = FuncAnimation(fig, update, frames=idx, blit=True)
# Save as GIF
with tempfile.NamedTemporaryFile(suffix='.gif', delete=False) as tmpfile:
ani.save(tmpfile.name, writer='pillow', fps=30)
gif_path = tmpfile.name
plt.close(fig)
return gif_path
def gradio_interface(lx, ly, t_max, gamma, nx, ny, initial, bc, frame_skip):
nx, ny, frame_skip = int(nx), int(ny), int(frame_skip)
return solve_and_animate_2d(
Lx=lx, Ly=ly, t_max=t_max, Gamma=gamma, Nx=nx, Ny=ny,
initial=initial, bc=bc, frame_skip=frame_skip
)
with gr.Blocks(theme=gr.themes.Soft(), title="2D Heat Simulator") as demo:
gr.Markdown("# ♨️ 2D Heat Equation Simulator\nAdjust parameters and run the simulation.")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("## Domain & Grid")
lx_slider = gr.Slider(0.1, 5.0, 1.0, 0.1, label="Lx")
ly_slider = gr.Slider(0.1, 5.0, 1.0, 0.1, label="Ly")
nx_slider = gr.Slider(3, 200, 50, 1, label="Nx")
ny_slider = gr.Slider(3, 200, 50, 1, label="Ny")
gr.Markdown("## Simulation")
t_slider = gr.Slider(0.01, 5.0, 0.5, 0.01, label="t_max")
gamma_slider = gr.Slider(0.001, 1.0, 0.1, 0.001, label="Gamma")
gr.Markdown("## Conditions")
initial_dropdown = gr.Dropdown(
["gaussian", "random", "sinusoidal", "step"], "gaussian", label="Initial"
)
bc_dropdown = gr.Dropdown(
["dirichlet", "neumann", "periodic"], "dirichlet", label="Boundary"
)
gr.Markdown("## Animation")
frame_skip_slider = gr.Slider(1, 50, 5, 1, label="Frame Skip")
run_btn = gr.Button("Run Simulation", variant="primary")
with gr.Column(scale=3):
plot_output = gr.Image(label="Heatmap Animation")
inputs_list = [lx_slider, ly_slider, t_slider, gamma_slider,
nx_slider, ny_slider, initial_dropdown, bc_dropdown, frame_skip_slider]
run_btn.click(fn=gradio_interface, inputs=inputs_list, outputs=plot_output)
gr.Examples(
examples=[
[1.0, 1.0, 0.5, 0.1, 50, 50, "gaussian", "dirichlet", 5],
[2.0, 1.0, 1.0, 0.05, 60, 30, "sinusoidal", "periodic", 10],
[1.0, 1.0, 0.2, 0.2, 80, 80, "step", "neumann", 2],
],
inputs=inputs_list,
outputs=[plot_output],
fn=gradio_interface
)
if __name__ == "__main__":
demo.launch() |