Nanny7 commited on
Commit
526a32d
·
1 Parent(s): e7217e1

Make orbital tab optional when Psikit unavailable

Browse files
Files changed (2) hide show
  1. app.py +47 -33
  2. requirements.txt +1 -4
app.py CHANGED
@@ -87,25 +87,32 @@ def smiles_to_name(smiles: str) -> str:
87
 
88
 
89
  def smiles_to_molecular_orbitals(smiles: str) -> str:
90
- """Generate HOMO/LUMO isosurfaces using Psikit and render with py3Dmol."""
91
  mol = _mol_from_smiles(smiles)
92
  if mol.GetNumAtoms() > 30:
93
  raise gr.Error("Please provide a molecule with 30 atoms or fewer for orbital visualization.")
94
 
95
  try:
96
- from psikit import Psikit
97
- except ImportError as exc: # pragma: no cover - runtime dependency
98
- raise gr.Error("Psikit is required for molecular orbital visualization.") from exc
 
 
 
 
99
 
100
  try:
101
- import py3Dmol
102
- except ImportError as exc: # pragma: no cover - runtime dependency
103
- raise gr.Error("py3Dmol is required for molecular orbital visualization.") from exc
 
 
 
104
 
105
  with tempfile.TemporaryDirectory() as tmpdir:
106
  original_cwd = os.getcwd()
 
107
  try:
108
- os.chdir(tmpdir)
109
  pk = Psikit()
110
  pk.read_from_smiles(smiles)
111
  pk.optimize()
@@ -113,36 +120,43 @@ def smiles_to_molecular_orbitals(smiles: str) -> str:
113
 
114
  cube_candidates = sorted(Path(tmpdir).glob("Psi_a_*A.cube"))
115
  if not cube_candidates:
116
- raise gr.Error("Failed to generate molecular orbital cube files. Try a smaller molecule.")
 
 
117
 
118
  mol_block = Chem.MolToMolBlock(pk.mol)
119
- except Exception as exc:
120
- raise gr.Error(f"Unable to compute molecular orbitals: {exc}") from exc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  finally:
122
  os.chdir(original_cwd)
123
 
124
- def _render_cube(cube_path: Path, title: str) -> str:
125
- cube_data = cube_path.read_text()
126
- view = py3Dmol.view(width=500, height=400)
127
- view.addModel(mol_block, "mol")
128
- view.setStyle({"stick": {}})
129
- isoval = 0.02
130
- view.addVolumetricData(cube_data, "cube", {"isoval": isoval, "color": "blue", "opacity": 0.6})
131
- view.addVolumetricData(cube_data, "cube", {"isoval": -isoval, "color": "red", "opacity": 0.6})
132
- view.zoomTo()
133
- return f"<h3>{title}</h3>" + view._make_html()
134
-
135
- # First cube is HOMO, second is LUMO following psikit naming convention.
136
- html_sections = []
137
- labels = ["HOMO", "LUMO"]
138
- for cube_file, label in zip(cube_candidates[:2], labels):
139
- html_sections.append(_render_cube(cube_file, label))
140
-
141
- if not html_sections:
142
- raise gr.Error("Could not prepare HOMO/LUMO visualizations.")
143
-
144
- return "".join(html_sections)
145
-
146
 
147
  def name_to_3d_molecule(name: str) -> str:
148
  """Convert chemical name to 3D molecule visualization"""
 
87
 
88
 
89
  def smiles_to_molecular_orbitals(smiles: str) -> str:
90
+ """Generate HOMO/LUMO isosurfaces using Psikit, when available."""
91
  mol = _mol_from_smiles(smiles)
92
  if mol.GetNumAtoms() > 30:
93
  raise gr.Error("Please provide a molecule with 30 atoms or fewer for orbital visualization.")
94
 
95
  try:
96
+ from psikit import Psikit # type: ignore[import]
97
+ except ImportError:
98
+ return (
99
+ "<p><strong>Molecular orbital rendering requires Psikit + Psi4.</strong> "
100
+ "Install them locally with <code>pip install psikit psi4 py3Dmol</code> "
101
+ "and run this app on your machine.</p>"
102
+ )
103
 
104
  try:
105
+ import py3Dmol # type: ignore[import]
106
+ except ImportError:
107
+ return (
108
+ "<p><strong>py3Dmol is missing.</strong> Install it with <code>pip install py3Dmol</code> "
109
+ "to view orbital isosurfaces.</p>"
110
+ )
111
 
112
  with tempfile.TemporaryDirectory() as tmpdir:
113
  original_cwd = os.getcwd()
114
+ os.chdir(tmpdir)
115
  try:
 
116
  pk = Psikit()
117
  pk.read_from_smiles(smiles)
118
  pk.optimize()
 
120
 
121
  cube_candidates = sorted(Path(tmpdir).glob("Psi_a_*A.cube"))
122
  if not cube_candidates:
123
+ return (
124
+ "<p>Psikit did not produce cube files. Try a smaller molecule or different conformer.</p>"
125
+ )
126
 
127
  mol_block = Chem.MolToMolBlock(pk.mol)
128
+
129
+ html_sections: list[str] = []
130
+ labels = ["HOMO", "LUMO"]
131
+ for cube_file, label in zip(cube_candidates[:2], labels):
132
+ cube_data = cube_file.read_text()
133
+ view = py3Dmol.view(width=500, height=400)
134
+ view.addModel(mol_block, "mol")
135
+ view.setStyle({"stick": {}})
136
+ isoval = 0.02
137
+ # Use the same volumetric data for positive/negative isosurfaces.
138
+ view.addVolumetricData(
139
+ cube_data,
140
+ "cube",
141
+ {"isoval": isoval, "color": "blue", "opacity": 0.6},
142
+ )
143
+ view.addVolumetricData(
144
+ cube_data,
145
+ "cube",
146
+ {"isoval": -isoval, "color": "red", "opacity": 0.6},
147
+ )
148
+ view.zoomTo()
149
+ html_sections.append(f"<h3>{label}</h3>" + view._make_html())
150
+
151
+ if not html_sections:
152
+ return "<p>Could not prepare HOMO/LUMO visualizations.</p>"
153
+
154
+ return "".join(html_sections)
155
+ except Exception as exc: # pragma: no cover - runtime heavy
156
+ return f"<p>Unable to compute molecular orbitals: {exc}</p>"
157
  finally:
158
  os.chdir(original_cwd)
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  def name_to_3d_molecule(name: str) -> str:
162
  """Convert chemical name to 3D molecule visualization"""
requirements.txt CHANGED
@@ -3,7 +3,4 @@ rdkit
3
  gradio==4.44.1
4
  huggingface_hub==0.19.4
5
  cirpy
6
- pubchempy
7
- psikit
8
- psi4
9
- py3Dmol
 
3
  gradio==4.44.1
4
  huggingface_hub==0.19.4
5
  cirpy
6
+ pubchempy