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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -148
app.py CHANGED
@@ -6,122 +6,156 @@ 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 # CORREGIDO
10
  import hashlib
11
  import shutil
12
 
13
- # ==== util: PDB writer robusto para Molecule3D ====
14
- def _pdb_cache_path(prefix: str, key: str) -> str:
15
- gradio_cache_dir = os.path.join(tempfile.gettempdir(), "gradio")
16
- os.makedirs(gradio_cache_dir, exist_ok=True)
17
- return os.path.join(gradio_cache_dir, f"{prefix}_{key}.pdb")
18
-
19
- def _write_pdb_with_fallback(atoms, pdb_path: str) -> str | None:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  """
21
- Intenta escribir PDB con ASE usando el id correcto ('proteindatabank').
22
- Si falla, crea un PDB minimal válido (líneas ATOM + END) suficiente para gradio_molecule3d.
23
- Devuelve la ruta si existe y tiene tamaño > 0, en caso contrario None.
24
  """
25
- try:
26
- # writer id correcto en ASE para PDB
27
- write(pdb_path, atoms, format="proteindatabank")
28
- except Exception as e:
29
- print(f"ASE PDB writer failed: {e} — trying manual minimal PDB")
30
- try:
31
- xyz = atoms.get_positions()
32
- syms = atoms.get_chemical_symbols()
33
- with open(pdb_path, "w") as f:
34
- serial = 1
35
- for (sym, (x, y, z)) in zip(syms, xyz):
36
- # Campos esenciales: 'ATOM', serial, nombre (símbolo), resname, chain, res seq, coords y elemento
37
- f.write(
38
- f"ATOM {serial:5d} {sym:<3s} MOL A 1 "
39
- f"{x:8.3f}{y:8.3f}{z:8.3f} 1.00 0.00 {sym:>2s}\n"
40
- )
41
- serial += 1
42
- f.write("END\n")
43
- except Exception as e2:
44
- print(f"Manual PDB fallback failed: {e2}")
45
- return None
46
-
47
- # Validación
48
  if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
49
- print(f"✅ PDB created: {pdb_path} ({os.path.getsize(pdb_path)} bytes)")
50
- try:
51
- with open(pdb_path, "r") as f:
52
- preview = f.read(160)
53
- print(f"PDB preview: {preview[:120]}...")
54
- except Exception:
55
- pass
56
  return pdb_path
57
- print("❌ PDB file not created or empty")
58
- return None
59
-
60
- # ==== Molecule3D viewer preparation CORREGIDO ====
61
- def prepare_molecule_for_viewer(traj_path):
62
- """Convert trajectory to format compatible with Molecule3D viewer"""
63
- if not traj_path or not os.path.exists(traj_path):
64
- print("No trajectory path provided or file doesn't exist")
65
- return None
66
  try:
67
- traj = Trajectory(traj_path)
68
- if len(traj) == 0:
69
- print("Empty trajectory")
70
- return None
71
-
72
- print(f"Preparing viewer: {len(traj)} frames, {len(traj[-1])} atoms")
73
- atoms = traj[-1]
74
-
75
- # clave única por posiciones del último frame
76
- pos_hash = hashlib.md5(atoms.get_positions().tobytes()).hexdigest()[:12]
77
- pdb_path = _pdb_cache_path("molecule", pos_hash)
78
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
80
- print(f"PDB already exists: {pdb_path}")
 
 
 
 
 
 
81
  return pdb_path
 
 
 
 
 
 
 
 
 
82
 
83
- return _write_pdb_with_fallback(atoms, pdb_path)
84
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  except Exception as e:
86
- print(f"Error preparing molecule for viewer: {e}")
87
- import traceback; traceback.print_exc()
 
88
  return None
89
 
90
- # ==== Función para convertir archivo inicial a PDB para SPE ====
91
  def prepare_input_for_viewer(structure_file):
92
- """Convert input structure file to PDB for Molecule3D viewer"""
93
  if not structure_file or not os.path.exists(structure_file):
94
  return None
95
  try:
96
  atoms = read(structure_file)
97
- key = hashlib.md5(str(structure_file).encode()).hexdigest()[:12]
98
- pdb_path = _pdb_cache_path("input", key)
99
-
100
- if os.path.exists(pdb_path) and os.path.getsize(pdb_path) > 0:
101
- print(f"Input PDB already exists: {pdb_path}")
102
- return pdb_path
103
-
104
- return _write_pdb_with_fallback(atoms, pdb_path)
105
-
106
  except Exception as e:
107
  print(f"Error preparing input for viewer: {e}")
108
  return None
109
 
 
 
 
 
110
  # ==== OrbMol SPE ====
111
  from orb_models.forcefield import pretrained
112
  from orb_models.forcefield.calculator import ORBCalculator
113
 
114
- #_MODEL_CALC = None
115
- #def _load_orbmol_calc():
116
- # global _MODEL_CALC
117
- # if _MODEL_CALC is None:
118
- # orbff = pretrained.orb_v3_conservative_inf_omat(
119
- # device="cpu", precision="float32-high"
120
- # )
121
- # _MODEL_CALC = ORBCalculator(orbff, device="cpu")
122
- # return _MODEL_CALC
123
-
124
- def predict_molecule(structure_file,task_name,charge=0, spin_multiplicity=1):
125
  """
126
  Single Point Energy + fuerzas (OrbMol). Acepta archivos subidos.
127
  """
@@ -154,7 +188,8 @@ def predict_molecule(structure_file,task_name,charge=0, spin_multiplicity=1):
154
 
155
  return "\n".join(lines), f"Calculation completed with {task_name}", pdb_file
156
  except Exception as e:
157
- import traceback; traceback.print_exc()
 
158
  return f"Error during calculation: {e}", "Error", None
159
 
160
  # ==== Simulaciones (helpers) ====
@@ -164,7 +199,7 @@ from simulation_scripts_orbmol import (
164
  )
165
 
166
  # ==== Wrappers con debug y Molecule3D ====
167
- def md_wrapper(structure_file,task_name, charge, spin, steps, tempK, timestep_fs, ensemble):
168
  try:
169
  if not structure_file:
170
  return ("Error: Please upload a structure file", None, "", "", "", None)
@@ -193,10 +228,11 @@ def md_wrapper(structure_file,task_name, charge, spin, steps, tempK, timestep_fs
193
 
194
  except Exception as e:
195
  print(f"MD Wrapper Error: {e}")
196
- import traceback; traceback.print_exc()
 
197
  return (f"Error: {e}", None, "", "", "", None)
198
 
199
- def relax_wrapper(structure_file,task_name, steps, fmax, charge, spin, relax_cell):
200
  try:
201
  if not structure_file:
202
  return ("Error: Please upload a structure file", None, "", "", "", None)
@@ -223,7 +259,8 @@ def relax_wrapper(structure_file,task_name, steps, fmax, charge, spin, relax_cel
223
 
224
  except Exception as e:
225
  print(f"Relax Wrapper Error: {e}")
226
- import traceback; traceback.print_exc()
 
227
  return (f"Error: {e}", None, "", "", "", None)
228
 
229
  # ==== UI ====
@@ -242,7 +279,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
242
  file_count="single"
243
  )
244
  with gr.Row():
245
- task_name = gr.Radio(
246
  ["OMol", "OMat", "OMol-Direct"],
247
  value="OMol",
248
  label="Model Type",
@@ -259,26 +296,21 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
259
  spe_status = gr.Textbox(label="Status", interactive=False, max_lines=1)
260
  spe_viewer = Molecule3D(
261
  label="Input Structure Viewer",
262
- reps=[
263
- {
264
- "model": 0, "chain": "", "resname": "",
265
- "style": "stick", "color": "whiteCarbon",
266
- "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 1.0
267
- }
268
- ]
269
  )
 
270
  # Charge and Spin are only applicable to OMol and OMol-Direct
271
- task_name.input(
272
  lambda x: (
273
  (gr.Number(visible=True), gr.Number(visible=True))
274
  if x == "OMol" or x == "OMol-Direct"
275
  else (gr.Number(visible=False), gr.Number(visible=False))
276
- ),
277
- [task_name],
278
- [charge_input, spin_input],
279
- )
280
 
281
- run_spe.click(predict_molecule, [xyz_input, task_name,charge_input, spin_input], [spe_out, spe_status, spe_viewer])
282
 
283
  # -------- MD --------
284
  with gr.Tab("Molecular Dynamics"):
@@ -293,7 +325,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
293
  file_count="single"
294
  )
295
  with gr.Row():
296
- task_name = gr.Radio(
297
  ["OMol", "OMat", "OMol-Direct"],
298
  value="OMol",
299
  label="Model Type",
@@ -315,37 +347,26 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
315
  md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
316
  md_viewer = Molecule3D(
317
  label="Final Structure Viewer (Last MD Frame)",
318
- reps=[
319
- {
320
- "model": 0, "chain": "", "resname": "",
321
- "style": "stick", "color": "whiteCarbon",
322
- "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 1.0
323
- },
324
- {
325
- "model": 0, "chain": "", "resname": "",
326
- "style": "sphere", "color": "whiteCarbon",
327
- "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 0.7
328
- }
329
- ]
330
  )
331
  md_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25)
332
  md_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
333
  md_explain = gr.Markdown()
334
 
335
  # Charge and Spin are only applicable to OMol and OMol-Direct
336
- task_name.input(
337
  lambda x: (
338
  (gr.Number(visible=True), gr.Number(visible=True))
339
  if x == "OMol" or x == "OMol-Direct"
340
  else (gr.Number(visible=False), gr.Number(visible=False))
341
- ),
342
- [task_name],
343
- [charge_md, spin_md],
344
- )
345
 
346
  run_md_btn.click(
347
  md_wrapper,
348
- inputs=[xyz_md, task_name,charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
349
  outputs=[md_status, md_traj, md_log, md_script, md_explain, md_viewer],
350
  )
351
 
@@ -362,7 +383,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
362
  file_count="single"
363
  )
364
  with gr.Row():
365
- task_name = gr.Radio(
366
  ["OMol", "OMat", "OMol-Direct"],
367
  value="OMol",
368
  label="Model Type",
@@ -382,42 +403,30 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
382
  rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False)
383
  rlx_viewer = Molecule3D(
384
  label="Optimized Structure Viewer",
385
- reps=[
386
- {
387
- "model": 0, "chain": "", "resname": "",
388
- "style": "stick", "color": "whiteCarbon",
389
- "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 1.0
390
- },
391
- {
392
- "model": 0, "chain": "", "resname": "",
393
- "style": "sphere", "color": "whiteCarbon",
394
- "residue_range": "", "around": 0, "byres": False, "visible": True, "opacity": 0.7
395
- }
396
- ]
397
  )
398
  rlx_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25)
399
  rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
400
  rlx_explain = gr.Markdown()
401
 
402
  # Charge and Spin are only applicable to OMol and OMol-Direct
403
- task_name.input(
404
  lambda x: (
405
  (gr.Number(visible=True), gr.Number(visible=True))
406
  if x == "OMol" or x == "OMol-Direct"
407
  else (gr.Number(visible=False), gr.Number(visible=False))
408
- ),
409
- [task_name],
410
- [charge_rlx, spin_rlx],
411
- )
412
 
413
  run_rlx_btn.click(
414
  relax_wrapper,
415
- inputs=[xyz_rlx,task_name, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell],
416
  outputs=[rlx_status, rlx_traj, rlx_log, rlx_script, rlx_explain, rlx_viewer],
417
  )
418
 
419
  print("Starting OrbMol model loading…")
420
- #_ = load_orbmol_model(task_name)
421
 
422
  if __name__ == "__main__":
423
- 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
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
  """
 
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) ====
 
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)
 
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)
234
 
235
+ def relax_wrapper(structure_file, task_name, steps, fmax, charge, spin, relax_cell):
236
  try:
237
  if not structure_file:
238
  return ("Error: Please upload a structure file", None, "", "", "", None)
 
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)
265
 
266
  # ==== UI ====
 
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",
 
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"):
 
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",
 
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
 
 
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",
 
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)