harishaseebat92 commited on
Commit
c27d6ee
·
1 Parent(s): 2dbe6b1

pp now boots safely on Spaces without tripping the LLVM SmallVector assertion

Browse files
Files changed (2) hide show
  1. README.md +16 -0
  2. qlbm.py +64 -53
README.md CHANGED
@@ -32,3 +32,19 @@ reverse proxy. Set the following environment variables (the Dockerfile already d
32
 
33
  The bundled `docker/nginx.conf` proxies incoming traffic on `${PORT:-7860}` to the
34
  individual services so the browser never tries to contact `127.0.0.1` directly.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  The bundled `docker/nginx.conf` proxies incoming traffic on `${PORT:-7860}` to the
34
  individual services so the browser never tries to contact `127.0.0.1` directly.
35
+
36
+ ## CUDA-Q backend on CPU-only hosts
37
+
38
+ The 3D lattice Boltzmann solver relies on CUDA-Q, which expects a CUDA-capable GPU.
39
+ CPU-only runtimes such as Hugging Face Spaces will now automatically skip loading the
40
+ quantum backend so the UI can still boot. Use the following environment variables to
41
+ override the default:
42
+
43
+ | Variable | Effect |
44
+ | --- | --- |
45
+ | `DISABLE_CUDAQ=1` | Force-disable the CUDA-Q backend regardless of GPU availability. |
46
+ | `FORCE_ENABLE_CUDAQ=1` | Attempt to load the CUDA-Q backend even if the platform was auto-detected as CPU-only. |
47
+
48
+ When the backend is disabled the "Run Simulation" button is greyed out and a warning
49
+ appears in the UI explaining why. Run the app locally on a CUDA-enabled machine and
50
+ set `FORCE_ENABLE_CUDAQ=1` if you still want to attempt a run on a constrained host.
qlbm.py CHANGED
@@ -1,18 +1,9 @@
1
  import os
2
  os.environ["OMP_NUM_THREADS"] = "1"
3
 
4
- # Defer cudaq import to avoid LLVM crash on CPU-only environments
5
- _cudaq_available = False
6
- try:
7
- import cudaq
8
- _cudaq_available = True
9
- except ImportError:
10
- pass
11
-
12
  import numpy as np
13
  import math
14
  import time
15
- import shutil
16
  import plotly.graph_objects as go
17
  import pyvista as pv
18
  from trame.app import get_server
@@ -22,46 +13,61 @@ from trame.widgets import html # <--- IMPORT ADDED HERE
22
  from trame_plotly.widgets import plotly as plotly_widgets
23
  from pyvista.trame.ui import plotter_ui
24
 
25
- # Defer import of fluid3d_pyvista to avoid triggering cudaq on CPU-only environments
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  simulate_qlbm_3D_and_animate = None
 
27
 
28
- def _detect_gpu_available():
29
- """Check if NVIDIA GPU is available."""
30
- nvidia_devices = os.environ.get("NVIDIA_VISIBLE_DEVICES")
31
- cuda_devices = os.environ.get("CUDA_VISIBLE_DEVICES")
32
-
33
- def _has_devices(val):
34
- if not val:
35
- return False
36
- return val.strip().lower() not in ("", "none", "nodevfiles", "-1")
37
-
38
- has_env = _has_devices(nvidia_devices) or _has_devices(cuda_devices)
39
- has_proc = os.path.exists("/proc/driver/nvidia/version")
40
- has_smi = shutil.which("nvidia-smi") is not None
41
- return has_env or has_proc or has_smi
42
-
43
- def _lazy_import_simulation():
44
- """Lazily import simulation module only when GPU is available."""
45
- global simulate_qlbm_3D_and_animate
46
- if simulate_qlbm_3D_and_animate is not None:
47
- return simulate_qlbm_3D_and_animate
48
-
49
- if not _detect_gpu_available():
50
- print("Warning: No GPU detected, simulation module not loaded.")
51
- return None
52
-
53
  try:
54
- from fluid3d_pyvista import simulate_qlbm_3D_and_animate as sim_func
55
- simulate_qlbm_3D_and_animate = sim_func
56
- return sim_func
57
  except ImportError:
58
  try:
59
- from quantum.fluid3d_pyvista import simulate_qlbm_3D_and_animate as sim_func
60
- simulate_qlbm_3D_and_animate = sim_func
61
- return sim_func
62
- except ImportError:
63
- print("Warning: fluid3d_pyvista not found.")
64
- return None
 
 
 
 
 
 
 
 
65
 
66
  # --- Server Setup ---
67
  server = get_server()
@@ -115,6 +121,8 @@ state.update({
115
  "simulation_has_run": False,
116
  "time_val": 0,
117
  "max_time_step": 0,
 
 
118
  # Workflow guidance styles
119
  "workflow_step": 0,
120
  "overview_card_style": _WORKFLOW_BASE_STYLE,
@@ -508,13 +516,8 @@ def update_geometry_view():
508
  def run_simulation():
509
  global simulation_data_frames, simulation_times, current_grid_object
510
 
511
- # Lazy import with GPU check
512
- sim_func = _lazy_import_simulation()
513
- if sim_func is None:
514
- if not _detect_gpu_available():
515
- state.run_error = "Simulation requires NVIDIA GPU which is not available in this environment."
516
- else:
517
- state.run_error = "Simulation module not found."
518
  return
519
 
520
  state.is_running = True
@@ -537,7 +540,7 @@ def run_simulation():
537
  # 2. Run Simulation
538
  # Note: simulate_qlbm_3D_and_animate updates the passed plotter with the first frame
539
  plotter.clear()
540
- _, frames, times, grid_obj = sim_func(
541
  num_reg_qubits=num_reg_qubits,
542
  T=T,
543
  distribution_type=distribution_type,
@@ -1004,10 +1007,18 @@ with SinglePageLayout(server) as layout:
1004
  text="Run Simulation",
1005
  color="primary",
1006
  block=True,
1007
- disabled=("is_running", False),
1008
  click=run_simulation,
1009
  style=("is_running ? '' : 'background-color:#87CEFA;'", ""),
1010
  )
 
 
 
 
 
 
 
 
1011
  with vuetify3.VRow(dense=True, classes="mt-2"):
1012
  with vuetify3.VCol(cols=6):
1013
  vuetify3.VBtn(
 
1
  import os
2
  os.environ["OMP_NUM_THREADS"] = "1"
3
 
 
 
 
 
 
 
 
 
4
  import numpy as np
5
  import math
6
  import time
 
7
  import plotly.graph_objects as go
8
  import pyvista as pv
9
  from trame.app import get_server
 
13
  from trame_plotly.widgets import plotly as plotly_widgets
14
  from pyvista.trame.ui import plotter_ui
15
 
16
+
17
+ def _env_flag(name: str) -> bool:
18
+ value = os.environ.get(name)
19
+ return str(value).lower() in {"1", "true", "yes", "on"} if value is not None else False
20
+
21
+
22
+ def _should_disable_quantum_backend():
23
+ if _env_flag("FORCE_ENABLE_CUDAQ"):
24
+ return False, ""
25
+
26
+ reasons = []
27
+ if _env_flag("DISABLE_CUDAQ"):
28
+ reasons.append("disabled via DISABLE_CUDAQ=1")
29
+
30
+ hf_space = os.environ.get("SPACE_ID") or os.environ.get("HF_SPACE_ID")
31
+ if hf_space:
32
+ reasons.append("running inside Hugging Face Spaces (CPU runtime)")
33
+
34
+ cuda_visible = os.environ.get("CUDA_VISIBLE_DEVICES")
35
+ if cuda_visible is not None and cuda_visible.strip() in {"", "-1"}:
36
+ reasons.append("no CUDA device exposed (CUDA_VISIBLE_DEVICES is empty)")
37
+
38
+ nvidia_visible = os.environ.get("NVIDIA_VISIBLE_DEVICES")
39
+ if nvidia_visible is not None and nvidia_visible.strip() in {"", "void"}:
40
+ reasons.append("no NVIDIA device exposed (NVIDIA_VISIBLE_DEVICES is empty)")
41
+
42
+ if reasons:
43
+ msg = "; ".join(reasons)
44
+ return True, f"Quantum backend disabled because {msg}. Set FORCE_ENABLE_CUDAQ=1 to override."
45
+
46
+ return False, ""
47
+
48
+
49
  simulate_qlbm_3D_and_animate = None
50
+ _SIMULATION_BACKEND_DISABLED, _SIMULATION_DISABLED_REASON = _should_disable_quantum_backend()
51
 
52
+ if not _SIMULATION_BACKEND_DISABLED:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  try:
54
+ from fluid3d_pyvista import simulate_qlbm_3D_and_animate # type: ignore
 
 
55
  except ImportError:
56
  try:
57
+ from quantum.fluid3d_pyvista import simulate_qlbm_3D_and_animate # type: ignore
58
+ except ImportError as exc:
59
+ _SIMULATION_DISABLED_REASON = "Simulation module not found."
60
+ simulate_qlbm_3D_and_animate = None
61
+ print(f"Warning: {exc}")
62
+ except Exception as exc: # pragma: no cover - defensive guard for HF runtime
63
+ _SIMULATION_DISABLED_REASON = f"Failed to load CUDA-Q backend: {exc}"
64
+ simulate_qlbm_3D_and_animate = None
65
+ else:
66
+ _SIMULATION_DISABLED_REASON = _SIMULATION_DISABLED_REASON or "Quantum backend disabled by environment settings."
67
+
68
+ _SIMULATION_BACKEND_READY = simulate_qlbm_3D_and_animate is not None
69
+ if not _SIMULATION_BACKEND_READY and _SIMULATION_DISABLED_REASON:
70
+ print(f"Warning: {_SIMULATION_DISABLED_REASON}")
71
 
72
  # --- Server Setup ---
73
  server = get_server()
 
121
  "simulation_has_run": False,
122
  "time_val": 0,
123
  "max_time_step": 0,
124
+ "simulation_backend_ready": _SIMULATION_BACKEND_READY,
125
+ "simulation_disabled_reason": _SIMULATION_DISABLED_REASON,
126
  # Workflow guidance styles
127
  "workflow_step": 0,
128
  "overview_card_style": _WORKFLOW_BASE_STYLE,
 
516
  def run_simulation():
517
  global simulation_data_frames, simulation_times, current_grid_object
518
 
519
+ if simulate_qlbm_3D_and_animate is None:
520
+ state.run_error = _SIMULATION_DISABLED_REASON or "Simulation module is not available on this platform."
 
 
 
 
 
521
  return
522
 
523
  state.is_running = True
 
540
  # 2. Run Simulation
541
  # Note: simulate_qlbm_3D_and_animate updates the passed plotter with the first frame
542
  plotter.clear()
543
+ _, frames, times, grid_obj = simulate_qlbm_3D_and_animate(
544
  num_reg_qubits=num_reg_qubits,
545
  T=T,
546
  distribution_type=distribution_type,
 
1007
  text="Run Simulation",
1008
  color="primary",
1009
  block=True,
1010
+ disabled=("is_running || !simulation_backend_ready", False),
1011
  click=run_simulation,
1012
  style=("is_running ? '' : 'background-color:#87CEFA;'", ""),
1013
  )
1014
+ vuetify3.VAlert(
1015
+ v_if="simulation_disabled_reason",
1016
+ type="warning",
1017
+ variant="tonal",
1018
+ density="compact",
1019
+ children=["{{ simulation_disabled_reason }}"],
1020
+ classes="mt-2",
1021
+ )
1022
  with vuetify3.VRow(dense=True, classes="mt-2"):
1023
  with vuetify3.VCol(cols=6):
1024
  vuetify3.VBtn(