CoolFace
Apppublic

harishaseebat92/Fluid_Simulation_Classical

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