annabossler commited on
Commit
3f17e4b
·
verified ·
1 Parent(s): bdb4e68

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -261
app.py CHANGED
@@ -5,145 +5,106 @@ import numpy as np
5
  import gradio as gr
6
  from ase.io import read, write
7
  from ase.io.trajectory import Trajectory
 
8
  import hashlib
9
  import shutil
10
 
11
- # ==== Test de gradio_molecule3d ====
12
- try:
13
- from gradio_molecule3d import Molecule3D
14
- MOLECULE3D_AVAILABLE = True
15
- print("✅ gradio_molecule3d imported successfully")
16
- except ImportError as e:
17
- MOLECULE3D_AVAILABLE = False
18
- print(f"❌ gradio_molecule3d import failed: {e}")
19
-
20
- # ==== PDB creation for Molecule3D ====
21
- def create_pdb_for_molecule3d(atoms):
22
- """Create a PDB file specifically for Molecule3D viewer"""
23
  try:
24
- # Create gradio cache directory
25
- cache_dir = os.path.join(tempfile.gettempdir(), "gradio")
26
- os.makedirs(cache_dir, exist_ok=True)
27
- print(f"Cache directory: {cache_dir}")
28
-
29
- # Create unique filename based on atomic positions
30
- pos_hash = hashlib.md5(atoms.get_positions().tobytes()).hexdigest()[:12]
31
- pdb_filename = f"mol_{pos_hash}.pdb"
32
- pdb_path = os.path.join(cache_dir, pdb_filename)
33
- print(f"Creating PDB: {pdb_path}")
34
-
35
- # Write PDB file using ASE
36
- write(pdb_path, atoms, format='pdb')
37
-
38
- # Verify file was created and has content
39
- if os.path.exists(pdb_path):
40
- file_size = os.path.getsize(pdb_path)
41
- print(f"PDB created successfully: {file_size} bytes")
42
-
43
- # Read and log first few lines for verification
44
- with open(pdb_path, 'r') as f:
45
- first_lines = f.read(200)
46
- print(f"PDB preview: {first_lines[:100]}...")
47
-
48
- if file_size > 0:
49
- return pdb_path
50
-
51
- print("PDB file was not created or is empty")
 
 
 
 
 
 
 
 
 
 
 
52
  return None
53
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  except Exception as e:
55
- print(f"Error creating PDB for Molecule3D: {e}")
56
- import traceback
57
- traceback.print_exc()
58
  return None
59
 
60
- # ==== Simple molecule viewer using HTML + 3Dmol.js ====
61
- def create_simple_molecule_viewer(atoms_or_pdb_path, viewer_id="mol_viewer"):
62
- """Create a simple 3D molecule viewer using 3Dmol.js"""
63
-
64
- # If it's a path, read atoms
65
- if isinstance(atoms_or_pdb_path, str) and os.path.exists(atoms_or_pdb_path):
66
- try:
67
- atoms = read(atoms_or_pdb_path)
68
- except:
69
- return "<div style='color:red;'>Error reading file</div>"
70
- else:
71
- atoms = atoms_or_pdb_path
72
-
73
- # Convert to XYZ format
74
- symbols = atoms.get_chemical_symbols()
75
- coords = atoms.get_positions()
76
-
77
- xyz_content = f"{len(symbols)}\nMolecule\n"
78
- for s, (x, y, z) in zip(symbols, coords):
79
- xyz_content += f"{s} {x:.6f} {y:.6f} {z:.6f}\n"
80
-
81
- # Escape for JavaScript
82
- xyz_escaped = xyz_content.replace('\n', '\\n').replace('"', '\\"')
83
-
84
- html = f"""
85
- <div style="margin:10px; padding:15px; border:2px solid #ddd; border-radius:10px; background:#f9f9f9;">
86
- <div style="margin-bottom:10px; font-weight:bold; color:#333;">
87
- 🧬 Molecule Viewer ({len(symbols)} atoms)
88
- </div>
89
- <div id="{viewer_id}" style="width:100%; height:400px; background:white; border:1px solid #ccc; border-radius:5px;"></div>
90
- </div>
91
-
92
- <script>
93
- // Load 3Dmol if not already loaded
94
- if (typeof window.$3Dmol === 'undefined') {{
95
- console.log('Loading 3Dmol.js...');
96
- var script = document.createElement('script');
97
- script.src = 'https://3dmol.org/build/3Dmol-min.js';
98
- script.onload = function() {{
99
- console.log('3Dmol.js loaded, initializing viewer...');
100
- setTimeout(function() {{ init_{viewer_id}(); }}, 100);
101
- }};
102
- document.head.appendChild(script);
103
- }} else {{
104
- console.log('3Dmol.js already available, initializing viewer...');
105
- init_{viewer_id}();
106
- }}
107
-
108
- function init_{viewer_id}() {{
109
- try {{
110
- var element = document.getElementById('{viewer_id}');
111
- if (!element) {{
112
- console.error('Element {viewer_id} not found');
113
- return;
114
- }}
115
-
116
- if (typeof $3Dmol === 'undefined') {{
117
- console.error('$3Dmol is undefined');
118
- return;
119
- }}
120
-
121
- console.log('Creating 3Dmol viewer...');
122
- var viewer = $3Dmol.createViewer(element, {{
123
- backgroundColor: 'white',
124
- antialias: true
125
- }});
126
-
127
- var xyz_data = "{xyz_escaped}";
128
- console.log('Adding molecule model...');
129
- viewer.addModel(xyz_data, "xyz");
130
- viewer.setStyle({{}}, {{
131
- stick: {{radius: 0.2, colorscheme: 'Jmol'}},
132
- sphere: {{radius: 0.4, colorscheme: 'Jmol'}}
133
- }});
134
-
135
- viewer.zoomTo();
136
- viewer.render();
137
- console.log('Molecule viewer initialized successfully');
138
-
139
- }} catch (error) {{
140
- console.error('Error initializing molecule viewer:', error);
141
- document.getElementById('{viewer_id}').innerHTML = '<div style="color:red; padding:20px;">Error loading 3D viewer: ' + error.message + '</div>';
142
- }}
143
- }}
144
- </script>
145
- """
146
- return html
147
 
148
  # ==== OrbMol SPE ====
149
  from orb_models.forcefield import pretrained
@@ -159,47 +120,20 @@ def _load_orbmol_calc():
159
  _MODEL_CALC = ORBCalculator(orbff, device="cpu")
160
  return _MODEL_CALC
161
 
162
- def create_pdb_for_molecule3d(atoms):
163
- """Create a PDB file specifically for Molecule3D viewer"""
164
- try:
165
- # Create gradio cache directory
166
- cache_dir = os.path.join(tempfile.gettempdir(), "gradio")
167
- os.makedirs(cache_dir, exist_ok=True)
168
-
169
- # Create unique filename
170
- unique_id = hashlib.md5(str(atoms.get_positions().tostring()).encode()).hexdigest()[:12]
171
- pdb_filename = f"mol_{unique_id}.pdb"
172
- pdb_path = os.path.join(cache_dir, pdb_filename)
173
-
174
- # Write PDB file
175
- write(pdb_path, atoms, format='pdb')
176
-
177
- # Verify file was created
178
- if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
179
- print(f"✅ PDB created for Molecule3D: {pdb_path} ({os.path.getsize(pdb_path)} bytes)")
180
- return pdb_path
181
- else:
182
- print("❌ PDB file was not created properly")
183
- return None
184
-
185
- except Exception as e:
186
- print(f"❌ Error creating PDB: {e}")
187
- return None
188
-
189
  def predict_molecule(structure_file, charge=0, spin_multiplicity=1):
190
- """Single Point Energy + fuerzas (OrbMol)"""
 
 
191
  try:
192
  calc = _load_orbmol_calc()
193
  if not structure_file:
194
- return "Error: Please upload a structure file", "Error", "", None if MOLECULE3D_AVAILABLE else ""
195
 
196
  file_path = structure_file
197
-
198
  if not os.path.exists(file_path):
199
- return f"Error: File not found: {file_path}", "Error", "", None if MOLECULE3D_AVAILABLE else ""
200
-
201
  if os.path.getsize(file_path) == 0:
202
- return f"Error: Empty file: {file_path}", "Error", "", None if MOLECULE3D_AVAILABLE else ""
203
 
204
  atoms = read(file_path)
205
  atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
@@ -214,37 +148,25 @@ def predict_molecule(structure_file, charge=0, spin_multiplicity=1):
214
  max_force = float(np.max(np.linalg.norm(forces, axis=1)))
215
  lines += ["", f"Max Force: {max_force:.4f} eV/Å"]
216
 
217
- # Create debug info
218
- symbols = atoms.get_chemical_symbols()
219
- debug_info = f"Molecule: {len(atoms)} atoms\nElements: {', '.join(set(symbols))}\nFormula: {''.join(sorted(symbols))}"
220
-
221
- # Create viewer
222
- if MOLECULE3D_AVAILABLE:
223
- pdb_path = create_pdb_for_molecule3d(atoms)
224
- if pdb_path:
225
- debug_info += f"\nPDB created: ✅ {os.path.basename(pdb_path)}"
226
- else:
227
- debug_info += f"\nPDB creation: ❌ Failed"
228
- return "\n".join(lines), "Calculation completed with OrbMol", debug_info, pdb_path
229
- else:
230
- html_viewer = create_simple_molecule_viewer(atoms, f"spe_viewer_{hash(file_path) % 10000}")
231
- return "\n".join(lines), "Calculation completed with OrbMol", debug_info, html_viewer
232
  except Exception as e:
233
- error_msg = f"Error during calculation: {e}"
234
- import traceback
235
- traceback.print_exc()
236
- return error_msg, "Error", str(e), None if MOLECULE3D_AVAILABLE else ""
237
 
238
- # ==== Simulaciones ====
239
  from simulation_scripts_orbmol import (
240
  run_md_simulation,
241
  run_relaxation_simulation,
242
  )
243
 
 
244
  def md_wrapper(structure_file, charge, spin, steps, tempK, timestep_fs, ensemble):
245
  try:
246
  if not structure_file:
247
- return ("Error: Please upload a structure file", None, "", "", "", None if MOLECULE3D_AVAILABLE else "", "")
248
 
249
  file_path = structure_file
250
  print(f"MD Wrapper: Processing {file_path}")
@@ -252,7 +174,7 @@ def md_wrapper(structure_file, charge, spin, steps, tempK, timestep_fs, ensemble
252
  traj_path, log_text, script_text, explanation = run_md_simulation(
253
  file_path,
254
  int(steps),
255
- 20,
256
  float(timestep_fs),
257
  float(tempK),
258
  "NVT" if ensemble == "NVT" else "NVE",
@@ -260,71 +182,62 @@ def md_wrapper(structure_file, charge, spin, steps, tempK, timestep_fs, ensemble
260
  int(spin),
261
  )
262
  status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})"
263
-
264
  print(f"MD completed, trajectory: {traj_path}")
265
-
266
- if MOLECULE3D_AVAILABLE:
267
- # Read last frame from trajectory
268
- try:
269
- traj = Trajectory(traj_path)
270
- last_atoms = traj[-1]
271
- print(f"Read trajectory: {len(traj)} frames, last frame has {len(last_atoms)} atoms")
272
-
273
- pdb_path = create_pdb_for_molecule3d(last_atoms)
274
-
275
- debug_info = f"Trajectory: {len(traj)} frames\nLast frame: {len(last_atoms)} atoms"
276
- if pdb_path:
277
- debug_info += f"\nPDB: ✅ {os.path.basename(pdb_path)}"
278
- else:
279
- debug_info += f"\nPDB: ❌ Creation failed"
280
-
281
- return (status, traj_path, log_text, script_text, explanation, pdb_path, debug_info)
282
-
283
- except Exception as traj_error:
284
- print(f"Error reading trajectory: {traj_error}")
285
- debug_info = f"Trajectory error: {traj_error}"
286
- return (status, traj_path, log_text, script_text, explanation, None, debug_info)
287
- else:
288
- # Create HTML viewer from last frame
289
- traj = Trajectory(traj_path)
290
- last_atoms = traj[-1]
291
- html_viewer = create_simple_molecule_viewer(last_atoms, f"md_viewer_{hash(traj_path) % 10000}")
292
- debug_info = f"Trajectory: {traj_path}, frames: {len(traj)}"
293
- return (status, traj_path, log_text, script_text, explanation, html_viewer, debug_info)
294
 
295
  except Exception as e:
296
- error_msg = f"Error: {e}"
297
  print(f"MD Wrapper Error: {e}")
298
- import traceback
299
- traceback.print_exc()
300
- return (error_msg, None, "", "", "", None if MOLECULE3D_AVAILABLE else "", str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
  # ==== UI ====
303
- with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo - Debug") as demo:
304
-
305
- # Header with debug info
306
- gr.Markdown(f"""
307
- # OrbMol Demo - Debug Version
308
-
309
- **System Status:**
310
- - gradio_molecule3d available: {'✅ YES' if MOLECULE3D_AVAILABLE else '❌ NO'}
311
- - Fallback viewer: {'❌ NO' if MOLECULE3D_AVAILABLE else '✅ HTML + 3Dmol.js'}
312
- """)
313
-
314
  with gr.Tabs():
315
  # -------- SPE --------
316
  with gr.Tab("Single Point Energy"):
317
  with gr.Row():
318
  with gr.Column(scale=2):
319
- gr.Markdown("## OrbMol — Single Point Energy")
320
- gr.Markdown("Upload molecular structure files for energy and force calculations.")
321
 
322
  xyz_input = gr.File(
323
  label="Upload Structure File (.xyz/.pdb/.cif/.traj)",
324
  file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
325
  file_count="single"
326
  )
327
-
328
  with gr.Row():
329
  charge_input = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
330
  spin_input = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin Multiplicity")
@@ -333,36 +246,37 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo - Debug") as demo:
333
  with gr.Column(variant="panel", min_width=500):
334
  spe_out = gr.Textbox(label="Energy & Forces", lines=15, interactive=False)
335
  spe_status = gr.Textbox(label="Status", interactive=False, max_lines=1)
336
- spe_debug = gr.Textbox(label="Debug Info", interactive=False, max_lines=3)
337
-
338
- # Conditional viewer
339
- if MOLECULE3D_AVAILABLE:
340
- spe_viewer = Molecule3D(label="Structure Viewer (Molecule3D)")
341
- outputs = [spe_out, spe_status, spe_debug, spe_viewer]
342
- else:
343
- spe_viewer = gr.HTML(label="Structure Viewer (3Dmol.js)")
344
- outputs = [spe_out, spe_status, spe_debug, spe_viewer]
 
345
 
346
- run_spe.click(predict_molecule, [xyz_input, charge_input, spin_input], outputs)
347
 
348
  # -------- MD --------
349
  with gr.Tab("Molecular Dynamics"):
350
  with gr.Row():
351
  with gr.Column(scale=2):
352
  gr.Markdown("## Molecular Dynamics Simulation")
 
353
 
354
  xyz_md = gr.File(
355
- label="Upload Structure File",
356
- file_types=[".xyz", ".pdb", ".cif", ".traj"],
357
  file_count="single"
358
  )
359
-
360
  with gr.Row():
361
  charge_md = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
362
- spin_md = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin")
363
  with gr.Row():
364
- steps_md = gr.Slider(minimum=10, maximum=500, value=100, step=10, label="Steps")
365
- temp_md = gr.Slider(minimum=10, maximum=1000, value=300, step=10, label="Temperature (K)")
366
  with gr.Row():
367
  timestep_md = gr.Slider(minimum=0.1, maximum=5.0, value=1.0, step=0.1, label="Timestep (fs)")
368
  ensemble_md = gr.Radio(["NVE", "NVT"], value="NVE", label="Ensemble")
@@ -371,24 +285,81 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo - Debug") as demo:
371
  with gr.Column(variant="panel", min_width=520):
372
  md_status = gr.Textbox(label="MD Status", interactive=False)
373
  md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
374
- md_debug = gr.Textbox(label="Debug Info", interactive=False, max_lines=3)
375
-
376
- # Conditional viewer
377
- if MOLECULE3D_AVAILABLE:
378
- md_viewer = Molecule3D(label="Final Structure (Molecule3D)")
379
- md_outputs = [md_status, md_traj, gr.Textbox(visible=False), gr.Code(visible=False), gr.Markdown(visible=False), md_viewer, md_debug]
380
- else:
381
- md_viewer = gr.HTML(label="Trajectory Animation (3Dmol.js)")
382
- md_outputs = [md_status, md_traj, gr.Textbox(visible=False), gr.Code(visible=False), gr.Markdown(visible=False), md_viewer, md_debug]
 
 
 
 
 
 
 
 
 
383
 
384
  run_md_btn.click(
385
  md_wrapper,
386
  inputs=[xyz_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
387
- outputs=md_outputs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  )
389
 
390
  print("Starting OrbMol model loading…")
391
  _ = _load_orbmol_calc()
392
 
393
  if __name__ == "__main__":
394
- demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
5
  import gradio as gr
6
  from ase.io import read, write
7
  from ase.io.trajectory import Trajectory
8
+ from gradio_molecule3d import Molecule3D
9
  import hashlib
10
  import shutil
11
 
12
+ # ==== util: PDB writer robusto para Molecule3D ====
13
+ def _pdb_cache_path(prefix: str, key: str) -> str:
14
+ gradio_cache_dir = os.path.join(tempfile.gettempdir(), "gradio")
15
+ os.makedirs(gradio_cache_dir, exist_ok=True)
16
+ return os.path.join(gradio_cache_dir, f"{prefix}_{key}.pdb")
17
+
18
+ def _write_pdb_with_fallback(atoms, pdb_path: str) -> str | None:
19
+ """
20
+ Intenta escribir PDB con ASE usando el id correcto ('proteindatabank').
21
+ Si falla, crea un PDB minimal válido (líneas ATOM + END) suficiente para gradio_molecule3d.
22
+ Devuelve la ruta si existe y tiene tamaño > 0, en caso contrario None.
23
+ """
24
  try:
25
+ # writer id correcto en ASE para PDB
26
+ write(pdb_path, atoms, format="proteindatabank")
27
+ except Exception as e:
28
+ print(f"ASE PDB writer failed: {e} — trying manual minimal PDB")
29
+ try:
30
+ xyz = atoms.get_positions()
31
+ syms = atoms.get_chemical_symbols()
32
+ with open(pdb_path, "w") as f:
33
+ serial = 1
34
+ for (sym, (x, y, z)) in zip(syms, xyz):
35
+ # Campos esenciales: 'ATOM', serial, nombre (símbolo), resname, chain, res seq, coords y elemento
36
+ f.write(
37
+ f"ATOM {serial:5d} {sym:<3s} MOL A 1 "
38
+ f"{x:8.3f}{y:8.3f}{z:8.3f} 1.00 0.00 {sym:>2s}\n"
39
+ )
40
+ serial += 1
41
+ f.write("END\n")
42
+ except Exception as e2:
43
+ print(f"Manual PDB fallback failed: {e2}")
44
+ return None
45
+
46
+ # Validación
47
+ if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
48
+ print(f"✅ PDB created: {pdb_path} ({os.path.getsize(pdb_path)} bytes)")
49
+ try:
50
+ with open(pdb_path, "r") as f:
51
+ preview = f.read(160)
52
+ print(f"PDB preview: {preview[:120]}...")
53
+ except Exception:
54
+ pass
55
+ return pdb_path
56
+ print("❌ PDB file not created or empty")
57
+ return None
58
+
59
+ # ==== Molecule3D viewer preparation CORREGIDO ====
60
+ def prepare_molecule_for_viewer(traj_path):
61
+ """Convert trajectory to format compatible with Molecule3D viewer"""
62
+ if not traj_path or not os.path.exists(traj_path):
63
+ print("No trajectory path provided or file doesn't exist")
64
  return None
65
+ try:
66
+ traj = Trajectory(traj_path)
67
+ if len(traj) == 0:
68
+ print("Empty trajectory")
69
+ return None
70
+
71
+ print(f"Preparing viewer: {len(traj)} frames, {len(traj[-1])} atoms")
72
+ atoms = traj[-1]
73
+
74
+ # clave única por posiciones del último frame
75
+ pos_hash = hashlib.md5(atoms.get_positions().tobytes()).hexdigest()[:12]
76
+ pdb_path = _pdb_cache_path("molecule", pos_hash)
77
+
78
+ if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
79
+ print(f"PDB already exists: {pdb_path}")
80
+ return pdb_path
81
+
82
+ return _write_pdb_with_fallback(atoms, pdb_path)
83
+
84
  except Exception as e:
85
+ print(f"Error preparing molecule for viewer: {e}")
86
+ import traceback; traceback.print_exc()
 
87
  return None
88
 
89
+ # ==== Función para convertir archivo inicial a PDB para SPE ====
90
+ def prepare_input_for_viewer(structure_file):
91
+ """Convert input structure file to PDB for Molecule3D viewer"""
92
+ if not structure_file or not os.path.exists(structure_file):
93
+ return None
94
+ try:
95
+ atoms = read(structure_file)
96
+ key = hashlib.md5(str(structure_file).encode()).hexdigest()[:12]
97
+ pdb_path = _pdb_cache_path("input", key)
98
+
99
+ if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
100
+ print(f"Input PDB already exists: {pdb_path}")
101
+ return pdb_path
102
+
103
+ return _write_pdb_with_fallback(atoms, pdb_path)
104
+
105
+ except Exception as e:
106
+ print(f"Error preparing input for viewer: {e}")
107
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  # ==== OrbMol SPE ====
110
  from orb_models.forcefield import pretrained
 
120
  _MODEL_CALC = ORBCalculator(orbff, device="cpu")
121
  return _MODEL_CALC
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  def predict_molecule(structure_file, charge=0, spin_multiplicity=1):
124
+ """
125
+ Single Point Energy + fuerzas (OrbMol). Acepta archivos subidos.
126
+ """
127
  try:
128
  calc = _load_orbmol_calc()
129
  if not structure_file:
130
+ return "Error: Please upload a structure file", "Error", None
131
 
132
  file_path = structure_file
 
133
  if not os.path.exists(file_path):
134
+ return f"Error: File not found: {file_path}", "Error", None
 
135
  if os.path.getsize(file_path) == 0:
136
+ return f"Error: Empty file: {file_path}", "Error", None
137
 
138
  atoms = read(file_path)
139
  atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
 
148
  max_force = float(np.max(np.linalg.norm(forces, axis=1)))
149
  lines += ["", f"Max Force: {max_force:.4f} eV/Å"]
150
 
151
+ # Preparar PDB para visualización
152
+ pdb_file = prepare_input_for_viewer(file_path)
153
+
154
+ return "\n".join(lines), "Calculation completed with OrbMol", pdb_file
 
 
 
 
 
 
 
 
 
 
 
155
  except Exception as e:
156
+ import traceback; traceback.print_exc()
157
+ return f"Error during calculation: {e}", "Error", None
 
 
158
 
159
+ # ==== Simulaciones (helpers) ====
160
  from simulation_scripts_orbmol import (
161
  run_md_simulation,
162
  run_relaxation_simulation,
163
  )
164
 
165
+ # ==== Wrappers con debug y Molecule3D ====
166
  def md_wrapper(structure_file, charge, spin, steps, tempK, timestep_fs, ensemble):
167
  try:
168
  if not structure_file:
169
+ return ("Error: Please upload a structure file", None, "", "", "", None)
170
 
171
  file_path = structure_file
172
  print(f"MD Wrapper: Processing {file_path}")
 
174
  traj_path, log_text, script_text, explanation = run_md_simulation(
175
  file_path,
176
  int(steps),
177
+ 20, # pre-relax steps
178
  float(timestep_fs),
179
  float(tempK),
180
  "NVT" if ensemble == "NVT" else "NVE",
 
182
  int(spin),
183
  )
184
  status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})"
 
185
  print(f"MD completed, trajectory: {traj_path}")
186
+
187
+ pdb_file = prepare_molecule_for_viewer(traj_path)
188
+ print(f"PDB file for Molecule3D: {pdb_file}")
189
+
190
+ return (status, traj_path, log_text, script_text, explanation, pdb_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  except Exception as e:
 
193
  print(f"MD Wrapper Error: {e}")
194
+ import traceback; traceback.print_exc()
195
+ return (f"Error: {e}", None, "", "", "", None)
196
+
197
+ def relax_wrapper(structure_file, steps, fmax, charge, spin, relax_cell):
198
+ try:
199
+ if not structure_file:
200
+ return ("Error: Please upload a structure file", None, "", "", "", None)
201
+
202
+ file_path = structure_file
203
+ print(f"Relax Wrapper: Processing {file_path}")
204
+
205
+ traj_path, log_text, script_text, explanation = run_relaxation_simulation(
206
+ file_path,
207
+ int(steps),
208
+ float(fmax),
209
+ int(charge),
210
+ int(spin),
211
+ bool(relax_cell),
212
+ )
213
+ status = f"Relaxation finished (≤ {int(steps)} steps, fmax={float(fmax)} eV/Å)"
214
+ print(f"Relaxation completed, trajectory: {traj_path}")
215
+
216
+ pdb_file = prepare_molecule_for_viewer(traj_path)
217
+ print(f"PDB file for Molecule3D: {pdb_file}")
218
+
219
+ return (status, traj_path, log_text, script_text, explanation, pdb_file)
220
+
221
+ except Exception as e:
222
+ print(f"Relax Wrapper Error: {e}")
223
+ import traceback; traceback.print_exc()
224
+ return (f"Error: {e}", None, "", "", "", None)
225
 
226
  # ==== UI ====
227
+ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
 
 
 
 
 
 
 
 
 
 
228
  with gr.Tabs():
229
  # -------- SPE --------
230
  with gr.Tab("Single Point Energy"):
231
  with gr.Row():
232
  with gr.Column(scale=2):
233
+ gr.Markdown("# OrbMol — Quantum-Accurate Molecular Predictions")
234
+ gr.Markdown("Upload molecular structure files (.xyz, .pdb, .cif, .traj) for energy and force calculations.")
235
 
236
  xyz_input = gr.File(
237
  label="Upload Structure File (.xyz/.pdb/.cif/.traj)",
238
  file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
239
  file_count="single"
240
  )
 
241
  with gr.Row():
242
  charge_input = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
243
  spin_input = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin Multiplicity")
 
246
  with gr.Column(variant="panel", min_width=500):
247
  spe_out = gr.Textbox(label="Energy & Forces", lines=15, interactive=False)
248
  spe_status = gr.Textbox(label="Status", interactive=False, max_lines=1)
249
+ spe_viewer = Molecule3D(
250
+ label="Input Structure Viewer",
251
+ reps=[
252
+ {
253
+ "model": 0, "chain": "", "resname": "",
254
+ "style": "stick", "color": "whiteCarbon",
255
+ "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 1.0
256
+ }
257
+ ]
258
+ )
259
 
260
+ run_spe.click(predict_molecule, [xyz_input, charge_input, spin_input], [spe_out, spe_status, spe_viewer])
261
 
262
  # -------- MD --------
263
  with gr.Tab("Molecular Dynamics"):
264
  with gr.Row():
265
  with gr.Column(scale=2):
266
  gr.Markdown("## Molecular Dynamics Simulation")
267
+ gr.Markdown("Upload your molecular structure and configure MD parameters.")
268
 
269
  xyz_md = gr.File(
270
+ label="Upload Structure File (.xyz/.pdb/.cif/.traj)",
271
+ file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
272
  file_count="single"
273
  )
 
274
  with gr.Row():
275
  charge_md = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
276
+ spin_md = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin Multiplicity")
277
  with gr.Row():
278
+ steps_md = gr.Slider(minimum=10, maximum=2000, value=100, step=10, label="Steps")
279
+ temp_md = gr.Slider(minimum=10, maximum=1500, value=300, step=10, label="Temperature (K)")
280
  with gr.Row():
281
  timestep_md = gr.Slider(minimum=0.1, maximum=5.0, value=1.0, step=0.1, label="Timestep (fs)")
282
  ensemble_md = gr.Radio(["NVE", "NVT"], value="NVE", label="Ensemble")
 
285
  with gr.Column(variant="panel", min_width=520):
286
  md_status = gr.Textbox(label="MD Status", interactive=False)
287
  md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
288
+ md_viewer = Molecule3D(
289
+ label="Final Structure Viewer (Last MD Frame)",
290
+ reps=[
291
+ {
292
+ "model": 0, "chain": "", "resname": "",
293
+ "style": "stick", "color": "whiteCarbon",
294
+ "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 1.0
295
+ },
296
+ {
297
+ "model": 0, "chain": "", "resname": "",
298
+ "style": "sphere", "color": "whiteCarbon",
299
+ "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 0.7
300
+ }
301
+ ]
302
+ )
303
+ md_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25)
304
+ md_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
305
+ md_explain = gr.Markdown()
306
 
307
  run_md_btn.click(
308
  md_wrapper,
309
  inputs=[xyz_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
310
+ outputs=[md_status, md_traj, md_log, md_script, md_explain, md_viewer],
311
+ )
312
+
313
+ # -------- Relax --------
314
+ with gr.Tab("Relaxation / Optimization"):
315
+ with gr.Row():
316
+ with gr.Column(scale=2):
317
+ gr.Markdown("## Structure Relaxation/Optimization")
318
+ gr.Markdown("Upload your molecular structure for geometry optimization.")
319
+
320
+ xyz_rlx = gr.File(
321
+ label="Upload Structure File (.xyz/.pdb/.cif/.traj)",
322
+ file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
323
+ file_count="single"
324
+ )
325
+ steps_rlx = gr.Slider(minimum=1, maximum=2000, value=300, step=1, label="Max Steps")
326
+ fmax_rlx = gr.Slider(minimum=0.001, maximum=0.5, value=0.05, step=0.001, label="Fmax (eV/Å)")
327
+ with gr.Row():
328
+ charge_rlx = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
329
+ spin_rlx = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin")
330
+ relax_cell = gr.Checkbox(False, label="Relax Unit Cell")
331
+ run_rlx_btn = gr.Button("Run Optimization", variant="primary")
332
+
333
+ with gr.Column(variant="panel", min_width=520):
334
+ rlx_status = gr.Textbox(label="Status", interactive=False)
335
+ rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False)
336
+ rlx_viewer = Molecule3D(
337
+ label="Optimized Structure Viewer",
338
+ reps=[
339
+ {
340
+ "model": 0, "chain": "", "resname": "",
341
+ "style": "stick", "color": "whiteCarbon",
342
+ "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 1.0
343
+ },
344
+ {
345
+ "model": 0, "chain": "", "resname": "",
346
+ "style": "sphere", "color": "whiteCarbon",
347
+ "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 0.7
348
+ }
349
+ ]
350
+ )
351
+ rlx_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25)
352
+ rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
353
+ rlx_explain = gr.Markdown()
354
+
355
+ run_rlx_btn.click(
356
+ relax_wrapper,
357
+ inputs=[xyz_rlx, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell],
358
+ outputs=[rlx_status, rlx_traj, rlx_log, rlx_script, rlx_explain, rlx_viewer],
359
  )
360
 
361
  print("Starting OrbMol model loading…")
362
  _ = _load_orbmol_calc()
363
 
364
  if __name__ == "__main__":
365
+ demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)