annabossler commited on
Commit
1b94f8b
·
verified ·
1 Parent(s): 48c538b

Update app.py

Browse files

upload universal functions from gradio_molecule3d.

Files changed (1) hide show
  1. app.py +121 -240
app.py CHANGED
@@ -6,159 +6,42 @@ 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
- from simulation_scripts_orbmol import load_orbmol_model
10
  import hashlib
11
- import shutil
12
 
13
- # ==== Configuración UNIVERSAL para Molecule3D ====
14
- UNIVERSAL_MOLECULE3D_REPS = [
15
  {
16
  "model": 0,
17
  "chain": "",
18
  "resname": "",
19
  "style": "sphere",
20
- "color": "element", # Colores automáticos por elemento químico
21
- "residue_range": "",
22
  "around": 0,
23
  "byres": False,
24
- "visible": True,
25
- "opacity": 0.8
26
  },
27
  {
28
  "model": 0,
29
  "chain": "",
30
  "resname": "",
31
  "style": "stick",
32
- "color": "element",
33
- "residue_range": "",
34
  "around": 0,
35
  "byres": False,
36
- "visible": True,
37
- "opacity": 1.0
38
- }
39
  ]
40
 
41
- # ==== Función mejorada para crear PDB universal ====
42
- def create_universal_pdb(atoms, cache_key):
43
- """
44
- Crea un PDB optimizado que funciona bien con gradio_molecule3d para cualquier molécula
45
- """
46
- cache_dir = os.path.join(tempfile.gettempdir(), "gradio")
47
- os.makedirs(cache_dir, exist_ok=True)
48
- pdb_path = os.path.join(cache_dir, f"mol_{cache_key}.pdb")
49
-
50
- if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
51
- return pdb_path
52
-
53
- try:
54
- positions = atoms.get_positions()
55
- symbols = atoms.get_chemical_symbols()
56
-
57
- with open(pdb_path, "w") as f:
58
- # Header estándar
59
- f.write("HEADER MOLECULAR STRUCTURE 01-JAN-25 MOL \n")
60
- f.write("TITLE CALCULATION RESULT\n")
61
- f.write("MODEL 1\n")
62
-
63
- # Escribir átomos con formato PDB correcto
64
- for i, (symbol, pos) in enumerate(zip(symbols, positions)):
65
- # Asegurar formato correcto del elemento
66
- element = symbol.strip().upper()
67
-
68
- # Nombre del átomo (4 caracteres, alineado a la izquierda)
69
- atom_name = f"{element:<4s}"
70
-
71
- # Línea ATOM con formato PDB estricto
72
- f.write(
73
- f"ATOM {i+1:5d} {atom_name} UNL A 1 "
74
- f"{pos[0]:8.3f}{pos[1]:8.3f}{pos[2]:8.3f}"
75
- f" 1.00 30.00 {element:>2s} \n"
76
- )
77
-
78
- f.write("ENDMDL\n")
79
- f.write("END\n")
80
-
81
- # Verificar creación
82
- if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
83
- print(f"✅ Universal PDB created: {pdb_path} ({os.path.getsize(pdb_path)} bytes)")
84
-
85
- # Debug: mostrar contenido
86
- with open(pdb_path, 'r') as f:
87
- content = f.read()
88
- print(f"PDB content preview:\n{content[:300]}...")
89
-
90
- return pdb_path
91
- else:
92
- print(f"❌ Universal PDB creation failed")
93
- return None
94
-
95
- except Exception as e:
96
- print(f"❌ Error in create_universal_pdb: {e}")
97
- import traceback
98
- traceback.print_exc()
99
- return None
100
-
101
- # ==== Función actualizada para preparar cualquier molécula ====
102
- def prepare_universal_molecule_viewer(traj_path_or_atoms):
103
- """
104
- Función universal que funciona con cualquier molécula
105
- """
106
- try:
107
- # Determinar si es trayectoria o átomos
108
- if isinstance(traj_path_or_atoms, str):
109
- if not os.path.exists(traj_path_or_atoms):
110
- print("Trajectory file doesn't exist")
111
- return None
112
-
113
- traj = Trajectory(traj_path_or_atoms)
114
- if len(traj) == 0:
115
- print("Empty trajectory")
116
- return None
117
-
118
- atoms = traj[-1]
119
- cache_key = hashlib.md5(str(traj_path_or_atoms).encode()).hexdigest()[:12]
120
- else:
121
- atoms = traj_path_or_atoms
122
- cache_key = hashlib.md5(atoms.get_positions().tobytes()).hexdigest()[:12]
123
-
124
- print(f"Preparing universal viewer: {len(atoms)} atoms")
125
- print(f"Chemical symbols: {atoms.get_chemical_symbols()}")
126
-
127
- # Crear PDB universal
128
- pdb_path = create_universal_pdb(atoms, cache_key)
129
-
130
- return pdb_path
131
-
132
- except Exception as e:
133
- print(f"Error in prepare_universal_molecule_viewer: {e}")
134
- import traceback
135
- traceback.print_exc()
136
- return None
137
-
138
- # ==== Funciones de preparación universales ====
139
- def prepare_input_for_viewer(structure_file):
140
- """Universal input preparation"""
141
- if not structure_file or not os.path.exists(structure_file):
142
- return None
143
- try:
144
- atoms = read(structure_file)
145
- return prepare_universal_molecule_viewer(atoms)
146
- except Exception as e:
147
- print(f"Error preparing input for viewer: {e}")
148
- return None
149
-
150
- def prepare_molecule_for_viewer(traj_path):
151
- """Universal trajectory preparation"""
152
- return prepare_universal_molecule_viewer(traj_path)
153
 
154
  # ==== OrbMol SPE ====
155
- from orb_models.forcefield import pretrained
156
- from orb_models.forcefield.calculator import ORBCalculator
157
-
158
  def predict_molecule(structure_file, task_name, charge=0, spin_multiplicity=1):
159
- """
160
- Single Point Energy + fuerzas (OrbMol). Acepta archivos subidos.
161
- """
162
  try:
163
  calc = load_orbmol_model(task_name)
164
  if not structure_file:
@@ -171,46 +54,44 @@ def predict_molecule(structure_file, task_name, charge=0, spin_multiplicity=1):
171
  return f"Error: Empty file: {file_path}", "Error", None
172
 
173
  atoms = read(file_path)
174
- atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
 
 
 
 
175
  atoms.calc = calc
176
-
177
  energy = atoms.get_potential_energy()
178
  forces = atoms.get_forces()
179
 
180
- lines = [f"Total Energy: {energy:.6f} eV", "", "Atomic Forces:"]
 
 
 
 
 
181
  for i, fc in enumerate(forces):
182
  lines.append(f"Atom {i+1}: [{fc[0]:.4f}, {fc[1]:.4f}, {fc[2]:.4f}] eV/Å")
183
  max_force = float(np.max(np.linalg.norm(forces, axis=1)))
184
  lines += ["", f"Max Force: {max_force:.4f} eV/Å"]
185
 
186
- # Preparar PDB para visualización
187
- pdb_file = prepare_input_for_viewer(file_path)
188
-
189
- return "\n".join(lines), f"Calculation completed with {task_name}", pdb_file
190
  except Exception as e:
191
  import traceback
192
  traceback.print_exc()
193
  return f"Error during calculation: {e}", "Error", None
194
 
195
- # ==== Simulaciones (helpers) ====
196
- from simulation_scripts_orbmol import (
197
- run_md_simulation,
198
- run_relaxation_simulation,
199
- )
200
-
201
- # ==== Wrappers con debug y Molecule3D ====
202
  def md_wrapper(structure_file, task_name, charge, spin, steps, tempK, timestep_fs, ensemble):
203
  try:
204
  if not structure_file:
205
  return ("Error: Please upload a structure file", None, "", "", "", None)
206
 
207
- file_path = structure_file
208
- print(f"MD Wrapper: Processing {file_path}")
209
-
210
  traj_path, log_text, script_text, explanation = run_md_simulation(
211
- file_path,
212
  int(steps),
213
- 20, # pre-relax steps
214
  float(timestep_fs),
215
  float(tempK),
216
  "NVT" if ensemble == "NVT" else "NVE",
@@ -218,16 +99,12 @@ def md_wrapper(structure_file, task_name, charge, spin, steps, tempK, timestep_f
218
  int(charge),
219
  int(spin),
220
  )
221
- status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})"
222
- print(f"MD completed, trajectory: {traj_path}")
223
 
224
- pdb_file = prepare_molecule_for_viewer(traj_path)
225
- print(f"PDB file for Molecule3D: {pdb_file}")
226
-
227
- return (status, traj_path, log_text, script_text, explanation, pdb_file)
228
 
229
  except Exception as e:
230
- print(f"MD Wrapper Error: {e}")
231
  import traceback
232
  traceback.print_exc()
233
  return (f"Error: {e}", None, "", "", "", None)
@@ -237,11 +114,8 @@ def relax_wrapper(structure_file, task_name, steps, fmax, charge, spin, relax_ce
237
  if not structure_file:
238
  return ("Error: Please upload a structure file", None, "", "", "", None)
239
 
240
- file_path = structure_file
241
- print(f"Relax Wrapper: Processing {file_path}")
242
-
243
  traj_path, log_text, script_text, explanation = run_relaxation_simulation(
244
- file_path,
245
  int(steps),
246
  float(fmax),
247
  str(task_name),
@@ -249,16 +123,12 @@ def relax_wrapper(structure_file, task_name, steps, fmax, charge, spin, relax_ce
249
  int(spin),
250
  bool(relax_cell),
251
  )
252
- status = f"Relaxation finished (≤ {int(steps)} steps, fmax={float(fmax)} eV/Å)"
253
- print(f"Relaxation completed, trajectory: {traj_path}")
254
 
255
- pdb_file = prepare_molecule_for_viewer(traj_path)
256
- print(f"PDB file for Molecule3D: {pdb_file}")
257
-
258
- return (status, traj_path, log_text, script_text, explanation, pdb_file)
259
 
260
  except Exception as e:
261
- print(f"Relax Wrapper Error: {e}")
262
  import traceback
263
  traceback.print_exc()
264
  return (f"Error: {e}", None, "", "", "", None)
@@ -271,103 +141,113 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
271
  with gr.Row():
272
  with gr.Column(scale=2):
273
  gr.Markdown("# OrbMol — Quantum-Accurate Molecular Predictions")
274
- gr.Markdown("Upload molecular structure files (.xyz, .pdb, .cif, .traj) for energy and force calculations.")
275
 
276
  xyz_input = gr.File(
277
- label="Upload Structure File (.xyz/.pdb/.cif/.traj)",
278
  file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
279
  file_count="single"
280
  )
 
 
 
 
 
281
  with gr.Row():
282
- task_name_spe = gr.Radio(
283
- ["OMol", "OMat", "OMol-Direct"],
284
- value="OMol",
285
- label="Model Type",
286
- info="Choose the OrbMol model variant for the calculation."
287
- )
288
- with gr.Row():
289
- charge_input = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
290
- spin_input = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin Multiplicity")
291
 
292
  run_spe = gr.Button("Run OrbMol Prediction", variant="primary")
293
 
294
  with gr.Column(variant="panel", min_width=500):
295
  spe_out = gr.Textbox(label="Energy & Forces", lines=15, interactive=False)
296
- spe_status = gr.Textbox(label="Status", interactive=False, max_lines=1)
 
 
297
  spe_viewer = Molecule3D(
298
  label="Input Structure Viewer",
299
- reps=UNIVERSAL_MOLECULE3D_REPS
 
 
 
 
 
300
  )
301
 
302
- # Charge and Spin are only applicable to OMol and OMol-Direct
303
- task_name_spe.input(
304
  lambda x: (
305
- (gr.Number(visible=True), gr.Number(visible=True))
306
- if x == "OMol" or x == "OMol-Direct"
307
- else (gr.Number(visible=False), gr.Number(visible=False))
308
  ),
309
  [task_name_spe],
310
- [charge_input, spin_input],
311
  )
312
 
313
- run_spe.click(predict_molecule, [xyz_input, task_name_spe, charge_input, spin_input], [spe_out, spe_status, spe_viewer])
 
 
 
 
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
- gr.Markdown("Upload your molecular structure and configure MD parameters.")
321
 
322
  xyz_md = 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
  with gr.Row():
328
- task_name_md = gr.Radio(
329
- ["OMol", "OMat", "OMol-Direct"],
330
- value="OMol",
331
- label="Model Type",
332
- info="Choose the OrbMol model variant for the calculation."
333
- )
334
- with gr.Row():
335
- charge_md = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
336
- spin_md = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin Multiplicity")
337
  with gr.Row():
338
- steps_md = gr.Slider(minimum=10, maximum=2000, value=100, step=10, label="Steps")
339
- temp_md = gr.Slider(minimum=10, maximum=1500, value=300, step=10, label="Temperature (K)")
340
  with gr.Row():
341
- timestep_md = gr.Slider(minimum=0.1, maximum=5.0, value=1.0, step=0.1, label="Timestep (fs)")
342
  ensemble_md = gr.Radio(["NVE", "NVT"], value="NVE", label="Ensemble")
343
  run_md_btn = gr.Button("Run MD Simulation", variant="primary")
344
 
345
  with gr.Column(variant="panel", min_width=520):
346
  md_status = gr.Textbox(label="MD Status", interactive=False)
347
  md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
 
 
348
  md_viewer = Molecule3D(
349
- label="Final Structure Viewer (Last MD Frame)",
350
- reps=UNIVERSAL_MOLECULE3D_REPS
 
 
 
 
 
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
- # Charge and Spin are only applicable to OMol and OMol-Direct
357
- task_name_md.input(
358
  lambda x: (
359
- (gr.Number(visible=True), gr.Number(visible=True))
360
- if x == "OMol" or x == "OMol-Direct"
361
- else (gr.Number(visible=False), gr.Number(visible=False))
362
  ),
363
  [task_name_md],
364
- [charge_md, spin_md],
365
  )
366
 
367
  run_md_btn.click(
368
  md_wrapper,
369
- inputs=[xyz_md, task_name_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
370
- outputs=[md_status, md_traj, md_log, md_script, md_explain, md_viewer],
371
  )
372
 
373
  # -------- Relax --------
@@ -375,58 +255,59 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
375
  with gr.Row():
376
  with gr.Column(scale=2):
377
  gr.Markdown("## Structure Relaxation/Optimization")
378
- gr.Markdown("Upload your molecular structure for geometry optimization.")
379
 
380
  xyz_rlx = gr.File(
381
- label="Upload Structure File (.xyz/.pdb/.cif/.traj)",
382
  file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
383
  file_count="single"
384
  )
 
 
 
 
 
385
  with gr.Row():
386
- task_name_rlx = gr.Radio(
387
- ["OMol", "OMat", "OMol-Direct"],
388
- value="OMol",
389
- label="Model Type",
390
- info="Choose the OrbMol model variant for the calculation."
391
- )
392
- with gr.Row():
393
- steps_rlx = gr.Slider(minimum=1, maximum=2000, value=300, step=1, label="Max Steps")
394
- fmax_rlx = gr.Slider(minimum=0.001, maximum=0.5, value=0.05, step=0.001, label="Fmax (eV/Å)")
395
  with gr.Row():
396
- charge_rlx = gr.Slider(minimum=-10, maximum=10, value=0, step=1, label="Charge")
397
- spin_rlx = gr.Slider(minimum=1, maximum=11, value=1, step=1, label="Spin")
398
  relax_cell = gr.Checkbox(False, label="Relax Unit Cell")
399
  run_rlx_btn = gr.Button("Run Optimization", variant="primary")
400
 
401
  with gr.Column(variant="panel", min_width=520):
402
  rlx_status = gr.Textbox(label="Status", interactive=False)
403
  rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False)
 
 
404
  rlx_viewer = Molecule3D(
405
  label="Optimized Structure Viewer",
406
- reps=UNIVERSAL_MOLECULE3D_REPS
 
 
 
 
 
407
  )
408
- rlx_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25)
409
- rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
 
410
  rlx_explain = gr.Markdown()
411
 
412
- # Charge and Spin are only applicable to OMol and OMol-Direct
413
- task_name_rlx.input(
414
  lambda x: (
415
- (gr.Number(visible=True), gr.Number(visible=True))
416
- if x == "OMol" or x == "OMol-Direct"
417
- else (gr.Number(visible=False), gr.Number(visible=False))
418
  ),
419
  [task_name_rlx],
420
- [charge_rlx, spin_rlx],
421
  )
422
 
423
  run_rlx_btn.click(
424
  relax_wrapper,
425
- inputs=[xyz_rlx, task_name_rlx, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell],
426
- outputs=[rlx_status, rlx_traj, rlx_log, rlx_script, rlx_explain, rlx_viewer],
427
  )
428
 
429
- print("Starting OrbMol model loading…")
430
-
431
  if __name__ == "__main__":
432
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
6
  from ase.io import read, write
7
  from ase.io.trajectory import Trajectory
8
  from gradio_molecule3d import Molecule3D
9
+ from simulation_scripts_orbmol import load_orbmol_model, run_md_simulation, run_relaxation_simulation
10
  import hashlib
 
11
 
12
+ # ==== Configuración Molecule3D (igual que Facebook UMA) ====
13
+ DEFAULT_MOLECULAR_REPRESENTATIONS = [
14
  {
15
  "model": 0,
16
  "chain": "",
17
  "resname": "",
18
  "style": "sphere",
19
+ "color": "Jmol",
 
20
  "around": 0,
21
  "byres": False,
22
+ "scale": 0.3,
 
23
  },
24
  {
25
  "model": 0,
26
  "chain": "",
27
  "resname": "",
28
  "style": "stick",
29
+ "color": "Jmol",
 
30
  "around": 0,
31
  "byres": False,
32
+ "scale": 0.2,
33
+ },
 
34
  ]
35
 
36
+ DEFAULT_MOLECULAR_SETTINGS = {
37
+ "backgroundColor": "white",
38
+ "orthographic": False,
39
+ "disableFog": False,
40
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  # ==== OrbMol SPE ====
 
 
 
43
  def predict_molecule(structure_file, task_name, charge=0, spin_multiplicity=1):
44
+ """Single Point Energy + fuerzas (OrbMol)"""
 
 
45
  try:
46
  calc = load_orbmol_model(task_name)
47
  if not structure_file:
 
54
  return f"Error: Empty file: {file_path}", "Error", None
55
 
56
  atoms = read(file_path)
57
+
58
+ # Solo aplicar charge/spin para OMol
59
+ if task_name in ["OMol", "OMol-Direct"]:
60
+ atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
61
+
62
  atoms.calc = calc
 
63
  energy = atoms.get_potential_energy()
64
  forces = atoms.get_forces()
65
 
66
+ lines = [
67
+ f"Model: {task_name}",
68
+ f"Total Energy: {energy:.6f} eV",
69
+ "",
70
+ "Atomic Forces:"
71
+ ]
72
  for i, fc in enumerate(forces):
73
  lines.append(f"Atom {i+1}: [{fc[0]:.4f}, {fc[1]:.4f}, {fc[2]:.4f}] eV/Å")
74
  max_force = float(np.max(np.linalg.norm(forces, axis=1)))
75
  lines += ["", f"Max Force: {max_force:.4f} eV/Å"]
76
 
77
+ # CLAVE: Devolver archivo original, NO un PDB procesado
78
+ return "\n".join(lines), f"✅ Calculation completed with {task_name}", file_path
79
+
 
80
  except Exception as e:
81
  import traceback
82
  traceback.print_exc()
83
  return f"Error during calculation: {e}", "Error", None
84
 
85
+ # ==== Wrappers MD y Relax ====
 
 
 
 
 
 
86
  def md_wrapper(structure_file, task_name, charge, spin, steps, tempK, timestep_fs, ensemble):
87
  try:
88
  if not structure_file:
89
  return ("Error: Please upload a structure file", None, "", "", "", None)
90
 
 
 
 
91
  traj_path, log_text, script_text, explanation = run_md_simulation(
92
+ structure_file,
93
  int(steps),
94
+ 20,
95
  float(timestep_fs),
96
  float(tempK),
97
  "NVT" if ensemble == "NVT" else "NVE",
 
99
  int(charge),
100
  int(spin),
101
  )
102
+ status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})"
 
103
 
104
+ # CLAVE: Devolver traj_path directamente
105
+ return (status, traj_path, log_text, script_text, explanation, traj_path)
 
 
106
 
107
  except Exception as e:
 
108
  import traceback
109
  traceback.print_exc()
110
  return (f"Error: {e}", None, "", "", "", None)
 
114
  if not structure_file:
115
  return ("Error: Please upload a structure file", None, "", "", "", None)
116
 
 
 
 
117
  traj_path, log_text, script_text, explanation = run_relaxation_simulation(
118
+ structure_file,
119
  int(steps),
120
  float(fmax),
121
  str(task_name),
 
123
  int(spin),
124
  bool(relax_cell),
125
  )
126
+ status = f"Relaxation finished (≤{int(steps)} steps, fmax={float(fmax)} eV/Å)"
 
127
 
128
+ # CLAVE: Devolver traj_path directamente
129
+ return (status, traj_path, log_text, script_text, explanation, traj_path)
 
 
130
 
131
  except Exception as e:
 
132
  import traceback
133
  traceback.print_exc()
134
  return (f"Error: {e}", None, "", "", "", None)
 
141
  with gr.Row():
142
  with gr.Column(scale=2):
143
  gr.Markdown("# OrbMol — Quantum-Accurate Molecular Predictions")
144
+ gr.Markdown("**Supported formats:** .xyz, .pdb, .cif, .traj, .mol, .sdf")
145
 
146
  xyz_input = gr.File(
147
+ label="Upload Structure File",
148
  file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
149
  file_count="single"
150
  )
151
+ task_name_spe = gr.Radio(
152
+ ["OMol", "OMat", "OMol-Direct"],
153
+ value="OMol",
154
+ label="Model Type"
155
+ )
156
  with gr.Row():
157
+ charge_input = gr.Slider(-10, 10, 0, step=1, label="Charge")
158
+ spin_input = gr.Slider(1, 11, 1, step=1, label="Spin Multiplicity")
 
 
 
 
 
 
 
159
 
160
  run_spe = gr.Button("Run OrbMol Prediction", variant="primary")
161
 
162
  with gr.Column(variant="panel", min_width=500):
163
  spe_out = gr.Textbox(label="Energy & Forces", lines=15, interactive=False)
164
+ spe_status = gr.Textbox(label="Status", interactive=False)
165
+
166
+ # PATRÓN FACEBOOK UMA: inputs + value=lambda
167
  spe_viewer = Molecule3D(
168
  label="Input Structure Viewer",
169
+ reps=DEFAULT_MOLECULAR_REPRESENTATIONS,
170
+ config=DEFAULT_MOLECULAR_SETTINGS,
171
+ render=False,
172
+ inputs=[xyz_input],
173
+ value=lambda x: x,
174
+ interactive=False
175
  )
176
 
177
+ task_name_spe.change(
 
178
  lambda x: (
179
+ gr.update(visible=x in ["OMol", "OMol-Direct"]),
180
+ gr.update(visible=x in ["OMol", "OMol-Direct"])
 
181
  ),
182
  [task_name_spe],
183
+ [charge_input, spin_input]
184
  )
185
 
186
+ run_spe.click(
187
+ predict_molecule,
188
+ [xyz_input, task_name_spe, charge_input, spin_input],
189
+ [spe_out, spe_status, spe_viewer]
190
+ )
191
 
192
  # -------- MD --------
193
  with gr.Tab("Molecular Dynamics"):
194
  with gr.Row():
195
  with gr.Column(scale=2):
196
  gr.Markdown("## Molecular Dynamics Simulation")
 
197
 
198
  xyz_md = gr.File(
199
+ label="Upload Structure File",
200
  file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
201
  file_count="single"
202
  )
203
+ task_name_md = gr.Radio(
204
+ ["OMol", "OMat", "OMol-Direct"],
205
+ value="OMol",
206
+ label="Model Type"
207
+ )
208
  with gr.Row():
209
+ charge_md = gr.Slider(-10, 10, 0, step=1, label="Charge")
210
+ spin_md = gr.Slider(1, 11, 1, step=1, label="Spin Multiplicity")
 
 
 
 
 
 
 
211
  with gr.Row():
212
+ steps_md = gr.Slider(10, 2000, 100, step=10, label="Steps")
213
+ temp_md = gr.Slider(10, 1500, 300, step=10, label="Temperature (K)")
214
  with gr.Row():
215
+ timestep_md = gr.Slider(0.1, 5.0, 1.0, step=0.1, label="Timestep (fs)")
216
  ensemble_md = gr.Radio(["NVE", "NVT"], value="NVE", label="Ensemble")
217
  run_md_btn = gr.Button("Run MD Simulation", variant="primary")
218
 
219
  with gr.Column(variant="panel", min_width=520):
220
  md_status = gr.Textbox(label="MD Status", interactive=False)
221
  md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
222
+
223
+ # PATRÓN FACEBOOK UMA
224
  md_viewer = Molecule3D(
225
+ label="MD Result Viewer",
226
+ reps=DEFAULT_MOLECULAR_REPRESENTATIONS,
227
+ config=DEFAULT_MOLECULAR_SETTINGS,
228
+ render=False,
229
+ inputs=[md_traj],
230
+ value=lambda x: x,
231
+ interactive=False
232
  )
233
+
234
+ md_log = gr.Textbox(label="Log", interactive=False, lines=15)
235
+ md_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20)
236
  md_explain = gr.Markdown()
237
 
238
+ task_name_md.change(
 
239
  lambda x: (
240
+ gr.update(visible=x in ["OMol", "OMol-Direct"]),
241
+ gr.update(visible=x in ["OMol", "OMol-Direct"])
 
242
  ),
243
  [task_name_md],
244
+ [charge_md, spin_md]
245
  )
246
 
247
  run_md_btn.click(
248
  md_wrapper,
249
+ [xyz_md, task_name_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
250
+ [md_status, md_traj, md_log, md_script, md_explain, md_viewer]
251
  )
252
 
253
  # -------- Relax --------
 
255
  with gr.Row():
256
  with gr.Column(scale=2):
257
  gr.Markdown("## Structure Relaxation/Optimization")
 
258
 
259
  xyz_rlx = gr.File(
260
+ label="Upload Structure File",
261
  file_types=[".xyz", ".pdb", ".cif", ".traj", ".mol", ".sdf"],
262
  file_count="single"
263
  )
264
+ task_name_rlx = gr.Radio(
265
+ ["OMol", "OMat", "OMol-Direct"],
266
+ value="OMol",
267
+ label="Model Type"
268
+ )
269
  with gr.Row():
270
+ steps_rlx = gr.Slider(1, 2000, 300, step=1, label="Max Steps")
271
+ fmax_rlx = gr.Slider(0.001, 0.5, 0.05, step=0.001, label="Fmax (eV/Å)")
 
 
 
 
 
 
 
272
  with gr.Row():
273
+ charge_rlx = gr.Slider(-10, 10, 0, step=1, label="Charge")
274
+ spin_rlx = gr.Slider(1, 11, 1, step=1, label="Spin")
275
  relax_cell = gr.Checkbox(False, label="Relax Unit Cell")
276
  run_rlx_btn = gr.Button("Run Optimization", variant="primary")
277
 
278
  with gr.Column(variant="panel", min_width=520):
279
  rlx_status = gr.Textbox(label="Status", interactive=False)
280
  rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False)
281
+
282
+ # PATRÓN FACEBOOK UMA
283
  rlx_viewer = Molecule3D(
284
  label="Optimized Structure Viewer",
285
+ reps=DEFAULT_MOLECULAR_REPRESENTATIONS,
286
+ config=DEFAULT_MOLECULAR_SETTINGS,
287
+ render=False,
288
+ inputs=[rlx_traj],
289
+ value=lambda x: x,
290
+ interactive=False
291
  )
292
+
293
+ rlx_log = gr.Textbox(label="Log", interactive=False, lines=15)
294
+ rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20)
295
  rlx_explain = gr.Markdown()
296
 
297
+ task_name_rlx.change(
 
298
  lambda x: (
299
+ gr.update(visible=x in ["OMol", "OMol-Direct"]),
300
+ gr.update(visible=x in ["OMol", "OMol-Direct"])
 
301
  ),
302
  [task_name_rlx],
303
+ [charge_rlx, spin_rlx]
304
  )
305
 
306
  run_rlx_btn.click(
307
  relax_wrapper,
308
+ [xyz_rlx, task_name_rlx, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell],
309
+ [rlx_status, rlx_traj, rlx_log, rlx_script, rlx_explain, rlx_viewer]
310
  )
311
 
 
 
312
  if __name__ == "__main__":
313
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)