molbuilder 1.0.0__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,615 @@
1
+ """Main animation renderer for extreme slow-motion atomic interactions.
2
+
3
+ Provides ``InteractionVisualizer`` which builds matplotlib FuncAnimation
4
+ pipelines that render MD trajectories at sub-femtosecond resolution,
5
+ with optional overlays for electron density, curly arrows, energy bars,
6
+ and time labels.
7
+
8
+ Convenience functions:
9
+ - ``visualize_md_trajectory(mol, n_steps, ...)``
10
+ - ``visualize_reaction(mol, mechanism, ...)``
11
+ - ``visualize_bond_formation(mol, atom_i, atom_j, ...)``
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import math
17
+ from dataclasses import dataclass, field
18
+ from typing import TYPE_CHECKING
19
+
20
+ import numpy as np
21
+
22
+ try:
23
+ import matplotlib
24
+ import matplotlib.pyplot as plt
25
+ from matplotlib.animation import FuncAnimation
26
+ from matplotlib.patches import FancyArrowPatch
27
+ HAS_MATPLOTLIB = True
28
+ except ImportError:
29
+ HAS_MATPLOTLIB = False
30
+
31
+ from molbuilder.core.element_properties import cpk_color, covalent_radius_pm
32
+ from molbuilder.core.elements import SYMBOL_TO_Z
33
+ from molbuilder.visualization.theme import (
34
+ BG_COLOR, TEXT_COLOR, BOND_COLOR,
35
+ FORMING_BOND_COLOR, BREAKING_BOND_COLOR,
36
+ ELECTRON_CLOUD_COLOR, ARROW_COLOR, TRANSITION_STATE_COLOR,
37
+ )
38
+
39
+ if TYPE_CHECKING:
40
+ from molbuilder.molecule.graph import Molecule
41
+ from molbuilder.dynamics.trajectory import Trajectory
42
+ from molbuilder.dynamics.mechanisms import ReactionMechanism, ElectronFlow
43
+ from molbuilder.dynamics.mechanism_choreography import MechanismChoreographer
44
+
45
+
46
+ # ===================================================================
47
+ # Configuration
48
+ # ===================================================================
49
+
50
+ @dataclass
51
+ class PlaybackConfig:
52
+ """Configuration for the interaction visualizer.
53
+
54
+ Attributes
55
+ ----------
56
+ slowmo_factor : float
57
+ Slowdown factor. Default 1e15 means 1 fs of simulation time
58
+ maps to 1 second of animation (extreme slow motion).
59
+ fps : int
60
+ Animation frame rate.
61
+ show_electron_density : bool
62
+ Render electron density clouds during bond events.
63
+ show_electron_flows : bool
64
+ Render curly/fishhook arrows for electron flow.
65
+ show_energy_bar : bool
66
+ Show an energy bar overlay.
67
+ show_time_label : bool
68
+ Show a time label with SI prefix.
69
+ show_bond_orders : bool
70
+ Show fractional bond orders as annotations.
71
+ export_path : str or None
72
+ If set, export animation to this file path (.mp4 or .gif).
73
+ camera_follow : bool
74
+ Auto-track the reaction center.
75
+ dpi : int
76
+ Resolution for export.
77
+ figsize : tuple[float, float]
78
+ Figure size in inches.
79
+ """
80
+ slowmo_factor: float = 1e15
81
+ fps: int = 30
82
+ show_electron_density: bool = True
83
+ show_electron_flows: bool = True
84
+ show_energy_bar: bool = True
85
+ show_time_label: bool = True
86
+ show_bond_orders: bool = False
87
+ export_path: str | None = None
88
+ camera_follow: bool = False
89
+ dpi: int = 150
90
+ figsize: tuple[float, float] = (10, 8)
91
+
92
+
93
+ def _time_label(t_fs: float) -> str:
94
+ """Format time with appropriate SI prefix."""
95
+ if abs(t_fs) < 1e-3:
96
+ return f"{t_fs * 1e3:.2f} as"
97
+ if abs(t_fs) < 1.0:
98
+ return f"{t_fs:.3f} fs"
99
+ if abs(t_fs) < 1000.0:
100
+ return f"{t_fs:.1f} fs"
101
+ return f"{t_fs / 1000.0:.2f} ps"
102
+
103
+
104
+ def _bond_color_from_order(order: float) -> str:
105
+ """Choose bond color based on fractional bond order."""
106
+ if order < 0.3:
107
+ return BREAKING_BOND_COLOR
108
+ if order > 0.7 and order < 1.3:
109
+ return BOND_COLOR
110
+ if order >= 0.3 and order < 0.7:
111
+ return TRANSITION_STATE_COLOR
112
+ return FORMING_BOND_COLOR
113
+
114
+
115
+ # ===================================================================
116
+ # InteractionVisualizer
117
+ # ===================================================================
118
+
119
+ class InteractionVisualizer:
120
+ """Renders MD trajectories as slow-motion matplotlib animations.
121
+
122
+ Parameters
123
+ ----------
124
+ trajectory : Trajectory
125
+ The MD trajectory to visualize.
126
+ molecule : Molecule
127
+ Original molecule (for bond connectivity and atom symbols).
128
+ mechanism : ReactionMechanism or None
129
+ Optional mechanism for electron flow overlays.
130
+ choreographer : MechanismChoreographer or None
131
+ Optional choreographer for mechanism annotations.
132
+ config : PlaybackConfig or None
133
+ Playback configuration.
134
+ """
135
+
136
+ def __init__(self, trajectory, molecule,
137
+ mechanism=None, choreographer=None,
138
+ config: PlaybackConfig | None = None):
139
+ if not HAS_MATPLOTLIB:
140
+ raise ImportError("matplotlib is required for visualization")
141
+
142
+ self.trajectory = trajectory
143
+ self.molecule = molecule
144
+ self.mechanism = mechanism
145
+ self.choreographer = choreographer
146
+ self.config = config or PlaybackConfig()
147
+
148
+ # Precompute animation frames
149
+ duration_fs = trajectory.duration
150
+ duration_anim_s = duration_fs * self.config.slowmo_factor * 1e-15
151
+ # Minimum 2 seconds of animation
152
+ duration_anim_s = max(duration_anim_s, 2.0)
153
+ self.n_frames = max(int(duration_anim_s * self.config.fps), 2)
154
+
155
+ # Time array for each animation frame
156
+ self.frame_times = np.linspace(
157
+ trajectory.t_start, trajectory.t_end, self.n_frames)
158
+
159
+ # Electron density renderer
160
+ self._edensity = None
161
+ if self.config.show_electron_density:
162
+ from molbuilder.visualization.electron_density_viz import (
163
+ ElectronDensityRenderer,
164
+ )
165
+ self._edensity = ElectronDensityRenderer(n_points=2000)
166
+
167
+ self._fig = None
168
+ self._ax = None
169
+ self._anim = None
170
+ self._paused = False
171
+
172
+ def build_animation(self) -> FuncAnimation:
173
+ """Build and return the matplotlib FuncAnimation.
174
+
175
+ Returns
176
+ -------
177
+ FuncAnimation
178
+ The animation object. Call ``plt.show()`` to display, or
179
+ use ``export()`` to save.
180
+ """
181
+ self._fig, self._ax = plt.subplots(
182
+ figsize=self.config.figsize,
183
+ facecolor=BG_COLOR,
184
+ )
185
+ self._ax.set_facecolor(BG_COLOR)
186
+ self._ax.set_aspect("equal")
187
+ self._ax.tick_params(colors=TEXT_COLOR)
188
+ for spine in self._ax.spines.values():
189
+ spine.set_color(TEXT_COLOR)
190
+
191
+ self._anim = FuncAnimation(
192
+ self._fig,
193
+ self._render_frame,
194
+ frames=self.n_frames,
195
+ interval=1000 // self.config.fps,
196
+ blit=False,
197
+ repeat=True,
198
+ )
199
+
200
+ return self._anim
201
+
202
+ def _render_frame(self, frame_idx: int):
203
+ """Render a single animation frame."""
204
+ ax = self._ax
205
+ ax.clear()
206
+ ax.set_facecolor(BG_COLOR)
207
+
208
+ t_fs = self.frame_times[frame_idx]
209
+ positions = self.trajectory.at_time(t_fs)
210
+
211
+ mol = self.molecule
212
+ symbols = [a.symbol for a in mol.atoms]
213
+
214
+ # Compute bounds for axis
215
+ margin = 2.0
216
+ x_min = positions[:, 0].min() - margin
217
+ x_max = positions[:, 0].max() + margin
218
+ y_min = positions[:, 1].min() - margin
219
+ y_max = positions[:, 1].max() + margin
220
+ ax.set_xlim(x_min, x_max)
221
+ ax.set_ylim(y_min, y_max)
222
+ ax.set_aspect("equal")
223
+
224
+ # Draw bonds
225
+ for bond in mol.bonds:
226
+ i, j = bond.atom_i, bond.atom_j
227
+ key = (min(i, j), max(i, j))
228
+ order = float(bond.order)
229
+
230
+ # Check for fractional bond order from trajectory
231
+ frac_order = self.trajectory.bond_order_at_time(t_fs, i, j)
232
+ if frac_order > 0.01:
233
+ order = frac_order
234
+
235
+ # Bond styling
236
+ color = BOND_COLOR
237
+ linestyle = "-"
238
+ linewidth = 1.5 * max(order, 0.2)
239
+
240
+ if frac_order > 0.01:
241
+ color = _bond_color_from_order(frac_order)
242
+ if frac_order < 0.5:
243
+ linestyle = ":"
244
+ elif frac_order < 0.8:
245
+ linestyle = "--"
246
+
247
+ pi = positions[i]
248
+ pj = positions[j]
249
+ ax.plot(
250
+ [pi[0], pj[0]], [pi[1], pj[1]],
251
+ color=color, linewidth=linewidth,
252
+ linestyle=linestyle, zorder=1,
253
+ )
254
+
255
+ # Bond order annotation
256
+ if self.config.show_bond_orders and frac_order > 0.05:
257
+ mid = 0.5 * (pi + pj)
258
+ ax.text(mid[0], mid[1] + 0.15, f"{frac_order:.2f}",
259
+ color=TEXT_COLOR, fontsize=7, ha="center",
260
+ va="bottom", zorder=5)
261
+
262
+ # Draw electron density
263
+ if self._edensity is not None and self.config.show_electron_density:
264
+ for bond in mol.bonds:
265
+ i, j = bond.atom_i, bond.atom_j
266
+ frac_order = self.trajectory.bond_order_at_time(t_fs, i, j)
267
+ if 0.1 < frac_order < 0.9:
268
+ z_a = SYMBOL_TO_Z.get(symbols[i], 1)
269
+ z_b = SYMBOL_TO_Z.get(symbols[j], 1)
270
+ self._edensity.render_on_axis_2d(
271
+ ax, positions[i], positions[j],
272
+ z_a, z_b, frac_order, point_size=1.5)
273
+
274
+ # Draw electron flow arrows
275
+ if (self.config.show_electron_flows
276
+ and self.mechanism is not None
277
+ and self.choreographer is not None):
278
+ self._draw_electron_flows(ax, positions, t_fs, frame_idx)
279
+
280
+ # Draw atoms (on top of bonds)
281
+ for idx, atom in enumerate(mol.atoms):
282
+ x, y = positions[idx, 0], positions[idx, 1]
283
+ color = cpk_color(atom.symbol)
284
+ radius = covalent_radius_pm(atom.symbol) / 100.0 * 0.3
285
+ radius = max(radius, 0.15)
286
+
287
+ circle = plt.Circle(
288
+ (x, y), radius, color=color,
289
+ zorder=3, ec="#222222", linewidth=0.5)
290
+ ax.add_patch(circle)
291
+
292
+ # Atom label for non-hydrogen atoms
293
+ if atom.symbol != "H":
294
+ ax.text(x, y, atom.symbol,
295
+ color="#000000", fontsize=7, fontweight="bold",
296
+ ha="center", va="center", zorder=4)
297
+
298
+ # Overlays
299
+ if self.config.show_time_label:
300
+ ax.text(0.02, 0.98, _time_label(t_fs),
301
+ transform=ax.transAxes,
302
+ color=TEXT_COLOR, fontsize=12, fontweight="bold",
303
+ va="top", ha="left",
304
+ bbox=dict(boxstyle="round,pad=0.3",
305
+ facecolor=BG_COLOR, alpha=0.8))
306
+
307
+ if self.config.show_energy_bar:
308
+ self._draw_energy_bar(ax, t_fs)
309
+
310
+ # Mechanism annotation
311
+ if self.choreographer is not None:
312
+ stage_idx = self._stage_for_time(t_fs)
313
+ if stage_idx is not None:
314
+ annotation = self.choreographer.stage_annotation(stage_idx)
315
+ if annotation:
316
+ ax.text(0.5, 0.02, annotation,
317
+ transform=ax.transAxes,
318
+ color=TRANSITION_STATE_COLOR,
319
+ fontsize=10, ha="center", va="bottom",
320
+ bbox=dict(boxstyle="round,pad=0.3",
321
+ facecolor=BG_COLOR, alpha=0.8))
322
+
323
+ ax.set_xlabel("x (A)", color=TEXT_COLOR, fontsize=8)
324
+ ax.set_ylabel("y (A)", color=TEXT_COLOR, fontsize=8)
325
+
326
+ def _draw_electron_flows(self, ax, positions, t_fs, frame_idx):
327
+ """Draw curly/fishhook arrows for electron flows."""
328
+ from molbuilder.dynamics.mechanisms import FlowType
329
+
330
+ stage_idx = self._stage_for_time(t_fs)
331
+ if stage_idx is None:
332
+ return
333
+
334
+ total_steps = len(self.mechanism.stages)
335
+ progress = (frame_idx / max(1, self.n_frames - 1))
336
+ stage_progress = (progress * total_steps) - stage_idx
337
+
338
+ flows = self.choreographer.electron_flows_at(
339
+ stage_idx, stage_progress)
340
+
341
+ for flow in flows:
342
+ start = positions[flow.from_atom][:2]
343
+ mid_bond = 0.5 * (
344
+ positions[flow.to_bond[0]][:2]
345
+ + positions[flow.to_bond[1]][:2])
346
+
347
+ color = ARROW_COLOR
348
+ style = "Simple,tail_width=2,head_width=8,head_length=6"
349
+ if flow.flow_type == FlowType.FISHHOOK_ARROW:
350
+ style = "Simple,tail_width=1,head_width=5,head_length=4"
351
+
352
+ arrow = FancyArrowPatch(
353
+ posA=tuple(start), posB=tuple(mid_bond),
354
+ arrowstyle=style,
355
+ color=color, alpha=0.8,
356
+ connectionstyle="arc3,rad=0.3",
357
+ zorder=6,
358
+ )
359
+ ax.add_patch(arrow)
360
+
361
+ if flow.label:
362
+ label_pos = 0.5 * (start + mid_bond)
363
+ ax.text(label_pos[0], label_pos[1] + 0.3,
364
+ flow.label, color=ARROW_COLOR,
365
+ fontsize=6, ha="center", va="bottom",
366
+ zorder=7)
367
+
368
+ def _draw_energy_bar(self, ax, t_fs):
369
+ """Draw a small energy bar overlay."""
370
+ times, ke, pe = self.trajectory.energies()
371
+ if len(times) < 2:
372
+ return
373
+
374
+ # Find closest frame
375
+ idx = np.searchsorted(times, t_fs)
376
+ idx = min(idx, len(times) - 1)
377
+ ke_val = ke[idx]
378
+ pe_val = pe[idx]
379
+ total = ke_val + pe_val
380
+
381
+ bar_text = f"KE: {ke_val:.1f} PE: {pe_val:.1f} Total: {total:.1f} kJ/mol"
382
+ ax.text(0.98, 0.98, bar_text,
383
+ transform=ax.transAxes,
384
+ color=TEXT_COLOR, fontsize=8,
385
+ va="top", ha="right",
386
+ bbox=dict(boxstyle="round,pad=0.3",
387
+ facecolor=BG_COLOR, alpha=0.8))
388
+
389
+ def _stage_for_time(self, t_fs):
390
+ """Determine which mechanism stage corresponds to a time."""
391
+ if self.mechanism is None:
392
+ return None
393
+ n_stages = len(self.mechanism.stages)
394
+ if n_stages == 0:
395
+ return None
396
+ frac = (t_fs - self.trajectory.t_start) / max(
397
+ self.trajectory.duration, 1e-10)
398
+ stage = int(frac * n_stages)
399
+ return min(stage, n_stages - 1)
400
+
401
+ def show(self):
402
+ """Build and display the animation interactively."""
403
+ self.build_animation()
404
+ plt.show()
405
+
406
+ def export(self, path: str | None = None):
407
+ """Export the animation to a file.
408
+
409
+ Parameters
410
+ ----------
411
+ path : str or None
412
+ Output path. If None, uses config.export_path.
413
+ Supports .mp4 (requires ffmpeg) and .gif (Pillow).
414
+ """
415
+ path = path or self.config.export_path
416
+ if path is None:
417
+ raise ValueError("No export path specified")
418
+
419
+ if self._anim is None:
420
+ self.build_animation()
421
+
422
+ if path.endswith(".mp4"):
423
+ try:
424
+ from matplotlib.animation import FFMpegWriter
425
+ writer = FFMpegWriter(fps=self.config.fps)
426
+ self._anim.save(path, writer=writer, dpi=self.config.dpi)
427
+ except Exception:
428
+ # Fallback to gif
429
+ path = path.replace(".mp4", ".gif")
430
+ from matplotlib.animation import PillowWriter
431
+ writer = PillowWriter(fps=self.config.fps)
432
+ self._anim.save(path, writer=writer, dpi=self.config.dpi)
433
+ elif path.endswith(".gif"):
434
+ from matplotlib.animation import PillowWriter
435
+ writer = PillowWriter(fps=self.config.fps)
436
+ self._anim.save(path, writer=writer, dpi=self.config.dpi)
437
+ else:
438
+ self._anim.save(path, dpi=self.config.dpi)
439
+
440
+ plt.close(self._fig)
441
+ return path
442
+
443
+ @property
444
+ def fig(self):
445
+ return self._fig
446
+
447
+
448
+ # ===================================================================
449
+ # Convenience functions
450
+ # ===================================================================
451
+
452
+ def visualize_md_trajectory(molecule,
453
+ n_steps: int = 500,
454
+ dt_fs: float = 0.5,
455
+ temperature_K: float = 300.0,
456
+ config: PlaybackConfig | None = None,
457
+ show: bool = True):
458
+ """Quick MD simulation + visualization.
459
+
460
+ Parameters
461
+ ----------
462
+ molecule : Molecule
463
+ Molecule to simulate.
464
+ n_steps : int
465
+ Number of MD steps.
466
+ dt_fs : float
467
+ Timestep in fs.
468
+ temperature_K : float
469
+ Target temperature.
470
+ config : PlaybackConfig or None
471
+ Visualization config.
472
+ show : bool
473
+ If True, display interactively.
474
+
475
+ Returns
476
+ -------
477
+ InteractionVisualizer
478
+ The visualizer (can be used for export).
479
+ """
480
+ from molbuilder.dynamics.simulation import MDSimulation
481
+
482
+ sim = MDSimulation(molecule, dt_fs=dt_fs,
483
+ temperature_K=temperature_K)
484
+ traj = sim.run(n_steps)
485
+
486
+ viz = InteractionVisualizer(traj, molecule, config=config)
487
+ if show:
488
+ viz.show()
489
+ return viz
490
+
491
+
492
+ def visualize_reaction(molecule,
493
+ mechanism,
494
+ n_steps_per_stage: int = 200,
495
+ dt_fs: float = 0.5,
496
+ temperature_K: float = 50.0,
497
+ config: PlaybackConfig | None = None,
498
+ show: bool = True):
499
+ """Mechanism-steered MD + visualization.
500
+
501
+ Parameters
502
+ ----------
503
+ molecule : Molecule
504
+ Starting molecule.
505
+ mechanism : ReactionMechanism
506
+ Reaction mechanism template.
507
+ n_steps_per_stage : int
508
+ MD steps per mechanism stage.
509
+ dt_fs : float
510
+ Timestep.
511
+ temperature_K : float
512
+ Temperature (low for cleaner mechanism).
513
+ config : PlaybackConfig or None
514
+ Visualization config.
515
+ show : bool
516
+ Display interactively.
517
+
518
+ Returns
519
+ -------
520
+ InteractionVisualizer
521
+ """
522
+ from molbuilder.dynamics.simulation import MDSimulation
523
+ from molbuilder.dynamics.mechanism_choreography import MechanismChoreographer
524
+ from molbuilder.dynamics.forcefield import ForceField
525
+
526
+ sim = MDSimulation(molecule, dt_fs=dt_fs,
527
+ temperature_K=temperature_K,
528
+ thermostat=True,
529
+ thermostat_tau_fs=50.0)
530
+ traj = sim.run_mechanism(mechanism, n_steps_per_stage)
531
+
532
+ choreographer = MechanismChoreographer(
533
+ mechanism, sim.ff, n_steps_per_stage)
534
+ viz = InteractionVisualizer(
535
+ traj, molecule,
536
+ mechanism=mechanism,
537
+ choreographer=choreographer,
538
+ config=config)
539
+ if show:
540
+ viz.show()
541
+ return viz
542
+
543
+
544
+ def visualize_bond_formation(molecule,
545
+ atom_i: int, atom_j: int,
546
+ n_steps: int = 400,
547
+ config: PlaybackConfig | None = None,
548
+ show: bool = True):
549
+ """Visualize a single bond formation event.
550
+
551
+ Creates a simple mechanism that brings two atoms together and
552
+ forms a bond.
553
+
554
+ Parameters
555
+ ----------
556
+ molecule : Molecule
557
+ Molecule containing both atoms.
558
+ atom_i, atom_j : int
559
+ Atom indices for the bond to form.
560
+ n_steps : int
561
+ Total MD steps.
562
+ config : PlaybackConfig or None
563
+ Visualization config.
564
+ show : bool
565
+ Display interactively.
566
+
567
+ Returns
568
+ -------
569
+ InteractionVisualizer
570
+ """
571
+ from molbuilder.dynamics.mechanisms import (
572
+ ReactionMechanism, MechanismStage, MechanismType,
573
+ ElectronFlow, FlowType,
574
+ )
575
+ from molbuilder.core.bond_data import bond_length
576
+
577
+ sym_i = molecule.atoms[atom_i].symbol
578
+ sym_j = molecule.atoms[atom_j].symbol
579
+ target_bl = bond_length(sym_i, sym_j, 1)
580
+
581
+ key = (min(atom_i, atom_j), max(atom_i, atom_j))
582
+ mechanism = ReactionMechanism(
583
+ name=f"Bond formation {sym_i}-{sym_j}",
584
+ mechanism_type=MechanismType.SN2,
585
+ stages=[
586
+ MechanismStage(
587
+ name="Approach",
588
+ distance_targets={(atom_i, atom_j): target_bl * 1.5},
589
+ bond_order_changes={key: 0.3},
590
+ electron_flows=[
591
+ ElectronFlow(atom_i, (atom_i, atom_j),
592
+ FlowType.CURLY_ARROW,
593
+ "Bond forming"),
594
+ ],
595
+ duration_weight=1.0,
596
+ annotation=f"Atoms approaching: {sym_i}...{sym_j}",
597
+ ),
598
+ MechanismStage(
599
+ name="Bond formation",
600
+ distance_targets={(atom_i, atom_j): target_bl},
601
+ bond_order_changes={key: 1.0},
602
+ electron_flows=[],
603
+ duration_weight=1.5,
604
+ annotation=f"Bond formed: {sym_i}-{sym_j}",
605
+ ),
606
+ ],
607
+ atom_roles={"atom_i": atom_i, "atom_j": atom_j},
608
+ )
609
+
610
+ return visualize_reaction(
611
+ molecule, mechanism,
612
+ n_steps_per_stage=n_steps // 2,
613
+ temperature_K=50.0,
614
+ config=config,
615
+ show=show)
@@ -10,3 +10,10 @@ POSITIVE_COLOR = "#3399ff"
10
10
  NEGATIVE_COLOR = "#ff5533"
11
11
  NEUTRAL_COLOR = "#44ccff"
12
12
  ENERGY_COLOR = "#ffaa33"
13
+
14
+ # Interaction visualization colors
15
+ FORMING_BOND_COLOR = "#44ff88"
16
+ BREAKING_BOND_COLOR = "#ff4444"
17
+ ELECTRON_CLOUD_COLOR = "#6699ff"
18
+ ARROW_COLOR = "#ff8800"
19
+ TRANSITION_STATE_COLOR = "#ffff44"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: molbuilder
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Professional-grade molecular engineering toolkit: atoms to retrosynthesis to process engineering
5
5
  Author: Taylor C. Powell
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
- molbuilder/__init__.py,sha256=0Nb0iCNk1ApK_Vx6m4oHxzhT_RiX97YYnVI_eTdAgbw,205
1
+ molbuilder/__init__.py,sha256=XV1Q1NePEaugMbuY_n_ALj4OerKAIAZwdEtQh__YP7I,205
2
2
  molbuilder/__main__.py,sha256=F64UnMCnwJXEF59k6jtSoLga7al9rRvGxAOK2FCpA9U,123
3
3
  molbuilder/atomic/__init__.py,sha256=dUtMg4Wr76cszBJ061BmTZN3akN9gK2AnjpRIvaExyM,217
4
4
  molbuilder/atomic/bohr.py,sha256=IPZjQqW4BSl_f96JZ45gbQVykIh17gn5eYhZtlJDfWo,8941
@@ -10,8 +10,8 @@ molbuilder/bonding/covalent.py,sha256=ewvRvYzbsmuRaVLiXnRyUCU8F_CdDz4Reo5o7b2qbm
10
10
  molbuilder/bonding/lewis.py,sha256=q4uXjvGKn3NO-TaZzFn8P7WaRgz1QarKF_JbznnCrLU,11967
11
11
  molbuilder/bonding/vsepr.py,sha256=YU8h4dOdSfCwDSIfqwkFeAkHr1LS7_Kws6DFeseMB6g,14946
12
12
  molbuilder/cli/__init__.py,sha256=eo7LYBWtUUUf3HLI564gX1qFQpQczPclSVurANG-b_I,64
13
- molbuilder/cli/demos.py,sha256=EV3y6e7u6_b_1sht6mJUB9R75HPd1VVXOElDM9MfR8k,17681
14
- molbuilder/cli/menu.py,sha256=bRFbx4gSlBlTc4Usiq7C53UNZGYitEBHU2tpVc2oQ8Y,3469
13
+ molbuilder/cli/demos.py,sha256=GuxeRXRiO1aZblvbj54Z2Ghm871ZfZanLt9oHUVU45s,20234
14
+ molbuilder/cli/menu.py,sha256=vUGT56ueRp-Rhf748bEXqtFgneNxGlpe9_maHvgyxvY,3568
15
15
  molbuilder/cli/wizard.py,sha256=NVQ9mGLsdP_FmXX5ubpdfZImmPR4f04tNReL-bKRvHc,27335
16
16
  molbuilder/core/__init__.py,sha256=QtbG7DjHWuvu88AP3uRukb_Ax1tMKKSTFfbNoP5PjYA,428
17
17
  molbuilder/core/bond_data.py,sha256=S5TcK6nWVG5--cIkG3Rw6-_trHVkP7quVmwglS7LSSE,5675
@@ -19,8 +19,15 @@ molbuilder/core/constants.py,sha256=Asehj4DftFKXNteveljh9bTFQaYugGY-ErIHdXRxMuA,
19
19
  molbuilder/core/element_properties.py,sha256=jiiE527bRgFLQcDepOfZWIK-hsTCMDMFEQIoUg2RdwU,7575
20
20
  molbuilder/core/elements.py,sha256=vTx0A6j53E0fl-8uOYvJXirBW-xQKc8PTp4JhO9us44,6870
21
21
  molbuilder/core/geometry.py,sha256=ktvmZHhnm1dmhHpW-9iY-ugVPJlLml8n2TvcptmFjiI,7440
22
+ molbuilder/dynamics/__init__.py,sha256=BK-b7XcXJ64H77Xgc4r8IbRZ4LUQFfPlUDl6m-qMVkA,1661
23
+ molbuilder/dynamics/forcefield.py,sha256=rvX44zj3O2e_CVuEhZ80vK2ebL-CiD5Y2s1kv7xYxnU,20370
24
+ molbuilder/dynamics/integrator.py,sha256=A6JKaS-8Mh6eysLa8asbZf559iVru_xOk-LfgELaETI,9130
25
+ molbuilder/dynamics/mechanism_choreography.py,sha256=0JLfpQw-6w-XT7-mKzZjmuMH3_tOa3sGtTyOu582sc4,7113
26
+ molbuilder/dynamics/mechanisms.py,sha256=PvmO3VqRyeeUpfXk6H8AkSIqo4fRbU3w-Fok5__NHVw,17773
27
+ molbuilder/dynamics/simulation.py,sha256=yosysL4iHuHa_FwmZJbj65QU8LSFqmDkBexNSiD4li4,7200
28
+ molbuilder/dynamics/trajectory.py,sha256=S7rofRDQucgXHt0EGTKXA2LyrB3gGJBD8BNuETzXYxk,6406
22
29
  molbuilder/gui/__init__.py,sha256=KqjMkDsQ5qIbSrK4C_YP45vHaomggTutwDg-dxmEmW4,99
23
- molbuilder/gui/app.py,sha256=O57Ksx9ajphXXlMbK5l-4zn0Nj2Ol1qvUhJv8uCXPkc,11507
30
+ molbuilder/gui/app.py,sha256=EffmhcivoDLSbkPVJg-x9Ob2lXVC72FjXvktE4FklR4,16254
24
31
  molbuilder/gui/canvas3d.py,sha256=LHVbgGbNhmyN24oCQ8z8XhwWhP9ehapTcbaP7at_ivY,4735
25
32
  molbuilder/gui/dialogs.py,sha256=ULUX3YiUHSjqWi7fCnMsi-e0pbYxWINJmYcKX8eiKhU,4037
26
33
  molbuilder/gui/event_handler.py,sha256=p9FF75csq7pf5u2we3uVfebri-tFWbJtRPVW5jtQX3o,4341
@@ -65,14 +72,17 @@ molbuilder/smiles/__init__.py,sha256=KX9neiR2temLI71KaZS_apcbrWZpPAAgRt4Q5FIJ9G8
65
72
  molbuilder/smiles/parser.py,sha256=rFoUlXH2t2skbXQyP9Xwn4JRjvWg0JcRsmq7gpCOAyw,15963
66
73
  molbuilder/smiles/tokenizer.py,sha256=HAaA8Nok0dIRB2jXfNzAO2Yuv7KgAlL0zMyFXWIos2w,8709
67
74
  molbuilder/smiles/writer.py,sha256=x7s-Y6HsF5yEhijvtQIPRBTkxTFccFtzsL-pKKcTHAQ,12025
68
- molbuilder/visualization/__init__.py,sha256=LMqqek4hZqYHMeDRAFiFuQIPJCWBKc5KlfnOtWjnZGM,70
75
+ molbuilder/visualization/__init__.py,sha256=EmYmHNXJs0veqnUeEX5tnQYP8q7zS_l1PWDgRJyVU9c,125
69
76
  molbuilder/visualization/bohr_viz.py,sha256=6lWZUOy4R1M6DdxJxHfBSY2DeW1KPDvbYKt5tXRx2t0,5624
77
+ molbuilder/visualization/electron_density_viz.py,sha256=4bKR-qaa1b9dLqE38qBbPA5ImKPtT4nB5RVfVKyTj70,7809
78
+ molbuilder/visualization/interaction_controls.py,sha256=YwHTU-QESi7hF0DWAQbEZlRdmxUhm9r9UrHxUyHcYJE,6363
79
+ molbuilder/visualization/interaction_viz.py,sha256=vTCnPeOQW9csQtD4TVmr5u_W5o4U3zJhru_nY9yePpA,20954
70
80
  molbuilder/visualization/molecule_viz.py,sha256=SvDIFvGviCGs9g0W7wQQj9frGdPydoBc4v5EsacNkgM,14002
71
81
  molbuilder/visualization/quantum_viz.py,sha256=7vvr1KaUa_WZ4FIb2r8ih6nKjUjP4piJeJosUBtsdrQ,15187
72
- molbuilder/visualization/theme.py,sha256=BzR8NfqCZ2Lq1y3cdgRkN5Qg8GnRqZGdEc_gq_JAdjY,321
73
- molbuilder-1.0.0.dist-info/licenses/LICENSE,sha256=WEWbPnVFDQwVEHeX_mpHol-ByLsQWgQZDMjSNjdFq8w,1094
74
- molbuilder-1.0.0.dist-info/METADATA,sha256=JYT7g6C1x8oXbQsyod9LhWhIt6Ag_0WXEKcYdBP3UBo,14828
75
- molbuilder-1.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
76
- molbuilder-1.0.0.dist-info/entry_points.txt,sha256=WkIjoyvOBBe_0KeT6ixldelD_7Hfj0mPADjamMmVgck,56
77
- molbuilder-1.0.0.dist-info/top_level.txt,sha256=zqS1kyhgo71hB_QcsI2eq5lO4w7VGiMaXH1f2Hn-kaA,11
78
- molbuilder-1.0.0.dist-info/RECORD,,
82
+ molbuilder/visualization/theme.py,sha256=j_jZ4lqF8v1GdHbyMxrOW1BWtN4AwwWEaB2LBPiDUeI,512
83
+ molbuilder-1.1.0.dist-info/licenses/LICENSE,sha256=WEWbPnVFDQwVEHeX_mpHol-ByLsQWgQZDMjSNjdFq8w,1094
84
+ molbuilder-1.1.0.dist-info/METADATA,sha256=kkOi_8G6HZTxCQXdFDjGQw0ApxZInBLbvQcH2eDnIx0,14828
85
+ molbuilder-1.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
86
+ molbuilder-1.1.0.dist-info/entry_points.txt,sha256=WkIjoyvOBBe_0KeT6ixldelD_7Hfj0mPADjamMmVgck,56
87
+ molbuilder-1.1.0.dist-info/top_level.txt,sha256=zqS1kyhgo71hB_QcsI2eq5lO4w7VGiMaXH1f2Hn-kaA,11
88
+ molbuilder-1.1.0.dist-info/RECORD,,