Spaces:
Running on Zero
Running on Zero
File size: 8,022 Bytes
6ac9514 b49b2a8 6ac9514 9dede68 6ac9514 b49b2a8 6ac9514 b49b2a8 6ac9514 9dede68 6ac9514 9dede68 873d39c 9dede68 873d39c 9dede68 b49b2a8 7691a82 9a83e3d 7691a82 9a83e3d 7691a82 9a83e3d 9dede68 873d39c 9dede68 6ac9514 b49b2a8 6ac9514 9dede68 b49b2a8 7691a82 b670bbb 7691a82 b670bbb 7691a82 b670bbb 7691a82 ab3f0b9 7691a82 6ac9514 873d39c ab3f0b9 7691a82 ab3f0b9 9dede68 6ac9514 9a83e3d 6ac9514 9dede68 6ac9514 b49b2a8 6ac9514 b49b2a8 6ac9514 873d39c 6ac9514 9dede68 6ac9514 | 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 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | # Hugging Face ZeroGPU: import spaces at the top of the file
try:
import spaces
GPU = spaces.GPU
except Exception:
def GPU(*args, **kwargs):
def decorator(fn):
return fn
return decorator
import os
import sys
from pathlib import Path
import json
import traceback
import pandas as pd
import io
# Configure paths
ROOT_DIR = Path(__file__).resolve().parent
BACKEND_DIR = ROOT_DIR / "backend"
for d in [str(ROOT_DIR), str(BACKEND_DIR)]:
if d not in sys.path:
sys.path.insert(0, d)
# Import solver and backend handlers
try:
from backend.cv_solver import solve_cv
except ImportError:
try:
from cv_solver import solve_cv
except ImportError:
from FD_solver import solve_cv
# Top-level @spaces.GPU function registered with Gradio for ZeroGPU A100 allocation
@GPU(duration=120)
def gradio_solve_cv(file_content: str, config_json: str):
"""ZeroGPU registered execution point for JAX optimization."""
try:
if not file_content or not file_content.strip():
return json.dumps({"type": "error", "message": "No CSV data file content provided. Please upload a cyclic voltammetry data file."})
raw_config = json.loads(config_json) if isinstance(config_json, str) else (config_json or {})
config = {
"scan_rate_v_s": float(raw_config.get("scan_rate", 0.010)),
"film_thickness": float(raw_config.get("film_thickness", 1e-4)),
"v_min": float(raw_config.get("v_min", -1.0)),
"v_max": float(raw_config.get("v_max", 1.0)),
"skip_factor": int(raw_config.get("skip_factor", 5)),
"num_peaks": int(raw_config.get("num_peaks", 50)),
"max_iter": int(raw_config.get("max_iter", 100)),
"tol_ftol": float(raw_config.get("tol_ftol", 1e-8)),
"tol_gtol": float(raw_config.get("tol_gtol", 1e-7)),
"num_terms": int(raw_config.get("num_terms", 50)),
"loss_weight_const": float(raw_config.get("loss_weight_const", 1.0))
}
pot_col = int(raw_config.get("pot_col", 0))
cur_col = int(raw_config.get("cur_col", 1))
df = pd.read_csv(io.StringIO(file_content), sep=None, engine='python')
result_dict = solve_cv(df, config, pot_col, cur_col, queue=None, loop=None)
return json.dumps({
"type": "done",
"params": {
"D0": result_dict["parameters"]["diffusivity"],
"Vc": result_dict["parameters"]["v_center"],
"beta_L": result_dict["parameters"]["beta_left"],
"beta_R": result_dict["parameters"]["beta_right"],
"I_offset": result_dict["parameters"]["baseline_offset"]
},
"plots": {
"v_plot": result_dict["plots"]["v_plot"],
"d_of_v": result_dict["plots"]["d_of_v"],
"dos_total": result_dict["plots"]["dos_total"],
"dos_peaks": result_dict["plots"]["dos_matrix"],
"exp_potential": result_dict["plots"]["exp_potential"],
"exp_current": result_dict["plots"]["exp_current"],
"sim_current": result_dict["plots"]["sim_current"]
},
"total_iterations": 100
})
except Exception as e:
return json.dumps({
"type": "error",
"message": str(e),
"trace": traceback.format_exc()
})
def get_app_assets():
"""Load index.html, style.css, and app.js."""
index_file = ROOT_DIR / "index.html"
css_file = ROOT_DIR / "style.css"
js_file = ROOT_DIR / "app.js"
html_content = index_file.read_text(encoding="utf-8") if index_file.exists() else "<h2>Dashboard Loading...</h2>"
css_content = css_file.read_text(encoding="utf-8") if css_file.exists() else ""
js_content = js_file.read_text(encoding="utf-8") if js_file.exists() else ""
# Inline CSS & JS for 100% self-contained execution
inlined = html_content
inlined = inlined.replace('<link rel="stylesheet" href="style.css">', f"<style>\n{css_content}\n</style>")
inlined = inlined.replace('<script src="app.js"></script>', f"<script>\n{js_content}\n</script>")
return inlined, css_content, js_content
inlined_html, app_css, app_js = get_app_assets()
# Health check helper
def health_info():
import jax
devices = [str(d) for d in jax.devices()]
return {
"status": "ok",
"engine": "JAX Auto-Diff Hardware Accelerated Engine",
"devices": devices
}
# Gradio integration for Hugging Face ZeroGPU
has_gradio = False
try:
import gradio as gr
from fastapi.middleware.cors import CORSMiddleware
from fastapi.requests import Request
from fastapi.responses import JSONResponse
head_html = f"""
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root, html, body {{
color-scheme: dark !important;
background-color: #0b1120 !important;
}}
{app_css}
footer {{visibility: hidden !important; display: none !important;}}
.gradio-container {{
max-width: 100% !important;
padding: 0 !important;
margin: 0 !important;
background: #0b1120 !important;
color: #e2e8f0 !important;
}}
.prose {{max-width: 100% !important;}}
.hidden-bridge {{position: absolute !important; opacity: 0 !important; pointer-events: none !important; height: 0 !important; width: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; border: none !important;}}
</style>
"""
with gr.Blocks(title="Cyclic Voltammetry Parameter Extraction & Physical Model Fitting", head=head_html, js=app_js) as demo:
# Native Gradio components for ZeroGPU hardware event dispatching
gr_input_file = gr.Textbox(value="", elem_id="gr_input_file", elem_classes=["hidden-bridge"])
gr_input_config = gr.Textbox(value="{}", elem_id="gr_input_config", elem_classes=["hidden-bridge"])
gr_output_json = gr.Textbox(value="", elem_id="gr_output_json", elem_classes=["hidden-bridge"])
gr_trigger_btn = gr.Button("Execute ZeroGPU", elem_id="gr_trigger_btn", elem_classes=["hidden-bridge"])
gr.HTML(inlined_html)
gr_trigger_btn.click(
fn=gradio_solve_cv,
inputs=[gr_input_file, gr_input_config],
outputs=[gr_output_json]
)
# Attach FastAPI routes directly to demo.app
demo.app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
async def api_solve_handler(request: Request):
data = await request.json()
file_content = data.get("file_content", "")
config = data.get("config", {})
res_str = gradio_solve_cv(file_content, json.dumps(config))
return JSONResponse(content=json.loads(res_str))
demo.app.add_api_route("/health", health_info, methods=["GET"])
demo.app.add_api_route("/api/health", health_info, methods=["GET"])
demo.app.add_api_route("/api/solve", api_solve_handler, methods=["POST"])
demo.app.add_api_route("/solve", api_solve_handler, methods=["POST"])
has_gradio = True
except ImportError:
has_gradio = False
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
if has_gradio:
print(f"Starting Gradio server on port {port}...")
demo.queue().launch(server_name="0.0.0.0", server_port=port, share=False)
else:
import uvicorn
from fastapi import FastAPI
fastapi_app = FastAPI()
fastapi_app.add_api_route("/health", health_info, methods=["GET"])
print(f"Starting FastAPI server on port {port}...")
uvicorn.run(fastapi_app, host="0.0.0.0", port=port) |