annabossler commited on
Commit
0ade04a
·
verified ·
1 Parent(s): 9223603

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +201 -158
app.py CHANGED
@@ -1,90 +1,23 @@
1
- import gradio as gr
2
- import numpy as np
3
- import tempfile
4
  import os
 
 
 
5
 
6
  from ase.io import read
7
- from ase import units
8
  from ase.optimize import LBFGS
9
- from ase.md.verlet import VelocityVerlet
10
  from ase.md.velocitydistribution import MaxwellBoltzmannDistribution
 
11
  from ase.io.trajectory import Trajectory
 
12
 
13
- # Intentar importar Molecule3D para vista 3D nativa
14
  try:
15
  from gradio_molecule3d import Molecule3D
16
  HAVE_MOL3D = True
17
  except Exception:
18
  HAVE_MOL3D = False
19
 
20
- from orb_models.forcefield import pretrained
21
- from orb_models.forcefield.calculator import ORBCalculator
22
-
23
-
24
- # =========================
25
- # OrbMol global model
26
- # =========================
27
- model_calc = None
28
-
29
- def load_orbmol_model():
30
- global model_calc
31
- if model_calc is None:
32
- try:
33
- print("Loading OrbMol model...")
34
- orbff = pretrained.orb_v3_conservative_inf_omat(
35
- device="cpu",
36
- precision="float32-high"
37
- )
38
- model_calc = ORBCalculator(orbff, device="cpu")
39
- print("OrbMol model loaded successfully")
40
- except Exception as e:
41
- print(f"Error loading model: {e}")
42
- model_calc = None
43
- return model_calc
44
-
45
-
46
- # =========================
47
- # Single-point (SPE)
48
- # =========================
49
- def predict_molecule(xyz_content, charge=0, spin_multiplicity=1):
50
- try:
51
- calc = load_orbmol_model()
52
- if calc is None:
53
- return "Error: Could not load OrbMol model", ""
54
-
55
- if not xyz_content.strip():
56
- return "Error: Please enter XYZ coordinates", ""
57
-
58
- with tempfile.NamedTemporaryFile(mode="w", suffix=".xyz", delete=False) as f:
59
- f.write(xyz_content)
60
- xyz_file = f.name
61
-
62
- atoms = read(xyz_file)
63
- atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
64
- atoms.calc = calc
65
-
66
- energy = atoms.get_potential_energy() # eV
67
- forces = atoms.get_forces() # eV/Å
68
-
69
- lines = []
70
- lines.append(f"Total Energy: {energy:.6f} eV\n")
71
- lines.append("Atomic Forces:")
72
- for i, f in enumerate(forces):
73
- lines.append(f"Atom {i+1}: [{f[0]:.4f}, {f[1]:.4f}, {f[2]:.4f}] eV/Å")
74
-
75
- max_force = float(np.max(np.linalg.norm(forces, axis=1)))
76
- lines.append(f"\nMax Force: {max_force:.4f} eV/Å")
77
-
78
- os.unlink(xyz_file)
79
- return "\n".join(lines), "Calculation completed with OrbMol"
80
-
81
- except Exception as e:
82
- return f"Error during calculation: {str(e)}", "Error"
83
-
84
-
85
- # =========================
86
- # Trajectory → HTML 3D fallback
87
- # =========================
88
  def traj_to_html(traj_path, width=520, height=520, interval_ms=200):
89
  traj = Trajectory(traj_path)
90
  xyz_frames = []
@@ -120,21 +53,24 @@ def traj_to_html(traj_path, width=520, height=520, interval_ms=200):
120
  """
121
  return html
122
 
 
 
 
123
 
124
- # =========================
125
- # MD with OrbMol
126
- # =========================
127
- def run_md(xyz_content, charge=0, spin_multiplicity=1,
128
- steps=100, temperature=300, timestep=1.0):
129
- try:
130
- calc = load_orbmol_model()
131
- if calc is None:
132
- return "Error: Could not load OrbMol model", ""
133
 
 
 
 
134
  if not xyz_content.strip():
135
- return "Error: Please enter XYZ coordinates", ""
136
 
137
- # Leer estructura
138
  with tempfile.NamedTemporaryFile(mode="w", suffix=".xyz", delete=False) as f:
139
  f.write(xyz_content)
140
  xyz_file = f.name
@@ -143,56 +79,109 @@ def run_md(xyz_content, charge=0, spin_multiplicity=1,
143
  atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
144
  atoms.calc = calc
145
 
146
- # Pre-relajación ligera
147
- opt = LBFGS(atoms, logfile=None)
148
- opt.run(fmax=0.05, steps=20)
149
-
150
- MaxwellBoltzmannDistribution(atoms, temperature_K=2*float(temperature))
151
-
152
- dyn = VelocityVerlet(atoms, timestep=float(timestep) * units.fs)
153
-
154
- tf = tempfile.NamedTemporaryFile(suffix=".traj", delete=False)
155
- tf.close()
156
- traj = Trajectory(tf.name, "w", atoms)
157
- dyn.attach(traj.write, interval=1)
158
- dyn.run(int(steps))
159
 
160
- if HAVE_MOL3D:
161
- # Mostrar último frame en Molecule3D
162
- last = traj[-1]
163
- mol_xyz = f"{len(last)}\nFinal frame\n"
164
- for s, (x, y, z) in zip(last.get_chemical_symbols(), last.get_positions()):
165
- mol_xyz += f"{s} {x:.6f} {y:.6f} {z:.6f}\n"
166
- view = Molecule3D(value=mol_xyz, label="Final Frame (XYZ)")
167
- else:
168
- view = traj_to_html(tf.name)
169
 
170
  try:
171
  os.unlink(xyz_file)
172
  except Exception:
173
  pass
174
 
175
- return f"MD completed: {int(steps)} steps at {int(temperature)} K", view
 
 
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  except Exception as e:
178
- return f"Error during MD simulation: {str(e)}", ""
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
- # =========================
182
- # Ejemplos
183
- # =========================
184
  examples = [
185
  ["""2
186
  Hydrogen molecule
187
  H 0.0 0.0 0.0
188
  H 0.0 0.0 0.74""", 0, 1],
189
-
190
  ["""3
191
- Water molecule
192
  O 0.0000 0.0000 0.0000
193
  H 0.7571 0.0000 0.5864
194
  H -0.7571 0.0000 0.5864""", 0, 1],
195
-
196
  ["""5
197
  Methane
198
  C 0.0000 0.0000 0.0000
@@ -202,78 +191,132 @@ H -0.3630 -0.5133 0.8887
202
  H -0.3630 -0.5133 -0.8887""", 0, 1],
203
  ]
204
 
205
-
206
- # =========================
207
- # Gradio UI
208
- # =========================
209
  with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
210
  with gr.Tabs():
211
- # -------- Tab 1: Single Point --------
212
  with gr.Tab("Single Point Energy"):
213
  with gr.Row():
214
  with gr.Column(scale=2):
215
- with gr.Column(variant="panel"):
216
- gr.Markdown("# OrbMol Demo - Quantum-Accurate Predictions")
217
- gr.Markdown("OrbMol is a neural network potential trained on the OMol25 dataset.")
218
-
219
- xyz_input = gr.Textbox(
220
- label="XYZ Coordinates",
221
- placeholder="Paste XYZ here...",
222
- lines=12,
223
- )
224
-
225
- with gr.Row():
226
- charge_input = gr.Slider(value=0, minimum=-10, maximum=10, step=1, label="Charge")
227
- spin_input = gr.Slider(value=1, minimum=1, maximum=11, step=1, label="Spin Multiplicity")
228
-
229
- predict_btn = gr.Button("Run OrbMol Prediction", variant="primary")
230
 
231
  with gr.Column(variant="panel", min_width=500):
232
- results_output = gr.Textbox(label="Energy & Forces", lines=15, interactive=False)
233
- status_output = gr.Textbox(label="Status", interactive=False, max_lines=1)
234
-
235
- gr.Examples(examples=examples, inputs=[xyz_input, charge_input, spin_input])
236
 
237
- predict_btn.click(
 
238
  predict_molecule,
239
  inputs=[xyz_input, charge_input, spin_input],
240
- outputs=[results_output, status_output],
241
  )
242
 
243
  with gr.Sidebar(open=True):
244
  gr.Markdown("## Learn more about OrbMol")
245
  with gr.Accordion("What is OrbMol?", open=False):
246
- gr.Markdown("* Neural network potential for molecules\n* Built on Orb-v3, trained on OMol25\n* Supports charge and spin")
 
 
 
 
247
  with gr.Accordion("Benchmarks", open=False):
248
- gr.Markdown("* <1 kcal/mol error on Wiggle150\n* Accurate protein–ligand binding energies\n* Stable MD on biomolecules >20k atoms")
 
 
 
 
249
  with gr.Accordion("Disclaimers", open=False):
250
- gr.Markdown("* Validate results for your use case\n* Training level of theory may limit accuracy")
 
 
 
251
 
252
- # -------- Tab 2: MD --------
253
  with gr.Tab("Molecular Dynamics"):
254
  with gr.Row():
255
  with gr.Column(scale=2):
256
- xyz_input_md = gr.Textbox(label="XYZ Coordinates", lines=12)
257
- charge_input_md = gr.Slider(value=0, minimum=-10, maximum=10, step=1, label="Charge")
258
- spin_input_md = gr.Slider(value=1, minimum=1, maximum=11, step=1, label="Spin Multiplicity")
259
- steps_input = gr.Slider(value=100, minimum=10, maximum=1000, step=10, label="Steps")
260
- temp_input = gr.Slider(value=300, minimum=10, maximum=1000, step=10, label="Temperature (K)")
261
- timestep_input = gr.Slider(value=1.0, minimum=0.1, maximum=5.0, step=0.1, label="Timestep (fs)")
 
 
 
 
262
  run_md_btn = gr.Button("Run MD Simulation", variant="primary")
263
 
264
  with gr.Column(variant="panel", min_width=520):
265
- md_status = gr.Textbox(label="MD Status", lines=2, interactive=False)
266
- md_view = gr.HTML() if not HAVE_MOL3D else Molecule3D(label="Trajectory Viewer")
 
 
 
 
 
 
 
 
 
 
267
 
268
  run_md_btn.click(
269
- run_md,
270
- inputs=[xyz_input_md, charge_input_md, spin_input_md, steps_input, temp_input, timestep_input],
271
- outputs=[md_status, md_view],
272
  )
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
- print("Starting OrbMol model loading...")
276
- load_orbmol_model()
277
 
278
  if __name__ == "__main__":
279
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
 
 
 
1
  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.optimize import LBFGS
 
8
  from ase.md.velocitydistribution import MaxwellBoltzmannDistribution
9
+ from ase.md.verlet import VelocityVerlet
10
  from ase.io.trajectory import Trajectory
11
+ from ase import units
12
 
13
+ # Visualizador 3D “pro” si está disponible
14
  try:
15
  from gradio_molecule3d import Molecule3D
16
  HAVE_MOL3D = True
17
  except Exception:
18
  HAVE_MOL3D = False
19
 
20
+ # --- Helpers de visualización: fallback 3Dmol.js si no hay Molecule3D ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def traj_to_html(traj_path, width=520, height=520, interval_ms=200):
22
  traj = Trajectory(traj_path)
23
  xyz_frames = []
 
53
  """
54
  return html
55
 
56
+ # --- OrbMol directo para SPE ---
57
+ from orb_models.forcefield import pretrained
58
+ from orb_models.forcefield.calculator import ORBCalculator
59
 
60
+ _model_calc = None
61
+ def _load_orbmol_calc():
62
+ global _model_calc
63
+ if _model_calc is None:
64
+ orbff = pretrained.orb_v3_conservative_inf_omat(device="cpu", precision="float32-high")
65
+ _model_calc = ORBCalculator(orbff, device="cpu")
66
+ return _model_calc
 
 
67
 
68
+ def predict_molecule(xyz_content, charge=0, spin_multiplicity=1):
69
+ try:
70
+ calc = _load_orbmol_calc()
71
  if not xyz_content.strip():
72
+ return "Error: Please enter XYZ coordinates", "Error"
73
 
 
74
  with tempfile.NamedTemporaryFile(mode="w", suffix=".xyz", delete=False) as f:
75
  f.write(xyz_content)
76
  xyz_file = f.name
 
79
  atoms.info = {"charge": int(charge), "spin": int(spin_multiplicity)}
80
  atoms.calc = calc
81
 
82
+ energy = atoms.get_potential_energy() # eV
83
+ forces = atoms.get_forces() # eV/Å
 
 
 
 
 
 
 
 
 
 
 
84
 
85
+ lines = [f"Total Energy: {energy:.6f} eV", "", "Atomic Forces:"]
86
+ for i, f in enumerate(forces):
87
+ lines.append(f"Atom {i+1}: [{f[0]:.4f}, {f[1]:.4f}, {f[2]:.4f}] eV/Å")
88
+ max_force = float(np.max(np.linalg.norm(forces, axis=1)))
89
+ lines += ["", f"Max Force: {max_force:.4f} eV/Å"]
 
 
 
 
90
 
91
  try:
92
  os.unlink(xyz_file)
93
  except Exception:
94
  pass
95
 
96
+ return "\n".join(lines), "Calculation completed with OrbMol"
97
+ except Exception as e:
98
+ return f"Error during calculation: {e}", "Error"
99
 
100
+ # --- Importa las rutinas FAIRChem-like para MD/Relax (ya soportan string XYZ o ruta) ---
101
+ from simulation_scripts_orbmol import (
102
+ run_md_simulation,
103
+ run_relaxation_simulation,
104
+ last_frame_xyz_from_traj,
105
+ )
106
+
107
+ # Wrappers para conectar outputs de Gradio correctamente (string XYZ / HTML, file, logs...)
108
+ def md_wrapper(xyz_content, charge, spin, steps, tempK, timestep_fs, ensemble):
109
+ try:
110
+ traj_path, log_text, script_text, explanation = run_md_simulation(
111
+ xyz_content, # acepta string XYZ
112
+ int(steps),
113
+ 20, # pre-relax steps como en la UI de UMA
114
+ float(timestep_fs),
115
+ float(tempK),
116
+ "NVT" if ensemble == "NVT" else "NVE",
117
+ int(charge),
118
+ int(spin),
119
+ )
120
+ status = f"MD completed: {int(steps)} steps at {int(tempK)} K ({ensemble})"
121
+ # Viewer
122
+ if HAVE_MOL3D:
123
+ xyz_final = last_frame_xyz_from_traj(traj_path) # value para Molecule3D
124
+ viewer_value = xyz_final
125
+ html_value = None
126
+ else:
127
+ viewer_value = None
128
+ html_value = traj_to_html(traj_path)
129
+
130
+ return (
131
+ status, # MD Status
132
+ viewer_value, # Molecule3D value (o None)
133
+ html_value, # HTML fallback (o None)
134
+ traj_path, # File download
135
+ log_text, # Log
136
+ script_text, # Script
137
+ explanation, # Explanation
138
+ )
139
  except Exception as e:
140
+ return (f"Error: {e}", None, None, None, "", "", "")
141
 
142
+ def relax_wrapper(xyz_content, steps, fmax, charge, spin, relax_cell):
143
+ try:
144
+ traj_path, log_text, script_text, explanation = run_relaxation_simulation(
145
+ xyz_content,
146
+ int(steps),
147
+ float(fmax),
148
+ int(charge),
149
+ int(spin),
150
+ bool(relax_cell),
151
+ )
152
+ status = f"Relaxation finished (≤ {int(steps)} steps, fmax={float(fmax)} eV/Å)"
153
+ if HAVE_MOL3D:
154
+ viewer_value = last_frame_xyz_from_traj(traj_path)
155
+ html_value = None
156
+ else:
157
+ viewer_value = None
158
+ html_value = traj_to_html(traj_path)
159
+
160
+ return (
161
+ status,
162
+ viewer_value,
163
+ html_value,
164
+ traj_path,
165
+ log_text,
166
+ script_text,
167
+ explanation,
168
+ )
169
+ except Exception as e:
170
+ return (f"Error: {e}", None, None, None, "", "", "")
171
 
172
+ # ------------------------
173
+ # Ejemplos rápidos
174
+ # ------------------------
175
  examples = [
176
  ["""2
177
  Hydrogen molecule
178
  H 0.0 0.0 0.0
179
  H 0.0 0.0 0.74""", 0, 1],
 
180
  ["""3
181
+ Water molecule
182
  O 0.0000 0.0000 0.0000
183
  H 0.7571 0.0000 0.5864
184
  H -0.7571 0.0000 0.5864""", 0, 1],
 
185
  ["""5
186
  Methane
187
  C 0.0000 0.0000 0.0000
 
191
  H -0.3630 -0.5133 -0.8887""", 0, 1],
192
  ]
193
 
194
+ # ------------------------
195
+ # UI Gradio
196
+ # ------------------------
 
197
  with gr.Blocks(theme=gr.themes.Ocean(), title="OrbMol Demo") as demo:
198
  with gr.Tabs():
199
+ # ========== SPE ==========
200
  with gr.Tab("Single Point Energy"):
201
  with gr.Row():
202
  with gr.Column(scale=2):
203
+ gr.Markdown("# OrbMol Demo — Quantum-Accurate Molecular Predictions")
204
+ gr.Markdown(
205
+ "Predict **energies** and **forces** with OrbMol (OMol25). "
206
+ "Supports **charge** and **spin multiplicity**."
207
+ )
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
+ "* Near-DFT accuracy on **GMTKN55** and **Wiggle150**\n"
241
+ "* Accurate **protein–ligand** interaction energies (PLA15)\n"
242
+ "* Stable long MD on biomolecules grandes"
243
+ )
244
  with gr.Accordion("Disclaimers", open=False):
245
+ gr.Markdown(
246
+ "* Validate for your use case\n"
247
+ "* Consider training **level of theory** and intended domain"
248
+ )
249
 
250
+ # ========== 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=1000, 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
+ if HAVE_MOL3D:
269
+ md_viewer = Molecule3D(label="Trajectory Viewer")
270
+ md_html = gr.HTML(visible=False) # placeholder para consistencia de outputs
271
+ else:
272
+ md_viewer = gr.Textbox(visible=False) # placeholder
273
+ md_html = gr.HTML()
274
+
275
+ md_traj = gr.File(label="Trajectory (.traj)", interactive=False)
276
+ md_log = gr.Code(label="Log", language="text", interactive=False, lines=15, max_lines=25)
277
+ md_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
278
+ md_explain = gr.Markdown()
279
 
280
  run_md_btn.click(
281
+ md_wrapper,
282
+ inputs=[xyz_md, charge_md, spin_md, steps_md, temp_md, timestep_md, ensemble_md],
283
+ outputs=[md_status, md_viewer, md_html, md_traj, md_log, md_script, md_explain],
284
  )
285
 
286
+ # ========== Relaxation ==========
287
+ with gr.Tab("Relaxation / Optimization"):
288
+ with gr.Row():
289
+ with gr.Column(scale=2):
290
+ xyz_rlx = gr.Textbox(label="XYZ Coordinates", lines=12, placeholder="Paste XYZ here...")
291
+ steps_rlx = gr.Slider(value=300, minimum=1, maximum=1000, step=1, label="Max Steps")
292
+ fmax_rlx = gr.Slider(value=0.05, minimum=0.001, maximum=0.5, step=0.001, label="Fmax (eV/Å)")
293
+ with gr.Row():
294
+ charge_rlx = gr.Slider(value=0, minimum=-10, maximum=10, step=1, label="Charge")
295
+ spin_rlx = gr.Slider(value=1, minimum=1, maximum=11, step=1, label="Spin")
296
+ relax_cell = gr.Checkbox(label="Relax Unit Cell", value=False)
297
+ run_rlx_btn = gr.Button("Run Optimization", variant="primary")
298
+
299
+ with gr.Column(variant="panel", min_width=520):
300
+ rlx_status = gr.Textbox(label="Status", interactive=False)
301
+ if HAVE_MOL3D:
302
+ rlx_viewer = Molecule3D(label="Final Structure")
303
+ rlx_html = gr.HTML(visible=False)
304
+ else:
305
+ rlx_viewer = gr.Textbox(visible=False)
306
+ rlx_html = gr.HTML()
307
+ rlx_traj = gr.File(label="Trajectory (.traj)", interactive=False)
308
+ rlx_log = gr.Code(label="Log", language="text", interactive=False, lines=15, max_lines=25)
309
+ rlx_script = gr.Code(label="Reproduction Script", language="python", interactive=False, lines=20, max_lines=30)
310
+ rlx_explain = gr.Markdown()
311
+
312
+ run_rlx_btn.click(
313
+ relax_wrapper,
314
+ inputs=[xyz_rlx, steps_rlx, fmax_rlx, charge_rlx, spin_rlx, relax_cell],
315
+ outputs=[rlx_status, rlx_viewer, rlx_html, rlx_traj, rlx_log, rlx_script, rlx_explain],
316
+ )
317
 
318
+ print("Starting OrbMol model loading")
319
+ _load_orbmol_calc()
320
 
321
  if __name__ == "__main__":
322
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)