harishaseebat92 commited on
Commit
5d756e6
·
1 Parent(s): f07ea24

Added Introduction page

Browse files
Files changed (1) hide show
  1. app.py +164 -73
app.py CHANGED
@@ -1,11 +1,58 @@
1
  from fluid import *
2
  from gradio_litmodel3d import LitModel3D
 
 
 
 
 
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  # Modified interface functions to take num_reg_qubits_input
6
  def qlbm_gradio_interface(grid_size_input: int, time_steps_input: int, distribution_type_param: str, velocity_field_param: str, vx_param: float, vy_param: float, boundary_condition_param: str):
7
- num_reg_qubits_val = int(math.log2(grid_size_input)) # Convert grid_size back to num_reg_qubits
8
- grid_size_val = grid_size_input
 
 
 
9
  time_steps_val = int(time_steps_input)
10
  vx_val = float(vx_param)
11
  vy_val = float(vy_param)
@@ -88,80 +135,124 @@ def set_gaussian_example():
88
 
89
  # Gradio interface for Fluid Dynamics - 2D only
90
  with gr.Blocks(theme=gr.themes.Soft(), title="QLBM Fluid Simulation (2D)") as demo:
91
- with gr.Row(): # Main row for top section
92
- with gr.Column(scale=1): # Column for left-side controls
93
- gr.Markdown("## Initial Distribution Examples")
94
- with gr.Row():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  with gr.Column(scale=1):
96
- example1 = LitModel3D("Placeholder_Images/sinusoidal.stl", label="Sinusoidal")
97
- sinusoidal_btn_2d = gr.Button("Sinusoidal")
 
 
 
 
98
  with gr.Column(scale=1):
99
- example2 = LitModel3D("Placeholder_Images/gaussian.stl", label="Gaussian")
100
- gaussian_btn_2d = gr.Button("Gaussian")
101
-
102
- gr.Markdown("## Simulation Parameters")
103
- num_reg_qubits_input_2d = gr.Slider(
104
- minimum=2**7, maximum=2**12, # Grid size values
105
- value=2**8, step=None, # step=None for arbitrary powers of 2 (handled by update function)
106
- label="Grid Size/Direction"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  )
108
- time_steps_slider_2d = gr.Slider(minimum=0, maximum=4000, value=1600, step=10, label="Time Steps")
109
- qubit_plot_2d = gr.Plot(label="Qubits vs. Grid Size")
110
- total_qubits_display_2d = gr.Markdown("Total Qubits: 19")
111
- warning_display_2d = gr.Markdown("")
112
- recommended_time_steps_display_2d = gr.Markdown("Recommended time steps: 1600")
113
-
114
- with gr.Column(scale=2): # Column for the main plot
115
- qlbm_interactive_plot_2d = gr.Plot(label="QLBM")
116
- download_button_2d = gr.DownloadButton(label="Download Plot Data (JSON)", visible=False) # New download button
117
- plotly_json_frames_state_2d = gr.State([]) # New state to store Plotly JSON frames
118
-
119
- with gr.Row(): # Row for bottom section
120
- with gr.Column(scale=1):
121
- gr.Markdown("## Initialization (Uniform)")
122
- selected_velocity_field_2d = gr.State("Uniform")
123
- vx_slider_2d = gr.Slider(minimum=-0.3, maximum=0.3, value=0.2, step=0.01, label="V_x", visible=True)
124
- vy_slider_2d = gr.Slider(minimum=-0.3, maximum=0.3, value=0.15, step=0.01, label="V_y", visible=True)
125
- distribution_type_input_2d = gr.Radio(choices=["Gaussian", "Sinusoidal"], value="Sinusoidal", label="Initial Distribution Type")
126
-
127
- with gr.Column(scale=1):
128
- gr.Markdown("## Boundary Conditions")
129
- boundary_condition_input_2d = gr.Radio(choices=["Periodic"], value="Periodic", label="Boundary Condition")
130
-
131
- run_qlbm_btn_2d = gr.Button("Run Simulation", variant="primary") # Button below the sections
132
-
133
- qlbm_inputs_list_2d = [
134
- num_reg_qubits_input_2d, time_steps_slider_2d, distribution_type_input_2d,
135
- selected_velocity_field_2d, vx_slider_2d, vy_slider_2d, boundary_condition_input_2d
136
- ]
137
- run_qlbm_btn_2d.click(
138
- fn=qlbm_gradio_interface,
139
- inputs=qlbm_inputs_list_2d,
140
- outputs=[qlbm_interactive_plot_2d, plotly_json_frames_state_2d] # Modified output
141
- ).then(
142
- lambda: gr.update(visible=True), # Make download button visible after simulation
143
- outputs=[download_button_2d]
144
- )
145
- download_button_2d.click(
146
- fn=download_plot_data,
147
- inputs=[plotly_json_frames_state_2d],
148
- outputs=[download_button_2d]
149
- )
150
- num_reg_qubits_input_2d.change(
151
- fn=update_qubit_info,
152
- inputs=num_reg_qubits_input_2d,
153
- outputs=[qubit_plot_2d, total_qubits_display_2d, warning_display_2d, recommended_time_steps_display_2d]
154
- )
155
- sinusoidal_btn_2d.click(
156
- fn=set_sinusoidal_example,
157
- inputs=[],
158
- outputs=[num_reg_qubits_input_2d, time_steps_slider_2d, distribution_type_input_2d, selected_velocity_field_2d, vx_slider_2d, vy_slider_2d, boundary_condition_input_2d]
159
- )
160
- gaussian_btn_2d.click(
161
- fn=set_gaussian_example,
162
- inputs=[],
163
- outputs=[num_reg_qubits_input_2d, time_steps_slider_2d, distribution_type_input_2d, selected_velocity_field_2d, vx_slider_2d, vy_slider_2d, boundary_condition_input_2d]
164
- )
165
 
166
  if __name__ == "__main__":
167
  try:
 
1
  from fluid import *
2
  from gradio_litmodel3d import LitModel3D
3
+ import math, zipfile, tempfile
4
+ import numpy as np
5
+ import plotly.graph_objects as go
6
+ import gradio as gr
7
+ import cudaq
8
 
9
+ # Helper: snap grid size to nearest power of two within bounds
10
+ def _nearest_pow2(n: int, min_pow: int = 2**7, max_pow: int = 2**12) -> int:
11
+ if n <= 0:
12
+ return min_pow
13
+ # nearest power of two
14
+ p = 1 << (int(round(math.log2(max(n, 1)))))
15
+ # clamp to bounds and adjust if out of range
16
+ if p < min_pow:
17
+ p = min_pow
18
+ if p > max_pow:
19
+ # choose the closer between max_pow and previous power
20
+ p = max_pow
21
+ return p
22
+
23
+ # New update function: also updates the slider value after snapping
24
+ def update_grid_and_qubit_info(grid_size):
25
+ snapped = _nearest_pow2(int(grid_size))
26
+ num_reg_qubits = int(math.log2(snapped))
27
+ total_qubits = 2 * num_reg_qubits + 3
28
+
29
+ x = np.array([128, 256, 512, 1024, 2048, 4096])
30
+ y = np.log2(x).astype(int)
31
+ fig = go.Figure()
32
+ fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name='Qubits/Direction'))
33
+ fig.add_trace(go.Scatter(x=[snapped], y=[num_reg_qubits], mode='markers', marker=dict(size=12, color='red'), name='Current Selection'))
34
+ fig.update_layout(
35
+ xaxis_title="Grid Size (Points/Direction)",
36
+ yaxis_title="Qubits/Direction",
37
+ width=400,
38
+ height=300
39
+ )
40
+
41
+ total_qubits_display = f"Total Qubits: {total_qubits}"
42
+ warn_pow2 = " (snapped to nearest power of two)" if snapped != int(grid_size) else ""
43
+ warning = ("⚠️ Warning: Grid sizes > 1024 may exceed simulator/memory limits!" if snapped > 1024 else "") + warn_pow2
44
+ recommended_time_steps = num_reg_qubits * 200
45
+ recommended_display = f"Recommended time steps: {recommended_time_steps}"
46
+
47
+ return gr.update(value=snapped), fig, total_qubits_display, warning, recommended_display
48
 
49
  # Modified interface functions to take num_reg_qubits_input
50
  def qlbm_gradio_interface(grid_size_input: int, time_steps_input: int, distribution_type_param: str, velocity_field_param: str, vx_param: float, vy_param: float, boundary_condition_param: str):
51
+ snapped_grid = _nearest_pow2(int(grid_size_input))
52
+ if snapped_grid != int(grid_size_input):
53
+ gr.Warning(f"Grid size {grid_size_input} is not a power of two. Using {snapped_grid}.")
54
+ num_reg_qubits_val = int(math.log2(snapped_grid))
55
+ grid_size_val = snapped_grid
56
  time_steps_val = int(time_steps_input)
57
  vx_val = float(vx_param)
58
  vy_val = float(vy_param)
 
135
 
136
  # Gradio interface for Fluid Dynamics - 2D only
137
  with gr.Blocks(theme=gr.themes.Soft(), title="QLBM Fluid Simulation (2D)") as demo:
138
+ with gr.Tabs():
139
+ with gr.TabItem("Introduction"):
140
+ gr.Markdown(
141
+ """
142
+ # Quantum Lattice Boltzmann (QLBM)
143
+ This app runs a 2D quantum-inspired Lattice Boltzmann Method using a D2Q5 model (5 discrete velocity directions) to evolve a scalar density field with uniform advection and periodic boundaries.
144
+
145
+ What is simulated
146
+ - D2Q5 advection–diffusion on a square grid (periodic BCs only).
147
+ - Uniform velocity (Vx, Vy) applied to all cells.
148
+ - State is evolved via CUDA-Q kernels; results are downsampled and visualized as 3D surfaces with a time slider.
149
+
150
+ How it works (from the implementation)
151
+ - Grid size N = 2^q (q = qubits per spatial dimension). Total qubits = 2*q + 3 direction qubits.
152
+ - Per timestep: a Householder-based prep_op initializes direction amplitudes, then conditional shifts stream along x and y (lshift/rshift) controlled by direction bits, followed by an unprep.
153
+ - Up to 40 uniform time samples between 0..T are saved for visualization.
154
+
155
+ Visualization pipeline
156
+ - Only the real component of the state is saved and plotted; the initialized field is normalized before evolution.
157
+ - Data is saved as .npy in a temporary folder, downsampled by a factor of 2^5 (32). For very small grids, the factor is reduced to keep at least 1×1.
158
+ - Plotly renders one surface per saved frame; a slider toggles visibility. You can download per-frame Plotly JSON for offline analysis.
159
+
160
+ Parameters you control
161
+ - Grid Size/Direction: N = 2^q. The UI snaps any input to the nearest power of two in [128, 4096]. Larger N increases memory and runtime.
162
+ - Time Steps (T): total evolution steps; up to 40 frames are sampled in [0, T].
163
+ - Initial Distribution: Sinusoidal or Gaussian.
164
+ - Velocity Field: Uniform (set Vx, Vy).
165
+ - Boundary Condition: Periodic.
166
+
167
+ Practical notes
168
+ - Recommended time steps ≈ q * 200.
169
+ - N > 1024 may exceed available memory/time.
170
+ - Requires a CUDA-capable GPU and CUDA-Q runtime; CPU fallback will be slow or may fail.
171
+
172
+ Workflow
173
+ 1) Review example initial distributions.
174
+ 2) Choose grid size N and time steps T.
175
+ 3) Set Vx and Vy; keep Periodic BC.
176
+ 4) Run the simulation; use the slider to browse frames.
177
+ 5) Optionally download the plotted frames as JSON for offline analysis.
178
+ """
179
+ )
180
+
181
+ with gr.TabItem("Simulation"):
182
+ with gr.Row(): # Main row for top section
183
+ with gr.Column(scale=1): # Column for left-side controls
184
+ gr.Markdown("## Initial Distribution Examples")
185
+ with gr.Row():
186
+ with gr.Column(scale=1):
187
+ example1 = LitModel3D("Placeholder_Images/sinusoidal.stl", label="Sinusoidal")
188
+ sinusoidal_btn_2d = gr.Button("Sinusoidal")
189
+ with gr.Column(scale=1):
190
+ example2 = LitModel3D("Placeholder_Images/gaussian.stl", label="Gaussian")
191
+ gaussian_btn_2d = gr.Button("Gaussian")
192
+
193
+ gr.Markdown("## Simulation Parameters")
194
+ num_reg_qubits_input_2d = gr.Slider(
195
+ minimum=2**7, maximum=2**12, # Grid size values
196
+ value=2**8, step=1, # use step=1 for broader Gradio compatibility
197
+ label="Grid Size/Direction"
198
+ )
199
+ time_steps_slider_2d = gr.Slider(minimum=0, maximum=4000, value=1600, step=10, label="Time Steps")
200
+ qubit_plot_2d = gr.Plot(label="Qubits vs. Grid Size")
201
+ total_qubits_display_2d = gr.Markdown("Total Qubits: 19")
202
+ warning_display_2d = gr.Markdown("")
203
+ recommended_time_steps_display_2d = gr.Markdown("Recommended time steps: 1600")
204
+
205
+ with gr.Column(scale=2): # Column for the main plot
206
+ qlbm_interactive_plot_2d = gr.Plot(label="QLBM")
207
+ download_button_2d = gr.DownloadButton(label="Download Plot Data (JSON)", visible=False) # New download button
208
+ plotly_json_frames_state_2d = gr.State([]) # New state to store Plotly JSON frames
209
+
210
+ with gr.Row(): # Row for bottom section
211
  with gr.Column(scale=1):
212
+ gr.Markdown("## Initialization (Uniform)")
213
+ selected_velocity_field_2d = gr.State("Uniform")
214
+ vx_slider_2d = gr.Slider(minimum=-0.3, maximum=0.3, value=0.2, step=0.01, label="V_x", visible=True)
215
+ vy_slider_2d = gr.Slider(minimum=-0.3, maximum=0.3, value=0.15, step=0.01, label="V_y", visible=True)
216
+ distribution_type_input_2d = gr.Radio(choices=["Gaussian", "Sinusoidal"], value="Sinusoidal", label="Initial Distribution Type")
217
+
218
  with gr.Column(scale=1):
219
+ gr.Markdown("## Boundary Conditions")
220
+ boundary_condition_input_2d = gr.Radio(choices=["Periodic"], value="Periodic", label="Boundary Condition")
221
+
222
+ run_qlbm_btn_2d = gr.Button("Run Simulation", variant="primary") # Button below the sections
223
+
224
+ qlbm_inputs_list_2d = [
225
+ num_reg_qubits_input_2d, time_steps_slider_2d, distribution_type_input_2d,
226
+ selected_velocity_field_2d, vx_slider_2d, vy_slider_2d, boundary_condition_input_2d
227
+ ]
228
+ run_qlbm_btn_2d.click(
229
+ fn=qlbm_gradio_interface,
230
+ inputs=qlbm_inputs_list_2d,
231
+ outputs=[qlbm_interactive_plot_2d, plotly_json_frames_state_2d] # Modified output
232
+ ).then(
233
+ lambda: gr.update(visible=True), # Make download button visible after simulation
234
+ outputs=[download_button_2d]
235
+ )
236
+ download_button_2d.click(
237
+ fn=download_plot_data,
238
+ inputs=[plotly_json_frames_state_2d],
239
+ outputs=[download_button_2d]
240
+ )
241
+ num_reg_qubits_input_2d.change(
242
+ fn=update_grid_and_qubit_info,
243
+ inputs=num_reg_qubits_input_2d,
244
+ outputs=[num_reg_qubits_input_2d, qubit_plot_2d, total_qubits_display_2d, warning_display_2d, recommended_time_steps_display_2d]
245
+ )
246
+ sinusoidal_btn_2d.click(
247
+ fn=set_sinusoidal_example,
248
+ inputs=[],
249
+ outputs=[num_reg_qubits_input_2d, time_steps_slider_2d, distribution_type_input_2d, selected_velocity_field_2d, vx_slider_2d, vy_slider_2d, boundary_condition_input_2d]
250
+ )
251
+ gaussian_btn_2d.click(
252
+ fn=set_gaussian_example,
253
+ inputs=[],
254
+ outputs=[num_reg_qubits_input_2d, time_steps_slider_2d, distribution_type_input_2d, selected_velocity_field_2d, vx_slider_2d, vy_slider_2d, boundary_condition_input_2d]
255
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
 
257
  if __name__ == "__main__":
258
  try: