From af35882b0a207fba1dbcef112b1c98f74bc48bd6 Mon Sep 17 00:00:00 2001 From: shaia Date: Fri, 6 Mar 2026 09:47:22 +0200 Subject: [PATCH] examples: Add 3D visualization examples Four new examples showcasing 3D features: - 3d_surface_plots.py: Interactive Plotly surfaces for velocity, pressure, vorticity - 3d_dashboard.py: Multi-panel dashboard with 3D surface subplot - 3d_surface_animation.py: Rotating camera matplotlib GIF animation - 3d_animated_dashboard.py: Animated Plotly dashboard with 3D scatter and surface views --- examples/3d_animated_dashboard.py | 149 +++++++++++++++++++++++++++++ examples/3d_dashboard.py | 123 ++++++++++++++++++++++++ examples/3d_surface_animation.py | 149 +++++++++++++++++++++++++++++ examples/3d_surface_plots.py | 151 ++++++++++++++++++++++++++++++ 4 files changed, 572 insertions(+) create mode 100644 examples/3d_animated_dashboard.py create mode 100644 examples/3d_dashboard.py create mode 100644 examples/3d_surface_animation.py create mode 100644 examples/3d_surface_plots.py diff --git a/examples/3d_animated_dashboard.py b/examples/3d_animated_dashboard.py new file mode 100644 index 0000000..c01d2d3 --- /dev/null +++ b/examples/3d_animated_dashboard.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Animated Dashboard with 3D Views Example. + +This example demonstrates how to create an animated Plotly dashboard +that includes 3D scatter plots and 3D surface views alongside 2D +visualizations, with Play/Pause playback controls. + +Features demonstrated: +1. Animated multi-panel dashboard (3x3 grid) +2. 3D scatter plot of velocity vectors colored by magnitude +3. 3D surface plot of velocity magnitude +4. Playback controls (Play/Pause, timeline slider) +5. Time evolution of flow fields + +Usage: + python examples/3d_animated_dashboard.py + +Requirements: + - cfd_python package (pip install -e ../cfd-python) + - cfd_viz package + - plotly package +""" + +import sys +from pathlib import Path + +import numpy as np + +try: + import cfd_python +except ImportError: + print("Error: cfd_python package not installed.") + print("Install with: pip install -e ../cfd-python") + sys.exit(1) + +from cfd_viz.common import read_vtk_file +from cfd_viz.interactive import ( + create_animated_dashboard, + create_interactive_frame_collection, +) + + +def run_transient_simulations() -> list: + """Run simulations at multiple timesteps. + + Returns: + List of output VTK file paths. + """ + output_dir = Path("output/vtk/3d_animated_transient") + output_dir.mkdir(parents=True, exist_ok=True) + + total_steps = 300 + output_interval = 50 + + print("Running transient simulations...") + print(" Grid: 60 x 60") + print(f" Total steps: {total_steps}") + print(f" Output interval: {output_interval}") + + vtk_files = [] + for step in range(output_interval, total_steps + 1, output_interval): + output_file = str(output_dir / f"flow_{step:04d}.vtk") + + cfd_python.run_simulation_with_params( + nx=60, + ny=60, + xmin=0.0, + xmax=1.0, + ymin=0.0, + ymax=1.0, + steps=step, + output_file=output_file, + ) + + print(f" Step {step}: {output_file}") + vtk_files.append(output_file) + + return vtk_files + + +def create_animated_3d_dashboard(vtk_files: list): + """Create an animated dashboard with 3D views. + + Args: + vtk_files: List of VTK file paths (one per timestep). + """ + print("\nLoading simulation frames...") + + frames_data = [] + time_indices = [] + + for vtk_file in vtk_files: + data = read_vtk_file(vtk_file) + if data is None: + print(f"Warning: Could not read {vtk_file}") + continue + + x, y = data.x, data.y + u, v = data.u, data.v + p = data.get("p", np.zeros_like(u)) + + frames_data.append((x, y, u, v, p)) + time_idx = int(vtk_file.split("_")[-1].split(".")[0]) + time_indices.append(time_idx) + + print(f"Loaded {len(frames_data)} frames") + + # Create frame collection for animated dashboard + frame_collection = create_interactive_frame_collection(frames_data, time_indices) + + # Create animated dashboard with 3D views + print("\nCreating animated dashboard with 3D views...") + fig = create_animated_dashboard( + frame_collection, + title="3D Animated Flow Dashboard", + ) + + output_dir = Path("output/html") + output_dir.mkdir(parents=True, exist_ok=True) + + out_path = output_dir / "3d_animated_dashboard.html" + fig.write_html(str(out_path)) + print(f" Saved: {out_path}") + + +def main(): + """Main function.""" + print("3D Animated Dashboard Example") + print("=" * 40) + + vtk_files = run_transient_simulations() + create_animated_3d_dashboard(vtk_files) + + print("\n" + "=" * 40) + print("Done!") + print("\nOutput file (open in browser):") + print(" - output/html/3d_animated_dashboard.html") + print("\nThe dashboard includes:") + print(" - Velocity magnitude heatmap (animated)") + print(" - Velocity vectors (animated)") + print(" - Pressure heatmap (animated)") + print(" - Vorticity heatmap (animated)") + print(" - 3D flow scatter plot (drag to rotate)") + print(" - 3D velocity magnitude surface (drag to rotate)") + print(" - Play/Pause controls and timeline slider") + + +if __name__ == "__main__": + main() diff --git a/examples/3d_dashboard.py b/examples/3d_dashboard.py new file mode 100644 index 0000000..2026ff6 --- /dev/null +++ b/examples/3d_dashboard.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Multi-panel Dashboard with 3D Surface Example. + +This example demonstrates how to create a multi-panel interactive +dashboard that includes a 3D surface subplot alongside 2D heatmaps +and vector field visualizations. + +Features demonstrated: +1. Multi-panel Plotly dashboard with 2x3 grid layout +2. 3D surface subplot showing velocity magnitude +3. 2D heatmaps for pressure and vorticity +4. Velocity vector field overlay + +Usage: + python examples/3d_dashboard.py + +Requirements: + - cfd_python package (pip install -e ../cfd-python) + - cfd_viz package + - plotly package +""" + +import sys +from pathlib import Path + +import numpy as np + +try: + import cfd_python +except ImportError: + print("Error: cfd_python package not installed.") + print("Install with: pip install -e ../cfd-python") + sys.exit(1) + +from cfd_viz.common import read_vtk_file +from cfd_viz.interactive import create_dashboard_figure, create_interactive_frame + + +def run_simulation() -> str: + """Run a steady-state CFD simulation. + + Returns: + Path to the output VTK file. + """ + output_file = "output/vtk/3d_dashboard_flow.vtk" + Path("output/vtk").mkdir(parents=True, exist_ok=True) + + print("Running simulation: 80x80 grid, 500 steps...") + cfd_python.run_simulation_with_params( + nx=80, + ny=80, + xmin=0.0, + xmax=1.0, + ymin=0.0, + ymax=1.0, + steps=500, + output_file=output_file, + ) + print("Simulation complete!") + return output_file + + +def create_dashboard(vtk_file: str): + """Create a multi-panel dashboard with 3D surface. + + Args: + vtk_file: Path to VTK file with simulation results. + """ + print(f"\nLoading results from: {vtk_file}") + + data = read_vtk_file(vtk_file) + if data is None: + print(f"Error: Could not read {vtk_file}") + return + + x, y = data.x, data.y + u, v = data.u, data.v + p = data.get("p", np.zeros_like(u)) + + print(f"Domain: [{x.min():.2f}, {x.max():.2f}] x [{y.min():.2f}, {y.max():.2f}]") + print(f"Grid: {data.nx} x {data.ny}") + + output_dir = Path("output/html") + output_dir.mkdir(parents=True, exist_ok=True) + + # Create interactive frame (bundles x, y, u, v, p for the dashboard) + frame = create_interactive_frame(x, y, u, v, p=p, time_index=500) + + # Create multi-panel dashboard (includes 3D surface subplot) + print("\nCreating multi-panel dashboard with 3D surface...") + fig = create_dashboard_figure( + frame, + title="CFD Flow Dashboard with 3D Surface", + ) + + out_path = output_dir / "3d_dashboard.html" + fig.write_html(str(out_path)) + print(f" Saved: {out_path}") + + +def main(): + """Main function.""" + print("3D Dashboard Example") + print("=" * 40) + + vtk_file = run_simulation() + create_dashboard(vtk_file) + + print("\n" + "=" * 40) + print("Done!") + print("\nOutput file (open in browser):") + print(" - output/html/3d_dashboard.html") + print("\nThe dashboard includes:") + print(" - Velocity magnitude heatmap") + print(" - Velocity vector field") + print(" - Pressure heatmap") + print(" - Vorticity heatmap") + print(" - Streamlines") + print(" - 3D velocity magnitude surface (drag to rotate)") + + +if __name__ == "__main__": + main() diff --git a/examples/3d_surface_animation.py b/examples/3d_surface_animation.py new file mode 100644 index 0000000..77a02dc --- /dev/null +++ b/examples/3d_surface_animation.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""3D Rotating Surface Animation Example. + +This example demonstrates how to create a rotating 3D surface animation +of CFD simulation results using matplotlib, exported as a GIF. + +Features demonstrated: +1. Transient simulation with multiple timesteps +2. 3D surface plot with matplotlib's mplot3d +3. Rotating camera animation across frames +4. Contour projection on the base plane +5. GIF export + +Usage: + python examples/3d_surface_animation.py + +Requirements: + - cfd_python package (pip install -e ../cfd-python) + - cfd_viz package + - pillow package (for GIF export) +""" + +import sys +from pathlib import Path + +try: + import cfd_python +except ImportError: + print("Error: cfd_python package not installed.") + print("Install with: pip install -e ../cfd-python") + sys.exit(1) + +from cfd_viz.animation import create_3d_surface_animation, create_animation_frames +from cfd_viz.common import read_vtk_file + + +def run_transient_simulations() -> list: + """Run simulations at multiple timesteps. + + Returns: + List of output VTK file paths. + """ + output_dir = Path("output/vtk/3d_transient") + output_dir.mkdir(parents=True, exist_ok=True) + + total_steps = 300 + output_interval = 50 + + print("Running transient simulations...") + print(" Grid: 60 x 60") + print(f" Total steps: {total_steps}") + print(f" Output interval: {output_interval}") + + vtk_files = [] + for step in range(output_interval, total_steps + 1, output_interval): + output_file = str(output_dir / f"flow_{step:04d}.vtk") + + cfd_python.run_simulation_with_params( + nx=60, + ny=60, + xmin=0.0, + xmax=1.0, + ymin=0.0, + ymax=1.0, + steps=step, + output_file=output_file, + ) + + print(f" Step {step}: {output_file}") + vtk_files.append(output_file) + + return vtk_files + + +def create_animation(vtk_files: list): + """Create a rotating 3D surface animation from transient results. + + Args: + vtk_files: List of VTK file paths (one per timestep). + """ + print("\nLoading simulation frames...") + + frames_data = [] + time_indices = [] + + for vtk_file in vtk_files: + data = read_vtk_file(vtk_file) + if data is None: + print(f"Warning: Could not read {vtk_file}") + continue + + X, Y = data.X, data.Y + u, v = data.u, data.v + p = data.get("p", None) + + frames_data.append((X, Y, u, v, p)) + time_idx = int(vtk_file.split("_")[-1].split(".")[0]) + time_indices.append(time_idx) + + print(f"Loaded {len(frames_data)} frames") + + # Build AnimationFrames (computes velocity_mag, vorticity, etc.) + animation_frames = create_animation_frames( + frames_data, + time_indices=time_indices, + compute_derived=True, + ) + + # Create 3D rotating surface animation + print("\nCreating 3D rotating surface animation...") + _fig, anim = create_3d_surface_animation( + animation_frames, + field_name="velocity_mag", + figsize=(12, 8), + interval=200, + rotate_camera=True, + title_prefix="Velocity Magnitude", + ) + + output_dir = Path("output/animations") + output_dir.mkdir(parents=True, exist_ok=True) + + out_path = output_dir / "3d_surface_rotation.gif" + print(f"Saving GIF to: {out_path}") + anim.save(str(out_path), writer="pillow", fps=5) + print(f" Saved: {out_path}") + + +def main(): + """Main function.""" + print("3D Surface Animation Example") + print("=" * 40) + + vtk_files = run_transient_simulations() + create_animation(vtk_files) + + print("\n" + "=" * 40) + print("Done!") + print("\nOutput file:") + print(" - output/animations/3d_surface_rotation.gif") + print("\nThe animation shows:") + print(" - Velocity magnitude as 3D surface height") + print(" - Rotating camera view across frames") + print(" - Contour projection on the base plane") + print(" - Flow evolution over simulation timesteps") + + +if __name__ == "__main__": + main() diff --git a/examples/3d_surface_plots.py b/examples/3d_surface_plots.py new file mode 100644 index 0000000..7cc7055 --- /dev/null +++ b/examples/3d_surface_plots.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Interactive 3D Surface Plots Example. + +This example demonstrates how to create interactive 3D surface +visualizations of different CFD fields using Plotly. + +Features demonstrated: +1. Velocity magnitude as a 3D surface +2. Pressure field as a 3D surface +3. Vorticity field as a 3D surface with diverging colorscale + +Usage: + python examples/3d_surface_plots.py + +Requirements: + - cfd_python package (pip install -e ../cfd-python) + - cfd_viz package + - plotly package +""" + +import sys +from pathlib import Path + +import numpy as np + +try: + import cfd_python +except ImportError: + print("Error: cfd_python package not installed.") + print("Install with: pip install -e ../cfd-python") + sys.exit(1) + +from cfd_viz.common import read_vtk_file +from cfd_viz.fields import magnitude, vorticity +from cfd_viz.interactive import create_surface_figure + + +def run_simulation() -> str: + """Run a steady-state CFD simulation. + + Returns: + Path to the output VTK file. + """ + output_file = "output/vtk/3d_surface_flow.vtk" + Path("output/vtk").mkdir(parents=True, exist_ok=True) + + print("Running simulation: 80x80 grid, 500 steps...") + cfd_python.run_simulation_with_params( + nx=80, + ny=80, + xmin=0.0, + xmax=1.0, + ymin=0.0, + ymax=1.0, + steps=500, + output_file=output_file, + ) + print("Simulation complete!") + return output_file + + +def create_3d_surfaces(vtk_file: str): + """Create interactive 3D surface plots for different fields. + + Args: + vtk_file: Path to VTK file with simulation results. + """ + print(f"\nLoading results from: {vtk_file}") + + data = read_vtk_file(vtk_file) + if data is None: + print(f"Error: Could not read {vtk_file}") + return + + x, y = data.x, data.y + u, v = data.u, data.v + p = data.get("p", np.zeros_like(u)) + dx, dy = data.dx, data.dy + + vel_mag = magnitude(u, v) + omega = vorticity(u, v, dx, dy) + + print(f"Domain: [{x.min():.2f}, {x.max():.2f}] x [{y.min():.2f}, {y.max():.2f}]") + print(f"Grid: {data.nx} x {data.ny}") + print(f"Max velocity: {np.nanmax(vel_mag):.4f}") + + output_dir = Path("output/html") + output_dir.mkdir(parents=True, exist_ok=True) + + # 1. Velocity magnitude surface + print("\nCreating velocity magnitude 3D surface...") + fig_vel = create_surface_figure( + x, + y, + vel_mag, + title="Velocity Magnitude", + colorscale="Viridis", + ) + out_path = output_dir / "3d_surface_velocity.html" + fig_vel.write_html(str(out_path)) + print(f" Saved: {out_path}") + + # 2. Pressure field surface + print("Creating pressure 3D surface...") + fig_p = create_surface_figure( + x, + y, + p, + title="Pressure Field", + colorscale="Plasma", + ) + out_path = output_dir / "3d_surface_pressure.html" + fig_p.write_html(str(out_path)) + print(f" Saved: {out_path}") + + # 3. Vorticity surface (diverging colorscale for signed values) + print("Creating vorticity 3D surface...") + fig_omega = create_surface_figure( + x, + y, + omega, + title="Vorticity Field", + colorscale="RdBu", + ) + out_path = output_dir / "3d_surface_vorticity.html" + fig_omega.write_html(str(out_path)) + print(f" Saved: {out_path}") + + +def main(): + """Main function.""" + print("3D Surface Plots Example") + print("=" * 40) + + vtk_file = run_simulation() + create_3d_surfaces(vtk_file) + + print("\n" + "=" * 40) + print("Done!") + print("\nOutput files (open in browser):") + print(" - output/html/3d_surface_velocity.html") + print(" - output/html/3d_surface_pressure.html") + print(" - output/html/3d_surface_vorticity.html") + print("\nInteraction tips:") + print(" - Drag to rotate the surface") + print(" - Scroll to zoom in/out") + print(" - Hover to see field values") + + +if __name__ == "__main__": + main()