A19grey commited on
Commit
7f2024d
·
1 Parent(s): 6196475

removed RDkit and molgallery for display no going back and just using plotly by hand

Browse files
Files changed (4) hide show
  1. app.py +20 -30
  2. molecules.json +100 -3
  3. requirements.txt +2 -4
  4. visualization.py +132 -157
app.py CHANGED
@@ -57,6 +57,11 @@ import json
57
  import os
58
  import logging
59
  from logging.handlers import RotatingFileHandler
 
 
 
 
 
60
 
61
  # Configure logging
62
  os.makedirs('logs', exist_ok=True)
@@ -134,13 +139,10 @@ def load_molecules():
134
  return {}
135
 
136
  # Now import the rest of the modules
137
- import gradio as gr
138
  import numpy as np
139
  from quantum_utils import run_vqe_simulation
140
  from visualization import plot_convergence, create_molecule_viewer, format_molecule_params
141
- from gradio_molgallery3d import MolGallery3D
142
-
143
- #Todo later add this working client stuff to fix timeouts client = Client("your-space", headers={"X-IP-Token": request.headers['x-ip-token']})
144
 
145
  # Add immediate logging
146
  print("Imported all modules successfully", file=sys.stderr, flush=True)
@@ -207,15 +209,12 @@ def create_interface():
207
 
208
  def set_client_for_session(request: gr.Request):
209
  """Initialize client with user's IP token to handle rate limiting."""
210
- # from https://www.gradio.app/docs/python-client/using-zero-gpu-spaces
211
  try:
212
- x_ip_token = request.headers['x-ip-token'] #
213
  return Client("A19grey/Cuda-Quantum-Molecular-Playground", headers={"x-ip-token": x_ip_token})
214
  except Exception as e:
215
  logger.error(f"Error setting client: {e}")
216
  return None
217
-
218
-
219
 
220
  # Load available molecules
221
  molecules = load_molecules()
@@ -246,23 +245,20 @@ def create_interface():
246
  *Inspired by the [NVIDIA CUDA-Q VQE Example](https://nvidia.github.io/cuda-quantum/latest/applications/python/vqe_advanced.html)*
247
  """)
248
 
249
- client = gr.State() # added this to fix timeouts - https://www.gradio.app/docs/python-client/using-zero-gpu-spaces
250
-
251
- state = gr.State({}) # Store current molecule data
252
 
253
  # Top section: 3 columns
254
  with gr.Row():
255
  with gr.Column(scale=1):
256
- # Molecule selection controls
257
  molecule_choice = gr.Dropdown(
258
  choices=list(molecules.keys()),
259
- value=None, # Start with no selection
260
  label="Select Molecule",
261
  interactive=True,
262
  allow_custom_value=False
263
  )
264
 
265
- # Get scale range from selected molecule
266
  default_molecule = molecules[list(molecules.keys())[0]]
267
  scale_factor = gr.Slider(
268
  minimum=default_molecule['scale_range']['min'],
@@ -274,19 +270,15 @@ def create_interface():
274
  )
275
 
276
  with gr.Column(scale=1):
277
- # Molecule parameters display
278
  params_display = gr.HTML(
279
  label="Molecule Parameters",
280
  value="<div>Select a molecule to view its parameters</div>"
281
  )
282
 
283
  with gr.Column(scale=1):
284
- # 3D Molecule Viewer (starts empty)
285
- molecule_viz = MolGallery3D(
286
- columns=1,
287
- height=400, # Reduced height to match other elements
288
  label="3D Molecule Viewer",
289
- automatic_rotation=True
290
  )
291
 
292
  # Middle section: Buttons and results in 2 columns
@@ -463,24 +455,22 @@ def create_interface():
463
  step_size = mol_data.get('scale_range', {}).get('step', 0.05)
464
  logger.debug(f"Scale parameters - min: {min_scale}, max: {max_scale}, default: {default_scale}")
465
 
466
- # Create molecule visualization
467
  logger.info(f"Creating molecule visualization for {molecule_choice}")
468
- mol_list = create_molecule_viewer(molecule_choice, default_scale)
469
- if mol_list is None or not mol_list:
470
  logger.error(f"No molecule visualization created for {molecule_choice}")
471
- mol_list = []
472
- else:
473
- logger.info(f"Successfully created visualization with {len(mol_list)} molecules")
474
 
475
  return [
476
- params_html, # Update params display
477
- gr.update( # Update slider
478
  minimum=min_scale,
479
  maximum=max_scale,
480
  value=default_scale,
481
  step=step_size
482
  ),
483
- mol_list # Update 3D view
484
  ]
485
  except Exception as e:
486
  logger.error(f"Error updating molecule info: {str(e)}", exc_info=True)
@@ -492,7 +482,7 @@ def create_interface():
492
  value=1.0,
493
  step=0.05
494
  ),
495
- [] # Empty list for molecule visualization
496
  ]
497
 
498
  # Update parameters and 3D view when molecule changes
 
57
  import os
58
  import logging
59
  from logging.handlers import RotatingFileHandler
60
+ import gradio as gr
61
+ import numpy as np
62
+ from quantum_utils import run_vqe_simulation
63
+ from visualization import plot_convergence, create_molecule_viewer, format_molecule_params
64
+ import plotly.graph_objects as go
65
 
66
  # Configure logging
67
  os.makedirs('logs', exist_ok=True)
 
139
  return {}
140
 
141
  # Now import the rest of the modules
142
+ print("about to import numpy and quantum_utils", file=sys.stderr, flush=True)
143
  import numpy as np
144
  from quantum_utils import run_vqe_simulation
145
  from visualization import plot_convergence, create_molecule_viewer, format_molecule_params
 
 
 
146
 
147
  # Add immediate logging
148
  print("Imported all modules successfully", file=sys.stderr, flush=True)
 
209
 
210
  def set_client_for_session(request: gr.Request):
211
  """Initialize client with user's IP token to handle rate limiting."""
 
212
  try:
213
+ x_ip_token = request.headers['x-ip-token']
214
  return Client("A19grey/Cuda-Quantum-Molecular-Playground", headers={"x-ip-token": x_ip_token})
215
  except Exception as e:
216
  logger.error(f"Error setting client: {e}")
217
  return None
 
 
218
 
219
  # Load available molecules
220
  molecules = load_molecules()
 
245
  *Inspired by the [NVIDIA CUDA-Q VQE Example](https://nvidia.github.io/cuda-quantum/latest/applications/python/vqe_advanced.html)*
246
  """)
247
 
248
+ client = gr.State()
249
+ state = gr.State({})
 
250
 
251
  # Top section: 3 columns
252
  with gr.Row():
253
  with gr.Column(scale=1):
 
254
  molecule_choice = gr.Dropdown(
255
  choices=list(molecules.keys()),
256
+ value=None,
257
  label="Select Molecule",
258
  interactive=True,
259
  allow_custom_value=False
260
  )
261
 
 
262
  default_molecule = molecules[list(molecules.keys())[0]]
263
  scale_factor = gr.Slider(
264
  minimum=default_molecule['scale_range']['min'],
 
270
  )
271
 
272
  with gr.Column(scale=1):
 
273
  params_display = gr.HTML(
274
  label="Molecule Parameters",
275
  value="<div>Select a molecule to view its parameters</div>"
276
  )
277
 
278
  with gr.Column(scale=1):
279
+ molecule_viz = gr.Plot(
 
 
 
280
  label="3D Molecule Viewer",
281
+ show_label=True
282
  )
283
 
284
  # Middle section: Buttons and results in 2 columns
 
455
  step_size = mol_data.get('scale_range', {}).get('step', 0.05)
456
  logger.debug(f"Scale parameters - min: {min_scale}, max: {max_scale}, default: {default_scale}")
457
 
458
+ # Create molecule visualization using Plotly
459
  logger.info(f"Creating molecule visualization for {molecule_choice}")
460
+ mol_plot = create_molecule_viewer(molecule_choice, default_scale)
461
+ if mol_plot is None:
462
  logger.error(f"No molecule visualization created for {molecule_choice}")
463
+ mol_plot = go.Figure() # Empty figure as fallback
 
 
464
 
465
  return [
466
+ params_html,
467
+ gr.update(
468
  minimum=min_scale,
469
  maximum=max_scale,
470
  value=default_scale,
471
  step=step_size
472
  ),
473
+ mol_plot
474
  ]
475
  except Exception as e:
476
  logger.error(f"Error updating molecule info: {str(e)}", exc_info=True)
 
482
  value=1.0,
483
  step=0.05
484
  ),
485
+ go.Figure() # Empty figure as fallback
486
  ]
487
 
488
  # Update parameters and 3D view when molecule changes
molecules.json CHANGED
@@ -48,7 +48,104 @@
48
  ],
49
  "description": "Triatomic water molecule with bent geometry. Scale factor affects the overall size of the molecule."
50
  },
51
- "CH4": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  "name": "Methane",
53
  "enabled": 0,
54
  "formula": "CH₄",
@@ -77,7 +174,7 @@
77
  },
78
  "CO": {
79
  "name": "Carbon monoxide",
80
- "enabled": 1,
81
  "formula": "CO",
82
  "smiles": "[C-]#[O+]",
83
  "basis": "sto-3g",
@@ -101,7 +198,7 @@
101
  },
102
  "N2": {
103
  "name": "Nitrogen molecule",
104
- "enabled": 1,
105
  "formula": "N₂",
106
  "smiles": "N#N",
107
  "basis": "sto-3g",
 
48
  ],
49
  "description": "Triatomic water molecule with bent geometry. Scale factor affects the overall size of the molecule."
50
  },
51
+ "BeH2": {
52
+ "name": "Beryllium Hydride",
53
+ "enabled": 1,
54
+ "formula": "BeH₂",
55
+ "smiles": "[BeH2]",
56
+ "basis": "sto-3g",
57
+ "multiplicity": 1,
58
+ "charge": 0,
59
+ "spatial_orbitals": 7,
60
+ "electron_count": 4,
61
+ "default_scale": 1.0,
62
+ "GPU_time": 90,
63
+ "iterations": 20,
64
+ "scale_range": {
65
+ "min": 0.5,
66
+ "max": 2.0,
67
+ "step": 0.05
68
+ },
69
+ "geometry_template": [
70
+ ["Be", [0.0, 0.0, 0.0]],
71
+ ["H", [1.33, 0.0, 0.0]],
72
+ ["H", [-1.33, 0.0, 0.0]]
73
+ ],
74
+ "description": "Linear beryllium hydride molecule with two hydrogen atoms symmetrically bonded. Scale factor affects the bond length."
75
+ },
76
+ "NH": {
77
+ "name": "Nitrogen Monohydride",
78
+ "enabled": 1,
79
+ "formula": "NH",
80
+ "smiles": "[NH]",
81
+ "basis": "sto-3g",
82
+ "multiplicity": 2,
83
+ "charge": 0,
84
+ "spatial_orbitals": 6,
85
+ "electron_count": 8,
86
+ "default_scale": 1.0,
87
+ "GPU_time": 90,
88
+ "iterations": 20,
89
+ "scale_range": {
90
+ "min": 0.5,
91
+ "max": 2.5,
92
+ "step": 0.05
93
+ },
94
+ "geometry_template": [
95
+ ["N", [0.0, 0.0, 0.0]],
96
+ ["H", [1.03, 0.0, 0.0]]
97
+ ],
98
+ "description": "Diatomic nitrogen monohydride molecule, important in radical chemistry. Scale factor affects bond length."
99
+ },
100
+ "HF": {
101
+ "name": "Hydrogen Fluoride",
102
+ "enabled": 1,
103
+ "formula": "HF",
104
+ "smiles": "[FH]",
105
+ "basis": "sto-3g",
106
+ "multiplicity": 1,
107
+ "charge": 0,
108
+ "spatial_orbitals": 5,
109
+ "electron_count": 10,
110
+ "default_scale": 1.0,
111
+ "GPU_time": 90,
112
+ "iterations": 20,
113
+ "scale_range": {
114
+ "min": 0.5,
115
+ "max": 2.0,
116
+ "step": 0.05
117
+ },
118
+ "geometry_template": [
119
+ ["H", [0.0, 0.0, 0.0]],
120
+ ["F", [0.92, 0.0, 0.0]]
121
+ ],
122
+ "description": "Diatomic hydrogen fluoride molecule, a simple polar molecule with strong dipole interactions. Scale factor affects bond length."
123
+ },
124
+ "LiH": {
125
+ "name": "Lithium Hydride",
126
+ "enabled": 1,
127
+ "formula": "LiH",
128
+ "smiles": "[LiH]",
129
+ "basis": "sto-3g",
130
+ "multiplicity": 1,
131
+ "charge": 0,
132
+ "spatial_orbitals": 4,
133
+ "electron_count": 4,
134
+ "default_scale": 1.0,
135
+ "GPU_time": 90,
136
+ "iterations": 20,
137
+ "scale_range": {
138
+ "min": 0.5,
139
+ "max": 2.5,
140
+ "step": 0.05
141
+ },
142
+ "geometry_template": [
143
+ ["Li", [0.0, 0.0, 0.0]],
144
+ ["H", [1.6, 0.0, 0.0]]
145
+ ],
146
+ "description": "Diatomic lithium hydride molecule with ionic bonding properties. Scale factor affects bond length."
147
+ }
148
+ ,"CH4": {
149
  "name": "Methane",
150
  "enabled": 0,
151
  "formula": "CH₄",
 
174
  },
175
  "CO": {
176
  "name": "Carbon monoxide",
177
+ "enabled": 0,
178
  "formula": "CO",
179
  "smiles": "[C-]#[O+]",
180
  "basis": "sto-3g",
 
198
  },
199
  "N2": {
200
  "name": "Nitrogen molecule",
201
+ "enabled": 0,
202
  "formula": "N₂",
203
  "smiles": "N#N",
204
  "basis": "sto-3g",
requirements.txt CHANGED
@@ -6,15 +6,13 @@ numpy==1.24.3
6
  scipy==1.10.1
7
 
8
  # Gradio and visualization components
9
- # gradio==4.44.1
10
- # gradio_client
11
- gradio_molgallery3d>=0.0.4
12
 
13
  # Plotting and visualization libraries
14
  plotly==5.18.0
15
  bokeh==3.2.2
16
  matplotlib==3.7.1
17
- rdkit==2023.09.1
18
 
19
  # ASE for molecular geometry handling
20
  ase==3.22.1
 
6
  scipy==1.10.1
7
 
8
  # Gradio and visualization components
9
+ gradio==4.44.1
10
+ gradio_client
 
11
 
12
  # Plotting and visualization libraries
13
  plotly==5.18.0
14
  bokeh==3.2.2
15
  matplotlib==3.7.1
 
16
 
17
  # ASE for molecular geometry handling
18
  ase==3.22.1
visualization.py CHANGED
@@ -1,29 +1,70 @@
1
  """
2
  visualization.py
3
- Routines for plotting VQE results and molecular visualization using Plotly and MolGallery3D
4
  """
5
 
6
  import plotly.graph_objects as go
7
  import numpy as np
8
  import logging
9
- from rdkit import Chem
10
- from rdkit.Chem import AllChem
11
- from gradio_molgallery3d import MolGallery3D
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
  """
@@ -82,7 +123,6 @@ def plot_convergence(results):
82
  # Handle both dictionary entries and direct energy values
83
  for i, entry in enumerate(history):
84
  if isinstance(entry, dict):
85
- # Handle dictionary format
86
  try:
87
  iterations.append(entry.get('iteration', i))
88
  energies.append(entry['energy'])
@@ -90,7 +130,6 @@ def plot_convergence(results):
90
  logger.warning(f"Skipping invalid entry {i}: {str(e)}")
91
  continue
92
  else:
93
- # Handle direct energy values
94
  try:
95
  iterations.append(i)
96
  energies.append(float(entry))
@@ -146,16 +185,16 @@ def plot_convergence(results):
146
 
147
  return fig
148
 
149
- def create_molecule_viewer(molecule_id: str, scale_factor: float) -> MolGallery3D:
150
  """
151
- Create a 3D visualization of the molecule using MolGallery3D and RDKit.
152
- For better visualization, hydrogen atoms are replaced with nitrogen atoms in the display only.
153
- The actual quantum calculations still use the real molecule structure.
154
- Atoms are displayed as spheres with explicit bonds between them.
155
 
156
  Args:
157
  molecule_id: Molecule identifier (e.g., "H2")
158
  scale_factor: Bond length scaling factor (1.0 = original size)
 
 
159
  """
160
  logger.info(f"Creating 3D view for {molecule_id} with scale_factor={scale_factor}")
161
 
@@ -169,147 +208,83 @@ def create_molecule_viewer(molecule_id: str, scale_factor: float) -> MolGallery3
169
  return None
170
 
171
  molecule_data = molecules[molecule_id]
172
- logger.debug(f"Molecule data loaded: {molecule_data}")
173
-
174
- # Special handling for different molecules
175
- mol = None
176
- if molecule_id == "H2":
177
- logger.debug("Creating H2 molecule (visualized as N2)")
178
- # Create N2 for visualization instead of H2
179
- mol = Chem.RWMol()
180
- n1 = mol.AddAtom(Chem.Atom(7)) # Add first N atom
181
- n2 = mol.AddAtom(Chem.Atom(7)) # Add second N atom
182
- mol.AddBond(n1, n2, Chem.BondType.SINGLE) # Add single bond
183
- mol = mol.GetMol()
184
- Chem.SanitizeMol(mol)
185
- logger.debug(f"N2 (for H2 viz) creation result: {mol is not None}")
186
-
187
- elif molecule_id == "H2O":
188
- logger.debug("Creating H2O molecule (visualized with N instead of H)")
189
- # Create H2O using RWMol but with N instead of H
190
- mol = Chem.RWMol()
191
-
192
- # Add atoms with explicit valence
193
- o = mol.AddAtom(Chem.Atom(8)) # Oxygen
194
- oxygen = mol.GetAtomWithIdx(o)
195
- oxygen.SetFormalCharge(0)
196
- oxygen.SetNoImplicit(True)
197
-
198
- n1 = mol.AddAtom(Chem.Atom(7)) # First nitrogen (replacing H)
199
- n1_atom = mol.GetAtomWithIdx(n1)
200
- n1_atom.SetFormalCharge(0)
201
- n1_atom.SetNoImplicit(True)
202
-
203
- n2 = mol.AddAtom(Chem.Atom(7)) # Second nitrogen (replacing H)
204
- n2_atom = mol.GetAtomWithIdx(n2)
205
- n2_atom.SetFormalCharge(0)
206
- n2_atom.SetNoImplicit(True)
207
-
208
- # Add bonds
209
- mol.AddBond(o, n1, Chem.BondType.SINGLE)
210
- mol.AddBond(o, n2, Chem.BondType.SINGLE)
211
-
212
- # Convert to immutable molecule and sanitize
213
- mol = mol.GetMol()
214
- Chem.SanitizeMol(mol)
215
- logger.debug(f"H2O (with N) creation result: {mol is not None}")
216
-
217
- else:
218
- # For other molecules, create from SMILES but replace H with N
219
- logger.debug(f"Creating molecule from SMILES: {molecule_data['smiles']}")
220
- mol = Chem.MolFromSmiles(molecule_data['smiles'])
221
- logger.debug(f"Initial molecule creation result: {mol is not None}")
222
- if mol is not None:
223
- # Replace explicit hydrogens with nitrogen
224
- mol = Chem.RWMol(mol)
225
- for atom in mol.GetAtoms():
226
- if atom.GetSymbol() == 'H':
227
- atom.SetAtomicNum(7) # Change to nitrogen
228
- mol = mol.GetMol()
229
- Chem.SanitizeMol(mol)
230
- logger.debug(f"Molecule with N replacing H result: {mol is not None}")
231
-
232
- if mol is None:
233
- logger.error(f"Failed to create molecule for {molecule_id}")
234
  return None
235
 
236
- logger.debug(f"Molecule created with {mol.GetNumAtoms()} atoms")
237
- for atom in mol.GetAtoms():
238
- # Calculate valence before checking hydrogens
239
- atom.UpdatePropertyCache()
240
- logger.debug(f"Atom {atom.GetIdx()}: Symbol={atom.GetSymbol()}, " +
241
- f"Valence={atom.GetTotalValence()}")
242
 
243
- # Log bond information
244
- logger.debug(f"Number of bonds: {mol.GetNumBonds()}")
245
- for bond in mol.GetBonds():
246
- logger.debug(f"Bond between {bond.GetBeginAtom().GetSymbol()}{bond.GetBeginAtomIdx()} and " +
247
- f"{bond.GetEndAtom().GetSymbol()}{bond.GetEndAtomIdx()}, type: {bond.GetBondType()}")
248
-
249
- # Generate 3D coordinates with distance matrix
250
- try:
251
- logger.debug("Attempting to generate 3D coordinates")
252
- # Use geometry template if available
253
- if 'geometry_template' in molecule_data:
254
- logger.debug("Using geometry template for 3D coordinates")
255
- conf = Chem.Conformer(mol.GetNumAtoms())
256
- for i, (atom_symbol, coords) in enumerate(molecule_data['geometry_template']):
257
- # Scale coordinates by the scale factor
258
- scaled_coords = np.array(coords) * scale_factor
259
- conf.SetAtomPosition(i, scaled_coords)
260
- mol.AddConformer(conf)
261
- result = 0 # Template-based embedding always succeeds
262
- else:
263
- result = AllChem.EmbedMolecule(mol, randomSeed=42)
264
-
265
- logger.debug(f"3D coordinate generation result (0=success, -1=failure): {result}")
266
-
267
- if result == -1:
268
- logger.debug("Standard embedding failed, trying with ETKDGv2")
269
- params = AllChem.ETKDGv2()
270
- result = AllChem.EmbedMolecule(mol, params, randomSeed=42)
271
- logger.debug(f"ETKDGv2 embedding result: {result}")
272
-
273
- if result == -1:
274
- logger.error("Failed to generate 3D coordinates")
275
- return None
276
-
277
- # Scale the molecule coordinates if not using template
278
- if 'geometry_template' not in molecule_data:
279
- conf = mol.GetConformer()
280
- positions = conf.GetPositions()
281
- logger.debug(f"Generated 3D coordinates:\n{positions}")
282
- centroid = np.mean(positions, axis=0)
283
- logger.debug(f"Molecule centroid: {centroid}")
284
-
285
- for i in range(mol.GetNumAtoms()):
286
- pos = positions[i]
287
- scaled_pos = centroid + (pos - centroid) * scale_factor
288
- conf.SetAtomPosition(i, scaled_pos)
289
- logger.debug(f"Atom {i} ({mol.GetAtomWithIdx(i).GetSymbol()}) scaled position: {scaled_pos}")
290
-
291
- logger.info(f"Successfully created 3D coordinates for {molecule_id}")
292
-
293
- # Verify final state
294
- logger.debug("Final molecule state:")
295
- logger.debug(f"Number of atoms: {mol.GetNumAtoms()}")
296
- logger.debug(f"Number of bonds: {mol.GetNumBonds()}")
297
- logger.debug(f"Has conformer: {mol.GetNumConformers() > 0}")
298
-
299
- # Final sanitization check
300
- Chem.SanitizeMol(mol)
301
-
302
- # Set RDKit display properties for ball-and-stick appearance
303
- for atom in mol.GetAtoms():
304
- atom.SetProp('molAtomMapNumber', str(atom.GetIdx())) # This helps with atom visibility
305
-
306
- # Return the molecule as a list as expected by MolGallery3D
307
- return [mol]
308
 
309
- except Exception as e:
310
- logger.error(f"Failed to generate 3D coordinates: {e}", exc_info=True)
311
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  except Exception as e:
314
  logger.error(f"Failed to create molecule viewer: {e}", exc_info=True)
315
  return None
 
1
  """
2
  visualization.py
3
+ Routines for plotting VQE results and molecular visualization using Plotly
4
  """
5
 
6
  import plotly.graph_objects as go
7
  import numpy as np
8
  import logging
 
 
 
9
  import json
10
 
11
  # Configure logging
12
+ # Set root logger to DEBUG level
13
+ logging.getLogger().setLevel(logging.DEBUG)
14
+
15
+ # Configure module logger
 
 
16
  logger = logging.getLogger(__name__)
17
+ logger.setLevel(logging.DEBUG)
18
+
19
+ # Create handlers with DEBUG level
20
+ console_handler = logging.StreamHandler()
21
+ console_handler.setLevel(logging.DEBUG)
22
+ file_handler = logging.FileHandler('molecule_creation.log')
23
+ file_handler.setLevel(logging.DEBUG)
24
+
25
+ # Create and set formatter
26
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
27
+ console_handler.setFormatter(formatter)
28
+ file_handler.setFormatter(formatter)
29
+
30
+ # Add handlers to logger
31
+ logger.addHandler(console_handler)
32
+ logger.addHandler(file_handler)
33
+
34
+ # Atom display properties
35
+ ATOM_COLORS = {
36
+ 'H': '#E8E8E8', # Light gray - more visible than pure white
37
+ 'Be': '#42F5EC', # Bright turquoise - stands out nicely
38
+ 'Li': '#FF34B3', # Hot pink - distinctive
39
+ 'B': '#FFB347', # Pastel orange
40
+ 'C': '#808080', # Classic gray for carbon
41
+ 'N': '#4169E1', # Royal blue - richer than pure blue
42
+ 'O': '#FF4500', # Orange red - more distinctive than pure red
43
+ 'F': '#DAA520', # Goldenrod - warmer than yellow
44
+ 'Na': '#9370DB', # Medium purple - softer than bright purple
45
+ 'Mg': '#98FB98', # Pale green - distinctive from beryllium
46
+ 'Al': '#DDA0DD' # Plum - distinctive pink/purple
47
+ }
48
 
49
+ # Bond display properties
50
+ BOND_STYLE = dict(
51
+ color='#2F4F4F', # Dark slate gray - better contrast
52
+ width=8 # Thicker bonds
53
+ )
54
+
55
+ ATOM_SIZES = {
56
+ 'H': 12, # Smaller for hydrogen
57
+ 'Be': 20, # Medium for beryllium
58
+ 'Li': 20, # Medium for lithium
59
+ 'B': 20, # Medium for boron
60
+ 'C': 16, # Medium for carbon
61
+ 'N': 16, # Medium for nitrogen
62
+ 'O': 16, # Medium for oxygen
63
+ 'F': 16, # Medium for fluorine
64
+ 'Na': 24, # Larger for sodium
65
+ 'Mg': 24, # Larger for magnesium
66
+ 'Al': 20 # Medium for aluminum
67
+ }
68
 
69
  def format_molecule_params(molecule_data: dict) -> str:
70
  """
 
123
  # Handle both dictionary entries and direct energy values
124
  for i, entry in enumerate(history):
125
  if isinstance(entry, dict):
 
126
  try:
127
  iterations.append(entry.get('iteration', i))
128
  energies.append(entry['energy'])
 
130
  logger.warning(f"Skipping invalid entry {i}: {str(e)}")
131
  continue
132
  else:
 
133
  try:
134
  iterations.append(i)
135
  energies.append(float(entry))
 
185
 
186
  return fig
187
 
188
+ def create_molecule_viewer(molecule_id: str, scale_factor: float) -> go.Figure:
189
  """
190
+ Create a 3D visualization of the molecule using Plotly.
191
+ Uses geometry templates from molecules.json for accurate atomic positions.
 
 
192
 
193
  Args:
194
  molecule_id: Molecule identifier (e.g., "H2")
195
  scale_factor: Bond length scaling factor (1.0 = original size)
196
+ Returns:
197
+ Plotly Figure object with 3D molecule visualization
198
  """
199
  logger.info(f"Creating 3D view for {molecule_id} with scale_factor={scale_factor}")
200
 
 
208
  return None
209
 
210
  molecule_data = molecules[molecule_id]
211
+ if 'geometry_template' not in molecule_data:
212
+ logger.error(f"No geometry template found for {molecule_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  return None
214
 
215
+ geometry = molecule_data['geometry_template']
 
 
 
 
 
216
 
217
+ # Extract atomic positions and symbols
218
+ symbols = [atom[0] for atom in geometry]
219
+ positions = np.array([atom[1] for atom in geometry]) * scale_factor
220
+
221
+ # Create figure
222
+ fig = go.Figure()
223
+
224
+ # Add atoms as spheres
225
+ for symbol, pos in zip(symbols, positions):
226
+ color = ATOM_COLORS.get(symbol, '#808080') # Default gray if not specified
227
+ size = ATOM_SIZES.get(symbol, 20) # Default size if not specified
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
+ fig.add_trace(go.Scatter3d(
230
+ x=[pos[0]],
231
+ y=[pos[1]],
232
+ z=[pos[2]],
233
+ mode='markers+text',
234
+ marker=dict(
235
+ size=size,
236
+ color=color,
237
+ symbol='circle',
238
+ line=dict(color='black', width=1)
239
+ ),
240
+ text=[symbol],
241
+ textposition='middle center',
242
+ name=symbol,
243
+ hoverinfo='name',
244
+ showlegend=False
245
+ ))
246
 
247
+ # Add bonds between atoms
248
+ for i in range(len(symbols)):
249
+ for j in range(i + 1, len(symbols)):
250
+ # Calculate distance between atoms
251
+ dist = np.linalg.norm(positions[i] - positions[j])
252
+ # Add bond if atoms are close enough (adjust threshold as needed)
253
+ if dist < 2.5 * scale_factor: # Typical bond length is ~1-1.5 Å
254
+ # Create line between atoms
255
+ fig.add_trace(go.Scatter3d(
256
+ x=[positions[i][0], positions[j][0]],
257
+ y=[positions[i][1], positions[j][1]],
258
+ z=[positions[i][2], positions[j][2]],
259
+ mode='lines',
260
+ line=BOND_STYLE,
261
+ hoverinfo='none',
262
+ showlegend=False
263
+ ))
264
+
265
+ # Update layout for better visualization
266
+ fig.update_layout(
267
+ scene=dict(
268
+ aspectmode='data', # Preserve actual molecular geometry
269
+ xaxis=dict(showspikes=False, showbackground=False, showticklabels=False, title=''),
270
+ yaxis=dict(showspikes=False, showbackground=False, showticklabels=False, title=''),
271
+ zaxis=dict(showspikes=False, showbackground=False, showticklabels=False, title=''),
272
+ camera=dict(
273
+ up=dict(x=0, y=1, z=0),
274
+ center=dict(x=0, y=0, z=0),
275
+ eye=dict(x=1.5, y=1.5, z=1.5)
276
+ )
277
+ ),
278
+ margin=dict(l=0, r=0, t=0, b=0),
279
+ showlegend=False,
280
+ width=400,
281
+ height=400,
282
+ paper_bgcolor='rgba(0,0,0,0)',
283
+ plot_bgcolor='rgba(0,0,0,0)'
284
+ )
285
+
286
+ return fig
287
+
288
  except Exception as e:
289
  logger.error(f"Failed to create molecule viewer: {e}", exc_info=True)
290
  return None