Spaces:
Running
on
Zero
Running
on
Zero
I can't draw water molecule but giving up for now
Browse files- app.py +23 -2
- history.md +14 -0
- molecules.json +1 -1
- visualization.py +123 -21
app.py
CHANGED
@@ -52,6 +52,21 @@ import subprocess
|
|
52 |
import sys
|
53 |
import json
|
54 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
56 |
def install_dependencies():
|
57 |
"""Install required packages from requirements.txt."""
|
@@ -304,22 +319,28 @@ def create_interface():
|
|
304 |
def update_molecule_info(molecule_choice):
|
305 |
"""Update description and scale range when molecule is selected."""
|
306 |
try:
|
|
|
307 |
mol_data = molecules[molecule_choice]
|
308 |
|
309 |
# Format parameters display
|
310 |
params_html = format_molecule_params(mol_data)
|
|
|
311 |
|
312 |
# Get scale range values with defaults
|
313 |
min_scale = mol_data.get('scale_range', {}).get('min', 0.1)
|
314 |
max_scale = mol_data.get('scale_range', {}).get('max', 3.0)
|
315 |
default_scale = mol_data.get('default_scale', 1.0)
|
316 |
step_size = mol_data.get('scale_range', {}).get('step', 0.05)
|
|
|
317 |
|
318 |
# Create molecule visualization
|
|
|
319 |
mol_list = create_molecule_viewer(molecule_choice, default_scale)
|
320 |
if mol_list is None or not mol_list:
|
321 |
-
|
322 |
mol_list = []
|
|
|
|
|
323 |
|
324 |
return [
|
325 |
params_html, # Update params display
|
@@ -332,7 +353,7 @@ def create_interface():
|
|
332 |
mol_list # Update 3D view
|
333 |
]
|
334 |
except Exception as e:
|
335 |
-
|
336 |
return [
|
337 |
"<div>Error loading molecule parameters</div>",
|
338 |
gr.update(
|
|
|
52 |
import sys
|
53 |
import json
|
54 |
import os
|
55 |
+
import logging
|
56 |
+
|
57 |
+
# Configure logging
|
58 |
+
logging.basicConfig(level=logging.DEBUG,
|
59 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
60 |
+
handlers=[
|
61 |
+
logging.StreamHandler(), # This will output to console
|
62 |
+
logging.FileHandler('app_molecule.log') # This will output to file
|
63 |
+
])
|
64 |
+
logger = logging.getLogger(__name__)
|
65 |
+
logger.setLevel(logging.DEBUG) # Ensure logger itself is at DEBUG level
|
66 |
+
|
67 |
+
# Force all handlers to DEBUG level
|
68 |
+
for handler in logger.handlers:
|
69 |
+
handler.setLevel(logging.DEBUG)
|
70 |
|
71 |
def install_dependencies():
|
72 |
"""Install required packages from requirements.txt."""
|
|
|
319 |
def update_molecule_info(molecule_choice):
|
320 |
"""Update description and scale range when molecule is selected."""
|
321 |
try:
|
322 |
+
logger.info(f"Updating molecule info for: {molecule_choice}")
|
323 |
mol_data = molecules[molecule_choice]
|
324 |
|
325 |
# Format parameters display
|
326 |
params_html = format_molecule_params(mol_data)
|
327 |
+
logger.debug(f"Generated parameter HTML for {molecule_choice}")
|
328 |
|
329 |
# Get scale range values with defaults
|
330 |
min_scale = mol_data.get('scale_range', {}).get('min', 0.1)
|
331 |
max_scale = mol_data.get('scale_range', {}).get('max', 3.0)
|
332 |
default_scale = mol_data.get('default_scale', 1.0)
|
333 |
step_size = mol_data.get('scale_range', {}).get('step', 0.05)
|
334 |
+
logger.debug(f"Scale parameters - min: {min_scale}, max: {max_scale}, default: {default_scale}")
|
335 |
|
336 |
# Create molecule visualization
|
337 |
+
logger.info(f"Creating molecule visualization for {molecule_choice}")
|
338 |
mol_list = create_molecule_viewer(molecule_choice, default_scale)
|
339 |
if mol_list is None or not mol_list:
|
340 |
+
logger.error(f"No molecule visualization created for {molecule_choice}")
|
341 |
mol_list = []
|
342 |
+
else:
|
343 |
+
logger.info(f"Successfully created visualization with {len(mol_list)} molecules")
|
344 |
|
345 |
return [
|
346 |
params_html, # Update params display
|
|
|
353 |
mol_list # Update 3D view
|
354 |
]
|
355 |
except Exception as e:
|
356 |
+
logger.error(f"Error updating molecule info: {str(e)}", exc_info=True)
|
357 |
return [
|
358 |
"<div>Error loading molecule parameters</div>",
|
359 |
gr.update(
|
history.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1 |
# Development History
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
## 2024-03-20
|
4 |
- Split VQE simulation into two steps:
|
5 |
* Added "Generate Hamiltonian" button to first compute and display Hamiltonian parameters
|
|
|
1 |
# Development History
|
2 |
|
3 |
+
## 2024-01-06 (Update 10)
|
4 |
+
- Extensive debugging of molecule visualization issues:
|
5 |
+
* Added comprehensive logging to track molecule creation and visualization
|
6 |
+
* Modified RDKit molecule creation to handle hydrogens explicitly
|
7 |
+
* Attempted multiple approaches for H2O and H2 creation:
|
8 |
+
- Direct SMILES parsing
|
9 |
+
- RWMol with explicit atom and bond creation
|
10 |
+
- Geometry template-based coordinate generation
|
11 |
+
* Added proper valence calculation and sanitization
|
12 |
+
* Added explicit bond creation and verification
|
13 |
+
* Issue remains unresolved: H-containing molecules (H2, H2O, etc.) fail to display
|
14 |
+
* Non-H molecules (CO, N2) display correctly
|
15 |
+
* Next steps: Consider alternative visualization approaches or different molecule viewer component
|
16 |
+
|
17 |
## 2024-03-20
|
18 |
- Split VQE simulation into two steps:
|
19 |
* Added "Generate Hamiltonian" button to first compute and display Hamiltonian parameters
|
molecules.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
"H2": {
|
3 |
"name": "Hydrogen molecule",
|
4 |
"formula": "H₂",
|
5 |
-
"smiles": "[
|
6 |
"basis": "sto-3g",
|
7 |
"multiplicity": 1,
|
8 |
"charge": 0,
|
|
|
2 |
"H2": {
|
3 |
"name": "Hydrogen molecule",
|
4 |
"formula": "H₂",
|
5 |
+
"smiles": "[HH]",
|
6 |
"basis": "sto-3g",
|
7 |
"multiplicity": 1,
|
8 |
"charge": 0,
|
visualization.py
CHANGED
@@ -12,8 +12,18 @@ from gradio_molgallery3d import MolGallery3D
|
|
12 |
import json
|
13 |
|
14 |
# Configure logging
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
logger = logging.getLogger(__name__)
|
16 |
-
logger.setLevel(logging.
|
|
|
|
|
|
|
|
|
17 |
|
18 |
def format_molecule_params(molecule_data: dict) -> str:
|
19 |
"""
|
@@ -153,48 +163,140 @@ def create_molecule_viewer(molecule_id: str, scale_factor: float) -> MolGallery3
|
|
153 |
return None
|
154 |
|
155 |
molecule_data = molecules[molecule_id]
|
|
|
156 |
|
157 |
# Special handling for different molecules
|
|
|
158 |
if molecule_id == "H2":
|
159 |
-
|
160 |
-
|
161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
elif molecule_id == "H2O":
|
163 |
-
|
164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
else:
|
166 |
# Create molecule using SMILES
|
|
|
167 |
mol = Chem.MolFromSmiles(molecule_data['smiles'])
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
172 |
|
173 |
if mol is None:
|
174 |
logger.error(f"Failed to create molecule for {molecule_id}")
|
175 |
return None
|
176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
# Generate 3D coordinates with distance matrix
|
178 |
try:
|
179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
centroid = np.mean(positions, axis=0)
|
185 |
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
conf.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
|
191 |
logger.info(f"Successfully created 3D coordinates for {molecule_id}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
return [mol] # Return the molecule as a list as expected by MolGallery3D
|
193 |
|
194 |
except Exception as e:
|
195 |
-
logger.error(f"Failed to generate 3D coordinates: {e}")
|
196 |
return None
|
197 |
|
198 |
except Exception as e:
|
199 |
-
logger.error(f"Failed to create molecule viewer: {e}")
|
200 |
return None
|
|
|
12 |
import json
|
13 |
|
14 |
# Configure logging
|
15 |
+
logging.basicConfig(level=logging.DEBUG,
|
16 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
17 |
+
handlers=[
|
18 |
+
logging.StreamHandler(), # This will output to console
|
19 |
+
logging.FileHandler('molecule_creation.log') # This will output to file
|
20 |
+
])
|
21 |
logger = logging.getLogger(__name__)
|
22 |
+
logger.setLevel(logging.DEBUG) # Ensure logger itself is at DEBUG level
|
23 |
+
|
24 |
+
# Force all handlers to DEBUG level
|
25 |
+
for handler in logger.handlers:
|
26 |
+
handler.setLevel(logging.DEBUG)
|
27 |
|
28 |
def format_molecule_params(molecule_data: dict) -> str:
|
29 |
"""
|
|
|
163 |
return None
|
164 |
|
165 |
molecule_data = molecules[molecule_id]
|
166 |
+
logger.debug(f"Molecule data loaded: {molecule_data}")
|
167 |
|
168 |
# Special handling for different molecules
|
169 |
+
mol = None
|
170 |
if molecule_id == "H2":
|
171 |
+
logger.debug("Creating H2 molecule")
|
172 |
+
# Create H2 using RWMol for explicit control
|
173 |
+
mol = Chem.RWMol()
|
174 |
+
h1 = mol.AddAtom(Chem.Atom(1)) # Add first H atom
|
175 |
+
h2 = mol.AddAtom(Chem.Atom(1)) # Add second H atom
|
176 |
+
mol.AddBond(h1, h2, Chem.BondType.SINGLE) # Add single bond
|
177 |
+
mol = mol.GetMol()
|
178 |
+
Chem.SanitizeMol(mol)
|
179 |
+
logger.debug(f"H2 creation result: {mol is not None}")
|
180 |
+
|
181 |
elif molecule_id == "H2O":
|
182 |
+
logger.debug("Creating H2O molecule")
|
183 |
+
# Create H2O using RWMol for explicit control
|
184 |
+
mol = Chem.RWMol()
|
185 |
+
|
186 |
+
# Add atoms with explicit valence
|
187 |
+
o = mol.AddAtom(Chem.Atom(8)) # Oxygen
|
188 |
+
oxygen = mol.GetAtomWithIdx(o)
|
189 |
+
oxygen.SetFormalCharge(0)
|
190 |
+
oxygen.SetNoImplicit(True)
|
191 |
+
|
192 |
+
h1 = mol.AddAtom(Chem.Atom(1)) # First hydrogen
|
193 |
+
h1_atom = mol.GetAtomWithIdx(h1)
|
194 |
+
h1_atom.SetFormalCharge(0)
|
195 |
+
h1_atom.SetNoImplicit(True)
|
196 |
+
|
197 |
+
h2 = mol.AddAtom(Chem.Atom(1)) # Second hydrogen
|
198 |
+
h2_atom = mol.GetAtomWithIdx(h2)
|
199 |
+
h2_atom.SetFormalCharge(0)
|
200 |
+
h2_atom.SetNoImplicit(True)
|
201 |
+
|
202 |
+
# Add bonds
|
203 |
+
mol.AddBond(o, h1, Chem.BondType.SINGLE)
|
204 |
+
mol.AddBond(o, h2, Chem.BondType.SINGLE)
|
205 |
+
|
206 |
+
# Convert to immutable molecule and sanitize
|
207 |
+
mol = mol.GetMol()
|
208 |
+
Chem.SanitizeMol(mol)
|
209 |
+
logger.debug(f"H2O creation result: {mol is not None}")
|
210 |
+
|
211 |
else:
|
212 |
# Create molecule using SMILES
|
213 |
+
logger.debug(f"Creating molecule from SMILES: {molecule_data['smiles']}")
|
214 |
mol = Chem.MolFromSmiles(molecule_data['smiles'])
|
215 |
+
logger.debug(f"Initial molecule creation result: {mol is not None}")
|
216 |
+
if mol is not None:
|
217 |
+
logger.debug("Adding hydrogens to molecule")
|
218 |
+
mol = Chem.AddHs(mol)
|
219 |
+
Chem.SanitizeMol(mol)
|
220 |
+
logger.debug(f"Molecule with hydrogens result: {mol is not None}")
|
221 |
|
222 |
if mol is None:
|
223 |
logger.error(f"Failed to create molecule for {molecule_id}")
|
224 |
return None
|
225 |
|
226 |
+
logger.debug(f"Molecule created with {mol.GetNumAtoms()} atoms")
|
227 |
+
for atom in mol.GetAtoms():
|
228 |
+
# Calculate valence before checking hydrogens
|
229 |
+
atom.UpdatePropertyCache()
|
230 |
+
logger.debug(f"Atom {atom.GetIdx()}: Symbol={atom.GetSymbol()}, " +
|
231 |
+
f"Explicit Hs={atom.GetNumExplicitHs()}, " +
|
232 |
+
f"Implicit Hs={atom.GetNumImplicitHs()}, " +
|
233 |
+
f"Valence={atom.GetTotalValence()}")
|
234 |
+
|
235 |
+
# Log bond information
|
236 |
+
logger.debug(f"Number of bonds: {mol.GetNumBonds()}")
|
237 |
+
for bond in mol.GetBonds():
|
238 |
+
logger.debug(f"Bond between {bond.GetBeginAtom().GetSymbol()}{bond.GetBeginAtomIdx()} and " +
|
239 |
+
f"{bond.GetEndAtom().GetSymbol()}{bond.GetEndAtomIdx()}, type: {bond.GetBondType()}")
|
240 |
+
|
241 |
# Generate 3D coordinates with distance matrix
|
242 |
try:
|
243 |
+
logger.debug("Attempting to generate 3D coordinates")
|
244 |
+
# Use geometry template if available
|
245 |
+
if 'geometry_template' in molecule_data:
|
246 |
+
logger.debug("Using geometry template for 3D coordinates")
|
247 |
+
conf = Chem.Conformer(mol.GetNumAtoms())
|
248 |
+
for i, (_, coords) in enumerate(molecule_data['geometry_template']):
|
249 |
+
# Scale coordinates by the scale factor
|
250 |
+
scaled_coords = np.array(coords) * scale_factor
|
251 |
+
conf.SetAtomPosition(i, scaled_coords)
|
252 |
+
mol.AddConformer(conf)
|
253 |
+
result = 0 # Template-based embedding always succeeds
|
254 |
+
else:
|
255 |
+
result = AllChem.EmbedMolecule(mol, randomSeed=42)
|
256 |
+
|
257 |
+
logger.debug(f"3D coordinate generation result: {result}") # 0 means success, -1 means failure
|
258 |
+
|
259 |
+
if result == -1:
|
260 |
+
logger.debug("Standard embedding failed, trying with ETKDGv2")
|
261 |
+
params = AllChem.ETKDGv2()
|
262 |
+
result = AllChem.EmbedMolecule(mol, params, randomSeed=42)
|
263 |
+
logger.debug(f"ETKDGv2 embedding result: {result}")
|
264 |
|
265 |
+
if result == -1:
|
266 |
+
logger.error("Failed to generate 3D coordinates")
|
267 |
+
return None
|
|
|
268 |
|
269 |
+
# Scale the molecule coordinates if not using template
|
270 |
+
if 'geometry_template' not in molecule_data:
|
271 |
+
conf = mol.GetConformer()
|
272 |
+
positions = conf.GetPositions()
|
273 |
+
logger.debug(f"Generated 3D coordinates:\n{positions}")
|
274 |
+
centroid = np.mean(positions, axis=0)
|
275 |
+
logger.debug(f"Molecule centroid: {centroid}")
|
276 |
+
|
277 |
+
for i in range(mol.GetNumAtoms()):
|
278 |
+
pos = positions[i]
|
279 |
+
scaled_pos = centroid + (pos - centroid) * scale_factor
|
280 |
+
conf.SetAtomPosition(i, scaled_pos)
|
281 |
+
logger.debug(f"Atom {i} ({mol.GetAtomWithIdx(i).GetSymbol()}) scaled position: {scaled_pos}")
|
282 |
|
283 |
logger.info(f"Successfully created 3D coordinates for {molecule_id}")
|
284 |
+
|
285 |
+
# Verify final state
|
286 |
+
logger.debug("Final molecule state:")
|
287 |
+
logger.debug(f"Number of atoms: {mol.GetNumAtoms()}")
|
288 |
+
logger.debug(f"Number of bonds: {mol.GetNumBonds()}")
|
289 |
+
logger.debug(f"Has conformer: {mol.GetNumConformers() > 0}")
|
290 |
+
|
291 |
+
# Final sanitization check
|
292 |
+
Chem.SanitizeMol(mol)
|
293 |
+
|
294 |
return [mol] # Return the molecule as a list as expected by MolGallery3D
|
295 |
|
296 |
except Exception as e:
|
297 |
+
logger.error(f"Failed to generate 3D coordinates: {e}", exc_info=True)
|
298 |
return None
|
299 |
|
300 |
except Exception as e:
|
301 |
+
logger.error(f"Failed to create molecule viewer: {e}", exc_info=True)
|
302 |
return None
|