annabossler commited on
Commit
e7ee8a1
·
verified ·
1 Parent(s): 18b592b

Update app.py

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