import os os.environ["TORCH_DYNAMO_DISABLE"] = "1" import tempfile import numpy as np import gradio as gr from ase.io import read, write from ase.io.trajectory import Trajectory from gradio_molecule3d import Molecule3D from simulation_scripts_orbmol import load_orbmol_model, run_md_simulation, run_relaxation_simulation import hashlib # ==== Configuración Molecule3D ==== DEFAULT_MOLECULAR_REPRESENTATIONS = [ { "model": 0, "chain": "", "resname": "", "style": "sphere", "color": "Jmol", "around": 0, "byres": False, "scale": 0.3, }, { "model": 0, "chain": "", "resname": "", "style": "stick", "color": "Jmol", "around": 0, "byres": False, "scale": 0.2, }, ] DEFAULT_MOLECULAR_SETTINGS = { "backgroundColor": "white", "orthographic": False, "disableFog": False, } # ==== Conversión a PDB para Molecule3D ==== def convert_to_pdb_for_viewer(file_path): """Convierte cualquier archivo a PDB para Molecule3D""" if not file_path or not os.path.exists(file_path): return None try: atoms = read(file_path) cache_dir = os.path.join(tempfile.gettempdir(), "gradio") os.makedirs(cache_dir, exist_ok=True) pdb_path = os.path.join(cache_dir, f"mol_{hashlib.md5(file_path.encode()).hexdigest()[:12]}.pdb") write(pdb_path, atoms, format="proteindatabank") return pdb_path except Exception as e: print(f"Error converting to PDB: {e}") return None # ==== OrbMol SPE ==== def predict_molecule(structure_file, task_name, charge=0, spin_multiplicity=1): """Single Point Energy + fuerzas (OrbMol)""" try: calc = load_orbmol_model(task_name) if not structure_file: return "Error: Please upload a structure file", "Error", None file_path = structure_file if not os.path.exists(file_path): return f"Error: File not found: {file_path}", "Error", None if os.path.getsize(file_path) == 0: return f"Error: Empty file: {file_path}", "Error", None atoms = read(file_path) if task_name in ["OMol", "OMol-Direct"]: atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)} atoms.calc = calc energy = atoms.get_potential_energy() forces = atoms.get_forces() lines = [ f"Model: {task_name}", f"Total Energy: {energy:.6f} eV", "", "Atomic Forces:" ] for i, fc in enumerate(forces): lines.append(f"Atom {i+1}: [{fc[0]:.4f}, {fc[1]:.4f}, {fc[2]:.4f}] eV/Å") max_force = float(np.max(np.linalg.norm(forces, axis=1))) lines += ["", f"Max Force: {max_force:.4f} eV/Å"] pdb_file = convert_to_pdb_for_viewer(file_path) return "\n".join(lines), f"Calculation completed with {task_name}", pdb_file except Exception as e: import traceback traceback.print_exc() return f"Error during calculation: {e}", "Error", None # ==== Wrappers MD y Relax ==== def md_wrapper(structure_file, task_name, charge, spin, steps, tempK, timestep_fs, ensemble): try: if not structure_file: return ("Error: Please upload a structure file", None, "", "", "", None) traj_path, log_text, script_text, explanation = run_md_simulation( structure_file, int(steps), 20, float(timestep_fs), float(tempK), "NVT" if ensemble == "NVT" else "NVE", str(task_name), int(charge), int(spin), ) status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})" pdb_file = convert_to_pdb_for_viewer(traj_path) return (status, traj_path, log_text, script_text, explanation, pdb_file) except Exception as e: import traceback traceback.print_exc() return (f"Error: {e}", None, "", "", "", None) def relax_wrapper(structure_file, task_name, steps, fmax, charge, spin, relax_cell): try: if not structure_file: return ("Error: Please upload a structure file", None, "", "", "", None) traj_path, log_text, script_text, explanation = run_relaxation_simulation( structure_file, int(steps), float(fmax), str(task_name), int(charge), int(spin), bool(relax_cell), ) status = f"Relaxation finished (<={int(steps)} steps, fmax={float(fmax)} eV/Å)" pdb_file = convert_to_pdb_for_viewer(traj_path) return (status, traj_path, log_text, script_text, explanation, pdb_file) except Exception as e: import traceback traceback.print_exc() return (f"Error: {e}", None, "", "", "", None) # ==== UI ==== with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo: with gr.Tabs(): # -------- SPE -------- with gr.Tab("Single Point Energy"): with gr.Row(): with gr.Column(scale=2): gr.Markdown("# OrbMol — Quantum-Accurate Molecular Predictions") gr.Markdown("**Supported formats:** .xyz, .pdb, .cif, .traj, .mol, .sdf") xyz_input = gr.File( label="Upload Structure File", file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"], file_count="single" ) task_name_spe = gr.Radio( ["OMol", "OMat", "OMol-Direct"], value="OMol", label="Model Type" ) with gr.Row(): charge_input = gr.Slider(-10, 10, 0, step=1, label="Charge") spin_input = gr.Slider(1, 11, 1, step=1, label="Spin Multiplicity") run_spe = gr.Button("Run OrbMol Prediction", variant="primary") with gr.Column(variant="panel", min_width=500): spe_out = gr.Textbox(label="Energy & Forces", lines=15, interactive=False) spe_status = gr.Textbox(label="Status", interactive=False) spe_viewer = Molecule3D( label="Input Structure Viewer", reps=DEFAULT_MOLECULAR_REPRESENTATIONS, config=DEFAULT_MOLECULAR_SETTINGS ) task_name_spe.change( lambda x: ( gr.update(visible=x in ["OMol", "OMol-Direct"]), gr.update(visible=x in ["OMol", "OMol-Direct"]) ), [task_name_spe], [charge_input, spin_input] ) run_spe.click( predict_molecule, [xyz_input, task_name_spe, charge_input, spin_input], [spe_out, spe_status, spe_viewer] ) # -------- MD -------- with gr.Tab("Molecular Dynamics"): with gr.Row(): with gr.Column(scale=2): gr.Markdown("## Molecular Dynamics Simulation") xyz_md = gr.File( label="Upload Structure File", file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"], file_count="single" ) task_name_md = gr.Radio( ["OMol", "OMat", "OMol-Direct"], value="OMol", label="Model Type" ) with gr.Row(): charge_md = gr.Slider(-10, 10, 0, step=1, label="Charge") spin_md = gr.Slider(1, 11, 1, step=1, label="Spin Multiplicity") with gr.Row(): steps_md = gr.Slider(10, 2000, 100, step=10, label="Steps") temp_md = gr.Slider(10, 1500, 300, step=10, label="Temperature (K)") with gr.Row(): timestep_md = gr.Slider(0.1, 5.0, 1.0, step=0.1, label="Timestep (fs)") ensemble_md = gr.Radio(["NVE", "NVT"], value="NVE", label="Ensemble") run_md_btn = gr.Button("Run MD Simulation", variant="primary") with gr.Column(variant="panel", min_width=520): md_status = gr.Textbox(label="MD Status", interactive=False) md_traj = gr.File(label="Trajectory (.traj)", interactive=False) md_viewer = Molecule3D( label="MD Result Viewer", reps=DEFAULT_MOLECULAR_REPRESENTATIONS, config=DEFAULT_MOLECULAR_SETTINGS ) md_log = gr.Textbox(label="Log", interactive=False, lines=15) md_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20) md_explain = gr.Markdown() task_name_md.change( lambda x: ( gr.update(visible=x in ["OMol", "OMol-Direct"]), gr.update(visible=x in ["OMol", "OMol-Direct"]) ), [task_name_md], [charge_md, spin_md] ) run_md_btn.click( md_wrapper, [xyz_md, task_name_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md], [md_status, md_traj, md_log, md_script, md_explain, md_viewer] ) # -------- Relax -------- with gr.Tab("Relaxation / Optimization"): with gr.Row(): with gr.Column(scale=2): gr.Markdown("## Structure Relaxation/Optimization") xyz_rlx = gr.File( label="Upload Structure File", file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"], file_count="single" ) task_name_rlx = gr.Radio( ["OMol", "OMat", "OMol-Direct"], value="OMol", label="Model Type" ) with gr.Row(): steps_rlx = gr.Slider(1, 2000, 300, step=1, label="Max Steps") fmax_rlx = gr.Slider(0.001, 0.5, 0.05, step=0.001, label="Fmax (eV/Å)") with gr.Row(): charge_rlx = gr.Slider(-10, 10, 0, step=1, label="Charge") spin_rlx = gr.Slider(1, 11, 1, step=1, label="Spin") relax_cell = gr.Checkbox(False, label="Relax Unit Cell") run_rlx_btn = gr.Button("Run Optimization", variant="primary") with gr.Column(variant="panel", min_width=520): rlx_status = gr.Textbox(label="Status", interactive=False) rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False) rlx_viewer = Molecule3D( label="Optimized Structure Viewer", reps=DEFAULT_MOLECULAR_REPRESENTATIONS, config=DEFAULT_MOLECULAR_SETTINGS ) rlx_log = gr.Textbox(label="Log", interactive=False, lines=15) rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20) rlx_explain = gr.Markdown() task_name_rlx.change( lambda x: ( gr.update(visible=x in ["OMol", "OMol-Direct"]), gr.update(visible=x in ["OMol", "OMol-Direct"]) ), [task_name_rlx], [charge_rlx, spin_rlx] ) run_rlx_btn.click( relax_wrapper, [xyz_rlx, task_name_rlx, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell], [rlx_status, rlx_traj, rlx_log, rlx_script, rlx_explain, rlx_viewer] ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)