# 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 "