annabossler commited on
Commit
3494b39
·
verified ·
1 Parent(s): 72ea7e1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -123
app.py CHANGED
@@ -2,19 +2,19 @@ import os
2
  import tempfile
3
  import numpy as np
4
  import gradio as gr
5
-
6
  from ase.io import read
7
  from ase.io.trajectory import Trajectory
8
 
9
- # ==== Visualizador 3D ====
10
  try:
11
  from gradio_molecule3d import Molecule3D
12
  HAVE_MOL3D = True
13
  except Exception:
14
  HAVE_MOL3D = False
15
 
 
 
16
  def traj_to_html(traj_path, width=520, height=520, interval_ms=200):
17
- """Fallback de visualización con 3Dmol.js si no hay Molecule3D."""
18
  traj = Trajectory(traj_path)
19
  xyz_frames = []
20
  for atoms in traj:
@@ -50,7 +50,7 @@ def traj_to_html(traj_path, width=520, height=520, interval_ms=200):
50
  return html
51
 
52
 
53
- # ==== OrbMol SPE directo (rápido) ====
54
  from orb_models.forcefield import pretrained
55
  from orb_models.forcefield.calculator import ORBCalculator
56
 
@@ -59,14 +59,13 @@ def _load_orbmol_calc():
59
  global _MODEL_CALC
60
  if _MODEL_CALC is None:
61
  orbff = pretrained.orb_v3_conservative_inf_omat(
62
- device="cpu",
63
- precision="float32-high"
64
  )
65
  _MODEL_CALC = ORBCalculator(orbff, device="cpu")
66
  return _MODEL_CALC
67
 
 
68
  def predict_molecule(xyz_content, charge=0, spin_multiplicity=1):
69
- """Single Point Energy/Forces con OrbMol (input en Textbox XYZ)."""
70
  try:
71
  calc = _load_orbmol_calc()
72
  if not xyz_content or not xyz_content.strip():
@@ -80,8 +79,8 @@ def predict_molecule(xyz_content, charge=0, spin_multiplicity=1):
80
  atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
81
  atoms.calc = calc
82
 
83
- energy = atoms.get_potential_energy() # eV
84
- forces = atoms.get_forces() # eV/Å
85
 
86
  lines = [f"Total Energy: {energy:.6f} eV", "", "Atomic Forces:"]
87
  for i, f in enumerate(forces):
@@ -89,30 +88,49 @@ def predict_molecule(xyz_content, charge=0, spin_multiplicity=1):
89
  max_force = float(np.max(np.linalg.norm(forces, axis=1)))
90
  lines += ["", f"Max Force: {max_force:.4f} eV/Å"]
91
 
92
- try:
93
- os.unlink(xyz_file)
94
- except Exception:
95
- pass
96
 
97
  return "\n".join(lines), "Calculation completed with OrbMol"
98
  except Exception as e:
99
  return f"Error during calculation: {e}", "Error"
100
 
101
 
102
- # ==== Simulación (estilo UMA) vía helpers locales ====
103
  from simulation_scripts_orbmol import (
104
  run_md_simulation,
105
  run_relaxation_simulation,
106
- last_frame_xyz_from_traj,
107
  )
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  def md_wrapper(xyz_content, charge, spin, steps, tempK, timestep_fs, ensemble):
110
- """Conecta Gradio con run_md_simulation y adapta outputs (viewer/HTML/file/log/script)."""
 
111
  try:
 
 
112
  traj_path, log_text, script_text, explanation = run_md_simulation(
113
- xyz_content, # acepta string XYZ o ruta
114
  int(steps),
115
- 20, # prerelax por defecto, como UMA
116
  float(timestep_fs),
117
  float(tempK),
118
  "NVT" if ensemble == "NVT" else "NVE",
@@ -121,30 +139,25 @@ def md_wrapper(xyz_content, charge, spin, steps, tempK, timestep_fs, ensemble):
121
  )
122
  status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})"
123
 
124
- if HAVE_MOL3D:
125
- viewer_value = last_frame_xyz_from_traj(traj_path) # string XYZ como value
126
- html_value = None
127
- else:
128
- viewer_value = None
129
- html_value = traj_to_html(traj_path)
130
-
131
- return (
132
- status, # md_status
133
- viewer_value, # md_viewer (Molecule3D value) o None
134
- html_value, # md_html (fallback) o None
135
- traj_path, # md_traj file
136
- log_text, # md_log (Textbox)
137
- script_text, # md_script (Code py)
138
- explanation, # md_explain
139
- )
140
  except Exception as e:
141
- return (f"Error: {e}", None, None, None, "", "", "")
 
 
 
 
 
142
 
143
  def relax_wrapper(xyz_content, steps, fmax, charge, spin, relax_cell):
144
- """Conecta Gradio con run_relaxation_simulation y adapta outputs."""
 
145
  try:
 
 
146
  traj_path, log_text, script_text, explanation = run_relaxation_simulation(
147
- xyz_content,
148
  int(steps),
149
  float(fmax),
150
  int(charge),
@@ -153,24 +166,15 @@ def relax_wrapper(xyz_content, steps, fmax, charge, spin, relax_cell):
153
  )
154
  status = f"Relaxation finished (≤ {int(steps)} steps, fmax={float(fmax)} eV/Å)"
155
 
156
- if HAVE_MOL3D:
157
- viewer_value = last_frame_xyz_from_traj(traj_path)
158
- html_value = None
159
- else:
160
- viewer_value = None
161
- html_value = traj_to_html(traj_path)
162
-
163
- return (
164
- status, # rlx_status
165
- viewer_value, # rlx_viewer (Molecule3D value) o None
166
- html_value, # rlx_html (fallback) o None
167
- traj_path, # rlx_traj file
168
- log_text, # rlx_log (Textbox)
169
- script_text, # rlx_script (Code py)
170
- explanation, # rlx_explain
171
- )
172
  except Exception as e:
173
- return (f"Error: {e}", None, None, None, "", "", "")
 
 
 
 
174
 
175
 
176
  # ==== Ejemplos ====
@@ -194,132 +198,114 @@ H -0.3630 -0.5133 -0.8887""", 0, 1],
194
  ]
195
 
196
 
197
- # ==== UI Gradio ====
198
  with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
199
  with gr.Tabs():
200
- # --- Tab SPE ---
201
  with gr.Tab("Single Point Energy"):
202
  with gr.Row():
203
  with gr.Column(scale=2):
204
- gr.Markdown("# OrbMol Demo — Quantum-Accurate Molecular Predictions")
205
- gr.Markdown(
206
- "Predict **energies** and **forces** with OrbMol (trained on **OMol25**, "
207
- "ωB97M-V/def2-TZVPD). Supports **charge** and **spin multiplicity**."
208
- )
209
- xyz_input = gr.Textbox(
210
- label="XYZ Coordinates",
211
- placeholder="Paste XYZ here...",
212
- lines=12,
213
- )
214
  with gr.Row():
215
- charge_input = gr.Slider(value=0, minimum=-10, maximum=10, step=1, label="Charge")
216
- spin_input = gr.Slider(value=1, minimum=1, maximum=11, step=1, label="Spin Multiplicity")
217
  run_spe = gr.Button("Run OrbMol Prediction", variant="primary")
218
-
219
  with gr.Column(variant="panel", min_width=500):
220
  spe_out = gr.Textbox(label="Energy & Forces", lines=15, interactive=False)
221
  spe_status = gr.Textbox(label="Status", interactive=False, max_lines=1)
222
 
223
- gr.Examples(examples=examples, inputs=[xyz_input, charge_input, spin_input], label="Examples")
224
- run_spe.click(
225
- predict_molecule,
226
- inputs=[xyz_input, charge_input, spin_input],
227
- outputs=[spe_out, spe_status],
228
- )
229
 
230
- with gr.Sidebar(open=True):
231
- gr.Markdown("## Learn more about OrbMol")
232
- with gr.Accordion("What is OrbMol?", open=False):
233
- gr.Markdown(
234
- "* Neural network potential for molecules\n"
235
- "* Built on Orb-v3, trained on OMol25 (ωB97M-V/def2-TZVPD)\n"
236
- "* Supports charge and spin multiplicity"
237
- )
238
- with gr.Accordion("Benchmarks", open=False):
239
- gr.Markdown(
240
- "* Strong results on **GMTKN55** y **Wiggle150**\n"
241
- "* Accurate **protein–ligand** energies (PLA15)\n"
242
- "* Stable MD en biomoléculas grandes"
243
- )
244
- with gr.Accordion("Disclaimers", open=False):
245
- gr.Markdown(
246
- "* Verifica resultados para tu caso\n"
247
- "* Considera el **nivel de teoría** de entrenamiento"
248
- )
249
-
250
- # --- Tab MD ---
251
  with gr.Tab("Molecular Dynamics"):
252
  with gr.Row():
253
  with gr.Column(scale=2):
254
- xyz_md = gr.Textbox(label="XYZ Coordinates", lines=12, placeholder="Paste XYZ here...")
255
  with gr.Row():
256
- charge_md = gr.Slider(value=0, minimum=-10, maximum=10, step=1, label="Charge")
257
- spin_md = gr.Slider(value=1, minimum=1, maximum=11, step=1, label="Spin Multiplicity")
258
  with gr.Row():
259
- steps_md = gr.Slider(value=100, minimum=10, maximum=2000, step=10, label="Steps")
260
- temp_md = gr.Slider(value=300, minimum=10, maximum=1500, step=10, label="Temperature (K)")
261
  with gr.Row():
262
- timestep_md = gr.Slider(value=1.0, minimum=0.1, maximum=5.0, step=0.1, label="Timestep (fs)")
263
- ensemble_md = gr.Radio(choices=["NVE", "NVT"], value="NVE", label="Ensemble")
264
  run_md_btn = gr.Button("Run MD Simulation", variant="primary")
265
 
266
  with gr.Column(variant="panel", min_width=520):
267
  md_status = gr.Textbox(label="MD Status", interactive=False)
268
 
 
 
 
 
269
  if HAVE_MOL3D:
270
- md_viewer = Molecule3D(label="Trajectory Viewer")
271
- md_html = gr.HTML(visible=False) # placeholder para layout consistente
 
 
 
 
 
272
  else:
273
- md_viewer = gr.Textbox(visible=False) # placeholder
274
- md_html = gr.HTML()
275
 
276
- md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
277
- md_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25) # <- FIX Code->Textbox
278
  md_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
279
  md_explain = gr.Markdown()
280
 
 
281
  run_md_btn.click(
282
  md_wrapper,
283
  inputs=[xyz_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
284
- outputs=[md_status, md_viewer, md_html, md_traj, md_log, md_script, md_explain],
285
  )
286
 
287
- # --- Tab Relaxation ---
288
  with gr.Tab("Relaxation / Optimization"):
289
  with gr.Row():
290
  with gr.Column(scale=2):
291
- xyz_rlx = gr.Textbox(label="XYZ Coordinates", lines=12, placeholder="Paste XYZ here...")
292
- steps_rlx = gr.Slider(value=300, minimum=1, maximum=2000, step=1, label="Max Steps")
293
- fmax_rlx = gr.Slider(value=0.05, minimum=0.001, maximum=0.5, step=0.001, label="Fmax (eV/Å)")
294
  with gr.Row():
295
- charge_rlx = gr.Slider(value=0, minimum=-10, maximum=10, step=1, label="Charge")
296
- spin_rlx = gr.Slider(value=1, minimum=1, maximum=11, step=1, label="Spin")
297
- relax_cell = gr.Checkbox(label="Relax Unit Cell", value=False)
298
  run_rlx_btn = gr.Button("Run Optimization", variant="primary")
299
 
300
  with gr.Column(variant="panel", min_width=520):
301
  rlx_status = gr.Textbox(label="Status", interactive=False)
302
 
 
303
  if HAVE_MOL3D:
304
- rlx_viewer = Molecule3D(label="Final Structure")
 
 
 
 
 
305
  rlx_html = gr.HTML(visible=False)
306
  else:
 
307
  rlx_viewer = gr.Textbox(visible=False)
308
- rlx_html = gr.HTML()
309
 
310
- rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False)
311
- rlx_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25) # <- FIX Code->Textbox
312
  rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
313
  rlx_explain = gr.Markdown()
314
 
315
  run_rlx_btn.click(
316
  relax_wrapper,
317
  inputs=[xyz_rlx, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell],
318
- outputs=[rlx_status, rlx_viewer, rlx_html, rlx_traj, rlx_log, rlx_script, rlx_explain],
319
  )
320
 
321
  print("Starting OrbMol model loading…")
322
- _LOAD = _load_orbmol_calc()
323
 
324
  if __name__ == "__main__":
325
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
2
  import tempfile
3
  import numpy as np
4
  import gradio as gr
 
5
  from ase.io import read
6
  from ase.io.trajectory import Trajectory
7
 
8
+ # ==== Intentar visor nativo como en UMA ====
9
  try:
10
  from gradio_molecule3d import Molecule3D
11
  HAVE_MOL3D = True
12
  except Exception:
13
  HAVE_MOL3D = False
14
 
15
+
16
+ # ==== Fallback HTML con 3Dmol.js ====
17
  def traj_to_html(traj_path, width=520, height=520, interval_ms=200):
 
18
  traj = Trajectory(traj_path)
19
  xyz_frames = []
20
  for atoms in traj:
 
50
  return html
51
 
52
 
53
+ # ==== OrbMol SPE directo ====
54
  from orb_models.forcefield import pretrained
55
  from orb_models.forcefield.calculator import ORBCalculator
56
 
 
59
  global _MODEL_CALC
60
  if _MODEL_CALC is None:
61
  orbff = pretrained.orb_v3_conservative_inf_omat(
62
+ device="cpu", precision="float32-high"
 
63
  )
64
  _MODEL_CALC = ORBCalculator(orbff, device="cpu")
65
  return _MODEL_CALC
66
 
67
+
68
  def predict_molecule(xyz_content, charge=0, spin_multiplicity=1):
 
69
  try:
70
  calc = _load_orbmol_calc()
71
  if not xyz_content or not xyz_content.strip():
 
79
  atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
80
  atoms.calc = calc
81
 
82
+ energy = atoms.get_potential_energy()
83
+ forces = atoms.get_forces()
84
 
85
  lines = [f"Total Energy: {energy:.6f} eV", "", "Atomic Forces:"]
86
  for i, f in enumerate(forces):
 
88
  max_force = float(np.max(np.linalg.norm(forces, axis=1)))
89
  lines += ["", f"Max Force: {max_force:.4f} eV/Å"]
90
 
91
+ try: os.unlink(xyz_file)
92
+ except Exception: pass
 
 
93
 
94
  return "\n".join(lines), "Calculation completed with OrbMol"
95
  except Exception as e:
96
  return f"Error during calculation: {e}", "Error"
97
 
98
 
99
+ # ==== Simulaciones (helpers locales) ====
100
  from simulation_scripts_orbmol import (
101
  run_md_simulation,
102
  run_relaxation_simulation,
 
103
  )
104
 
105
+ # Convierte textbox XYZ a fichero temporal si es necesario (para ASE)
106
+ def _string_looks_like_xyz(text: str) -> bool:
107
+ try:
108
+ first = (text or "").strip().splitlines()[0]
109
+ int(first.split()[0])
110
+ return True
111
+ except Exception:
112
+ return False
113
+
114
+ def _to_file_if_xyz(input_or_path: str):
115
+ if isinstance(input_or_path, str) and _string_looks_like_xyz(input_or_path):
116
+ tf = tempfile.NamedTemporaryFile(mode="w", suffix=".xyz", delete=False)
117
+ tf.write(input_or_path)
118
+ tf.flush(); tf.close()
119
+ return tf.name, True
120
+ return input_or_path, False
121
+
122
+
123
+ # Wrappers: devuelven SIEMPRE (status, traj_path, log, script, explain, html_fallback)
124
  def md_wrapper(xyz_content, charge, spin, steps, tempK, timestep_fs, ensemble):
125
+ tmp_created = False
126
+ path_or_str = xyz_content
127
  try:
128
+ path_or_str, tmp_created = _to_file_if_xyz(xyz_content)
129
+
130
  traj_path, log_text, script_text, explanation = run_md_simulation(
131
+ path_or_str,
132
  int(steps),
133
+ 20, # pre-relax
134
  float(timestep_fs),
135
  float(tempK),
136
  "NVT" if ensemble == "NVT" else "NVE",
 
139
  )
140
  status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})"
141
 
142
+ html_value = "" if HAVE_MOL3D else traj_to_html(traj_path)
143
+ return (status, traj_path, log_text, script_text, explanation, html_value)
144
+
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  except Exception as e:
146
+ return (f"Error: {e}", None, "", "", "", "")
147
+ finally:
148
+ if tmp_created and isinstance(path_or_str, str) and os.path.exists(path_or_str):
149
+ try: os.remove(path_or_str)
150
+ except Exception: pass
151
+
152
 
153
  def relax_wrapper(xyz_content, steps, fmax, charge, spin, relax_cell):
154
+ tmp_created = False
155
+ path_or_str = xyz_content
156
  try:
157
+ path_or_str, tmp_created = _to_file_if_xyz(xyz_content)
158
+
159
  traj_path, log_text, script_text, explanation = run_relaxation_simulation(
160
+ path_or_str,
161
  int(steps),
162
  float(fmax),
163
  int(charge),
 
166
  )
167
  status = f"Relaxation finished (≤ {int(steps)} steps, fmax={float(fmax)} eV/Å)"
168
 
169
+ html_value = "" if HAVE_MOL3D else traj_to_html(traj_path)
170
+ return (status, traj_path, log_text, script_text, explanation, html_value)
171
+
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  except Exception as e:
173
+ return (f"Error: {e}", None, "", "", "", "")
174
+ finally:
175
+ if tmp_created and isinstance(path_or_str, str) and os.path.exists(path_or_str):
176
+ try: os.remove(path_or_str)
177
+ except Exception: pass
178
 
179
 
180
  # ==== Ejemplos ====
 
198
  ]
199
 
200
 
201
+ # ==== UI ====
202
  with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
203
  with gr.Tabs():
204
+ # -------- SPE --------
205
  with gr.Tab("Single Point Energy"):
206
  with gr.Row():
207
  with gr.Column(scale=2):
208
+ gr.Markdown("# OrbMol — Quantum-Accurate Molecular Predictions")
209
+ gr.Markdown("Energías y fuerzas con **charge** y **spin multiplicity**.")
210
+ xyz_input = gr.Textbox(label="XYZ Coordinates", lines=12, placeholder="Paste XYZ here…")
 
 
 
 
 
 
 
211
  with gr.Row():
212
+ charge_input = gr.Slider(0, -10, 10, 1, label="Charge")
213
+ spin_input = gr.Slider(1, 1, 11, 1, label="Spin Multiplicity")
214
  run_spe = gr.Button("Run OrbMol Prediction", variant="primary")
 
215
  with gr.Column(variant="panel", min_width=500):
216
  spe_out = gr.Textbox(label="Energy & Forces", lines=15, interactive=False)
217
  spe_status = gr.Textbox(label="Status", interactive=False, max_lines=1)
218
 
219
+ gr.Examples(examples=examples, inputs=[xyz_input, charge_input, spin_input])
220
+ run_spe.click(predict_molecule, [xyz_input, charge_input, spin_input], [spe_out, spe_status])
 
 
 
 
221
 
222
+ # -------- MD --------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  with gr.Tab("Molecular Dynamics"):
224
  with gr.Row():
225
  with gr.Column(scale=2):
226
+ xyz_md = gr.Textbox(label="XYZ Coordinates", lines=12, placeholder="Paste XYZ here")
227
  with gr.Row():
228
+ charge_md = gr.Slider(0, -10, 10, 1, label="Charge")
229
+ spin_md = gr.Slider(1, 1, 11, 1, label="Spin Multiplicity")
230
  with gr.Row():
231
+ steps_md = gr.Slider(100, 10, 2000, 10, label="Steps")
232
+ temp_md = gr.Slider(300, 10, 1500, 10, label="Temperature (K)")
233
  with gr.Row():
234
+ timestep_md = gr.Slider(1.0, 0.1, 5.0, 0.1, label="Timestep (fs)")
235
+ ensemble_md = gr.Radio(["NVE", "NVT"], value="NVE", label="Ensemble")
236
  run_md_btn = gr.Button("Run MD Simulation", variant="primary")
237
 
238
  with gr.Column(variant="panel", min_width=520):
239
  md_status = gr.Textbox(label="MD Status", interactive=False)
240
 
241
+ # Archivo de trayectoria SIEMPRE (Molecule3D lo consume)
242
+ md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
243
+
244
+ # Visor nativo si está disponible
245
  if HAVE_MOL3D:
246
+ md_viewer = Molecule3D(
247
+ label="Trajectory Viewer",
248
+ inputs=[md_traj],
249
+ value=lambda x: x,
250
+ interactive=False,
251
+ )
252
+ md_html = gr.HTML(visible=False)
253
  else:
254
+ md_html = gr.HTML(label="Trajectory Viewer")
255
+ md_viewer = gr.Textbox(visible=False)
256
 
257
+ md_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25)
 
258
  md_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
259
  md_explain = gr.Markdown()
260
 
261
+ # NOTA: no actualizamos md_viewer directamente; se refresca al cambiar md_traj
262
  run_md_btn.click(
263
  md_wrapper,
264
  inputs=[xyz_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
265
+ outputs=[md_status, md_traj, md_log, md_script, md_explain, md_html],
266
  )
267
 
268
+ # -------- Relax --------
269
  with gr.Tab("Relaxation / Optimization"):
270
  with gr.Row():
271
  with gr.Column(scale=2):
272
+ xyz_rlx = gr.Textbox(label="XYZ Coordinates", lines=12, placeholder="Paste XYZ here")
273
+ steps_rlx = gr.Slider(300, 1, 2000, 1, label="Max Steps")
274
+ fmax_rlx = gr.Slider(0.05, 0.001, 0.5, 0.001, label="Fmax (eV/Å)")
275
  with gr.Row():
276
+ charge_rlx = gr.Slider(0, -10, 10, 1, label="Charge")
277
+ spin_rlx = gr.Slider(1, 1, 11, 1, label="Spin")
278
+ relax_cell = gr.Checkbox(False, label="Relax Unit Cell")
279
  run_rlx_btn = gr.Button("Run Optimization", variant="primary")
280
 
281
  with gr.Column(variant="panel", min_width=520):
282
  rlx_status = gr.Textbox(label="Status", interactive=False)
283
 
284
+ rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False)
285
  if HAVE_MOL3D:
286
+ rlx_viewer = Molecule3D(
287
+ label="Final Structure",
288
+ inputs=[rlx_traj],
289
+ value=lambda x: x,
290
+ interactive=False,
291
+ )
292
  rlx_html = gr.HTML(visible=False)
293
  else:
294
+ rlx_html = gr.HTML(label="Final Structure")
295
  rlx_viewer = gr.Textbox(visible=False)
 
296
 
297
+ rlx_log = gr.Textbox(label="Log", interactive=False, lines=15, max_lines=25)
 
298
  rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
299
  rlx_explain = gr.Markdown()
300
 
301
  run_rlx_btn.click(
302
  relax_wrapper,
303
  inputs=[xyz_rlx, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell],
304
+ outputs=[rlx_status, rlx_traj, rlx_log, rlx_script, rlx_explain, rlx_html],
305
  )
306
 
307
  print("Starting OrbMol model loading…")
308
+ _ = _load_orbmol_calc()
309
 
310
  if __name__ == "__main__":
311
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)