rdworks 0.25.7__py3-none-any.whl → 0.35.1__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.
rdworks/__init__.py CHANGED
@@ -1,35 +1,34 @@
1
- __version__ = '0.25.7'
1
+ __version__ = '0.35.1'
2
+
3
+ from rdworks.conf import Conf
4
+ from rdworks.mol import Mol
5
+ from rdworks.mollibr import MolLibr
2
6
 
3
- from rdworks.xml import list_predefined_xml, get_predefined_xml, parse_xml
4
- from rdworks.units import ev2kcalpermol, hartree2ev, hartree2kcalpermol, periodictable
5
- from rdworks.readin import read_csv, merge_csv, read_dataframe, read_smi, read_sdf, read_mae
6
- from rdworks.std import desalt_smiles, standardize_smiles, standardize
7
- from rdworks.tautomers import complete_tautomers
8
7
  from rdworks.stereoisomers import complete_stereoisomers
8
+ from rdworks.tautomers import complete_tautomers
9
9
  from rdworks.ionized import IonizedStates
10
+
11
+ from rdworks.readin import read_csv, merge_csv, read_dataframe, read_smi, read_sdf, read_mae
12
+ from rdworks.std import desalt_smiles, standardize_smiles, standardize
13
+
10
14
  from rdworks.rgroup import expand_rgroup, most_common, most_common_in_NP
11
15
  from rdworks.scaffold import scaffold_network, scaffold_tree, BRICS_fragmented, BRICS_fragment_indices
12
16
  from rdworks.matchedseries import MatchedSeries
13
- from rdworks.descriptor import rd_descriptor, rd_descriptor_f
14
- from rdworks.utils import fix_decimal_places_in_list, fix_decimal_places_in_dict, mae_to_dict, mae_rd_index
15
- from rdworks.display import svg
16
- from rdworks.conf import Conf
17
- from rdworks.mol import Mol
18
- from rdworks.mollibr import MolLibr
19
17
 
20
- from rdkit import rdBase, RDLogger
21
- rdkit_logger = RDLogger.logger().setLevel(RDLogger.CRITICAL)
18
+ from rdworks.descriptor import rd_descriptor, rd_descriptor_f
19
+ from rdworks.xml import list_predefined_xml, get_predefined_xml, parse_xml
22
20
 
21
+ import rdkit
23
22
  import logging
24
23
 
24
+ __rdkit_version__ = rdkit.rdBase.rdkitVersion
25
+
26
+ rdkit_logger = rdkit.RDLogger.logger().setLevel(rdkit.RDLogger.CRITICAL)
27
+
25
28
  main_logger = logging.getLogger()
26
29
  main_logger.setLevel(logging.INFO) # level: DEBUG < INFO < WARNING < ERROR < CRITICAL
27
- logger_formatter = logging.Formatter(
28
- fmt='%(asctime)s %(levelname)s %(message)s',
29
- datefmt='%Y-%m-%d %H:%M:%S')
30
+ logger_formatter = logging.Formatter(fmt='%(asctime)s %(levelname)s %(message)s',
31
+ datefmt='%Y-%m-%d %H:%M:%S')
30
32
  logger_ch = logging.StreamHandler()
31
33
  logger_ch.setFormatter(logger_formatter)
32
34
  main_logger.addHandler(logger_ch)
33
-
34
-
35
- __rdkit_version__ = rdBase.rdkitVersion
rdworks/conf.py CHANGED
@@ -1,46 +1,69 @@
1
1
  import io
2
2
  import copy
3
3
  import json
4
- import types
5
4
  import numpy as np
6
-
7
5
  import ase
8
- from ase.optimize import FIRE
9
6
 
10
7
  from collections.abc import Callable
8
+ from typing import Self
9
+
10
+ from mendeleev import element
11
+ from ase.optimize import FIRE
11
12
 
12
13
  from rdkit import Chem
13
14
  from rdkit.Chem import rdMolTransforms, AllChem, rdMolAlign
14
15
  from rdkit.Chem.Draw import rdMolDraw2D
16
+ from PIL import Image
15
17
 
16
- from typing import List, Optional, Union, Self
18
+ from rdworks.units import ev2kcalpermol, pm2angstrom
19
+ from rdworks.utils import recursive_round
20
+ from rdworks.xtb.wrapper import GFN2xTB
21
+ from rdworks.display import render_png, render_svg
17
22
 
18
- from .units import ev2kcalpermol
19
- from .element import radii
20
23
 
21
24
  class Conf:
22
- """Container for 3D conformers.
23
- """
25
+ """Container for 3D conformers."""
24
26
 
25
- def __init__(self, molecular_input:Chem.Mol, name:str='') -> None:
26
- """Create 3D conformers.
27
+ def __init__(self, molecule: str | Chem.Mol | None = None, name: str='') -> None:
28
+ """Initialize.
27
29
 
28
30
  Args:
29
- molecular_input (Chem.Mol): Molecule for conformer generation.
30
- name (str, optional): Name prefix of the generated conformers. Defaults to ''.
31
+ molecule (Chem.Mol | MolBlock string): Molecule for 3D conformer.
32
+ name (str): Name prefix of the generated conformers. Defaults to ''.
31
33
 
32
34
  Raises:
33
- ValueError: if `molecular_input` is not rdkit.Chem.Mol object.
35
+ ValueError: if `molecule` is not rdkit.Chem.Mol object.
34
36
  """
35
- self.rdmol = None # has only one rdkit conformer
37
+ assert isinstance(molecule, str | Chem.Mol) or molecule is None
38
+
39
+ self.rdmol = None # must contain one and only one rdkit conformer
36
40
  self.name = name
41
+ self.natoms = 0
37
42
  self.props = {}
38
- if isinstance(molecular_input, Chem.Mol):
39
- self.rdmol = molecular_input
40
- self.natoms = self.rdmol.GetNumAtoms()
41
- self.props.update({'atoms': self.natoms})
42
- else:
43
- raise ValueError(f'rdworks.Conf() takes Chem.Mol object')
43
+
44
+ if molecule is None:
45
+ return
46
+
47
+ if isinstance(molecule, str): # 3-D MolBLock string
48
+ try:
49
+ self.rdmol = Chem.MolFromMolBlock(molecule)
50
+ except:
51
+ ValueError(f'Conf() Error: invalid MolBlock string')
52
+
53
+ elif isinstance(molecule, Chem.Mol): # 3-D
54
+ try:
55
+ self.rdmol = molecule
56
+ except:
57
+ ValueError(f'Conf() Error: invalid Chem.Mol object')
58
+
59
+ num_atoms = self.rdmol.GetNumAtoms()
60
+ tot_atoms = self.rdmol.GetNumAtoms(onlyExplicit=False)
61
+ assert num_atoms == tot_atoms, "Conf() Error: missing hydrogens"
62
+ self.natoms = num_atoms
63
+ self.props.update({'atoms': num_atoms})
64
+
65
+ assert self.rdmol.GetConformer().Is3D(), "Conf() Error: not 3D"
66
+
44
67
 
45
68
 
46
69
  def __str__(self) -> str:
@@ -50,12 +73,7 @@ class Conf:
50
73
  str: string representation.
51
74
  """
52
75
  return f"<rdworks.Conf({self.rdmol} name={self.name} atoms={self.natoms})>"
53
-
54
-
55
- ##################################################
56
- ### Cascading methods
57
- ##################################################
58
-
76
+
59
77
 
60
78
  def copy(self) -> Self:
61
79
  """Returns a copy of self.
@@ -85,7 +103,7 @@ class Conf:
85
103
  return self
86
104
 
87
105
 
88
- def sync(self, coord:Union[np.ndarray, list]) -> Self:
106
+ def sync(self, coord: np.ndarray | list) -> Self:
89
107
  """Synchronize the conformer coordinates with the provided `coord`.
90
108
 
91
109
  Args:
@@ -107,56 +125,31 @@ class Conf:
107
125
  return self
108
126
 
109
127
 
110
- def get_potential_energy(self, calculator: str | Callable = 'MMFF94') -> float:
111
- """Get potential energy in kcal/mol.
128
+ def set_torsion(self, i:int, j:int, k:int, l:int, degree:float) -> Self:
129
+ """Set dihedral angle (i-j-k-l) in degrees.
112
130
 
113
131
  Args:
114
- calculator (str | Callable): MMFF94 (= MMFF), MMFF94s, UFF, or ASE calculator.
115
- `MMFF94` or `MMFF` - Intended for general use, including organic molecules and proteins,
116
- and primarily relies on data from quantum mechanical calculations.
117
- It's often used in molecular dynamics simulations.
118
- `MMFF94s` - A "static" variant of MMFF94, with adjusted parameters for out-of-plane
119
- bending and dihedral torsions to favor planar geometries for specific nitrogen atoms.
120
- This makes it better suited for geometry optimization studies where a static,
121
- time-averaged structure is desired. The "s" stands for "static".
122
- `UFF` - UFF refers to the "Universal Force Field," a force field model used for
123
- molecular mechanics calculations. It's a tool for geometry optimization,
124
- energy minimization, and exploring molecular conformations in 3D space.
125
- UFF is often used to refine conformers generated by other methods,
126
- such as random conformer generation, to produce more physically plausible
127
- and stable structures.
132
+ i (int): atom index
133
+ j (int): atom index
134
+ k (int): atom index
135
+ l (int): atom index
136
+ degree (float): dihedral angle in degrees
128
137
 
129
138
  Returns:
130
- float: potential energy in kcal/mol.
139
+ Self: modified Conf object
131
140
  """
132
- PE = None
133
- if isinstance(calculator, str):
134
- if calculator == 'MMFF94' or calculator == 'MMFF':
135
- mp = AllChem.MMFFGetMoleculeProperties(self.rdmol, mmffVariant='MMFF94')
136
- ff = AllChem.MMFFGetMoleculeForceField(self.rdmol, mp)
137
- elif calculator == 'MMFF94s':
138
- mp = AllChem.MMFFGetMoleculeProperties(self.rdmol, mmffVariant='MMFF94s')
139
- ff = AllChem.MMFFGetMoleculeForceField(self.rdmol, mp)
140
- elif calculator == 'UFF':
141
- ff = AllChem.UFFGetMoleculeForceField(self.rdmol)
142
- else:
143
- raise ValueError("Unsupported calculator")
144
- PE = ff.CalcEnergy()
145
- self.props.update({'E_tot(kcal/mol)': PE})
146
- else:
147
- try:
148
- ase_atoms = ase.Atoms(symbols=self.symbols(), positions=self.positions())
149
- ase_atoms.calc = calculator
150
- PE = ase_atoms.get_potential_energy() # np.array
151
- PE = ev2kcalpermol * float(PE[0]) # np.float64 to float
152
- self.props.update({'E_tot(kcal/mol)': PE})
153
- except:
154
- raise RuntimeError("ASE calculator error")
155
- return PE
141
+ rdMolTransforms.SetDihedralDeg(self.rdmol.GetConformer(), i, j, k, l, degree)
142
+
143
+ return self
156
144
 
157
145
 
158
- def optimize(self, calculator: str | Callable = 'MMFF94', fmax:float=0.05) -> Self:
159
- """Optimize conformation using a callable.
146
+
147
+ def optimize(self,
148
+ calculator: str | Callable = 'MMFF94',
149
+ fmax:float=0.05,
150
+ max_iter:int=1000,
151
+ **kwargs) -> Self:
152
+ """Optimize 3D geometry.
160
153
 
161
154
  Args:
162
155
  calculator (str | Callable): MMFF94 (= MMFF), MMFF94s, UFF, or ASE calculator.
@@ -173,31 +166,52 @@ class Conf:
173
166
  UFF is often used to refine conformers generated by other methods,
174
167
  such as random conformer generation, to produce more physically plausible
175
168
  and stable structures.
176
- fmax (float, optional): fmax for the calculator. Defaults to 0.05.
169
+ fmax (float, optional): fmax for the calculator convergence. Defaults to 0.05.
170
+ max_iter (int, optional): max iterations for the calculator. Defaults to 1000.
171
+
172
+ Args for xTB:
173
+ water (str, optional): water solvation model (choose 'gbsa' or 'alpb')
174
+ alpb: ALPB solvation model (Analytical Linearized Poisson-Boltzmann).
175
+ gbsa: generalized Born (GB) model with Surface Area contributions.
177
176
 
178
177
  Returns:
179
178
  Self: self
180
179
  """
181
180
  if isinstance(calculator, str) :
182
- init = self.get_potential_energy(calculator)
183
- if calculator == 'MMFF94' or calculator == 'MMFF':
184
- retcode = AllChem.MMFFOptimizeMolecule(self.rdmol, mmffVariant='MMFF94')
181
+
182
+ PE_start = self.potential_energy(calculator)
183
+
184
+ if calculator.lower() == 'xTB'.lower():
185
+ water = kwargs.get('water', None)
186
+ PE_final, self.rdmol = GFN2xTB(self.rdmol).optimize(water=water)
187
+
188
+ elif calculator.lower() == 'MMFF94'.lower() or calculator.lower() == 'MMFF'.lower():
189
+ retcode = Chem.rdForceFieldHelpers.MMFFOptimizeMolecule(self.rdmol,
190
+ mmffVariant='MMFF94',
191
+ maxIters=max_iter)
185
192
  # returns 0 if the optimization converged
186
- elif calculator == 'MMFF94s':
187
- retcode = AllChem.MMFFOptimizeMolecule(self.rdmol, mmffVariant='MMFF94s')
193
+ elif calculator.lower() == 'MMFF94s'.lower():
194
+ retcode = Chem.rdForceFieldHelpers.MMFFOptimizeMolecule(self.rdmol,
195
+ mmffVariant='MMFF94s',
196
+ maxIters=max_iter)
188
197
  # returns 0 if the optimization converged
189
- elif calculator == 'UFF':
190
- retcode = AllChem.UFFOptimizeMolecule(self.rdmol)
198
+ elif calculator.lower() == 'UFF'.lower():
199
+ retcode = Chem.rdForceFieldHelpers.UFFOptimizeMolecule(self.rdmol,
200
+ maxIters=max_iter)
191
201
  # returns 0 if the optimization converged
192
- final = self.get_potential_energy(calculator)
202
+
203
+ PE_final = self.potential_energy(calculator)
204
+
193
205
  self.props.update({
194
- 'E_tot_init(kcal/mol)': init , # energy before optimization
195
- 'E_tot(kcal/mol)': final, # energy after optimization
206
+ 'E_tot_init(kcal/mol)': PE_start , # energy before optimization
207
+ 'E_tot(kcal/mol)': PE_final, # energy after optimization
196
208
  'Converged' : retcode == 0, # True or False
197
209
  })
210
+
198
211
  return self
199
212
 
200
213
  else:
214
+ # assuming ASE calculator
201
215
  with io.StringIO() as logfile:
202
216
  ase_atoms = ase.Atoms(symbols=self.symbols(), positions=self.positions())
203
217
  ase_atoms.calc = calculator
@@ -209,9 +223,11 @@ class Conf:
209
223
  'E_tot(kcal/mol)': data[-1][0] * ev2kcalpermol, # energy after optimization
210
224
  'Converged' : data[-1][1] < fmax, # True or False
211
225
  })
226
+
212
227
  # update atomic coordinates
213
228
  return self.sync(ase_atoms.get_positions())
214
229
 
230
+
215
231
 
216
232
  ##################################################
217
233
  ### Endpoint methods
@@ -233,13 +249,13 @@ class Conf:
233
249
  idx2 = bond.GetEndAtomIdx()
234
250
  nuc1 = self.rdmol.GetAtomWithIdx(idx1).GetAtomicNum()
235
251
  nuc2 = self.rdmol.GetAtomWithIdx(idx2).GetAtomicNum()
236
- sum_radii = (radii[nuc1] + radii[nuc2])
252
+ sum_radii = (element(nuc1).vdw_radius + element(nuc2).vdw_radius) * pm2angstrom
237
253
  bond_length = rdMolTransforms.GetBondLength(self.rdmol.GetConformer(), idx1, idx2)
238
254
  if abs(bond_length - sum_radii) > tolerance:
239
255
  return False
240
256
 
241
257
  return True
242
-
258
+
243
259
 
244
260
  def positions(self) -> np.array:
245
261
  """Returns the coordinates.
@@ -298,14 +314,94 @@ class Conf:
298
314
  return np.sqrt(np.mean(b))
299
315
 
300
316
 
301
- def serialize(self, key:str='') -> dict:
317
+ def potential_energy(self, calculator: str | Callable = 'MMFF94', **kwargs) -> float:
318
+ """Get potential energy and set `E_tot(kcal/mol)` in the self.props.
319
+
320
+ Args:
321
+ calculator (str | Callable): MMFF94 (= MMFF), MMFF94s, UFF, or ASE calculator.
322
+ `MMFF94` or `MMFF` - Intended for general use, including organic molecules and proteins,
323
+ and primarily relies on data from quantum mechanical calculations.
324
+ It's often used in molecular dynamics simulations.
325
+ `MMFF94s` - A "static" variant of MMFF94, with adjusted parameters for out-of-plane
326
+ bending and dihedral torsions to favor planar geometries for specific nitrogen atoms.
327
+ This makes it better suited for geometry optimization studies where a static,
328
+ time-averaged structure is desired. The "s" stands for "static".
329
+ `UFF` - UFF refers to the "Universal Force Field," a force field model used for
330
+ molecular mechanics calculations. It's a tool for geometry optimization,
331
+ energy minimization, and exploring molecular conformations in 3D space.
332
+ UFF is often used to refine conformers generated by other methods,
333
+ such as random conformer generation, to produce more physically plausible
334
+ and stable structures.
335
+
336
+ Returns:
337
+ float | None: potential energy in kcal/mol or None.
338
+ """
339
+
340
+ if isinstance(calculator, str):
341
+ if calculator.lower() == 'xTB'.lower():
342
+ water = kwargs.get('water', None)
343
+ PE = GFN2xTB(self.rdmol).singlepoint(water=water)
344
+
345
+ elif calculator.lower() == 'MMFF94'.lower() or calculator.lower() == 'MMFF'.lower():
346
+ mp = Chem.rdForceFieldHelpers.MMFFGetMoleculeProperties(self.rdmol, mmffVariant='MMFF94')
347
+ ff = Chem.rdForceFieldHelpers.MMFFGetMoleculeForceField(self.rdmol, mp)
348
+ PE = ff.CalcEnergy()
349
+
350
+ elif calculator.lower() == 'MMFF94s'.lower():
351
+ mp = Chem.rdForceFieldHelpers.MMFFGetMoleculeProperties(self.rdmol, mmffVariant='MMFF94s')
352
+ ff = Chem.rdForceFieldHelpers.MMFFGetMoleculeForceField(self.rdmol, mp)
353
+ PE = ff.CalcEnergy()
354
+
355
+ elif calculator.lower() == 'UFF'.lower():
356
+ ff = Chem.rdForceFieldHelpers.UFFGetMoleculeForceField(self.rdmol)
357
+ PE = ff.CalcEnergy()
358
+
359
+ else:
360
+ raise ValueError("Unsupported calculator")
361
+
362
+ self.props.update({'E_tot(kcal/mol)': PE})
363
+
364
+ return PE
365
+
366
+ else:
367
+ try:
368
+ ase_atoms = ase.Atoms(symbols=self.symbols(), positions=self.positions())
369
+ ase_atoms.calc = calculator
370
+ PE = ase_atoms.get_potential_energy() # np.array
371
+ PE = ev2kcalpermol * float(PE[0]) # np.float64 to float
372
+ self.props.update({'E_tot(kcal/mol)': PE})
373
+
374
+ return PE
375
+
376
+ except:
377
+ raise RuntimeError("ASE calculator error")
378
+
379
+
380
+ def torsion_angle(self, i:int, j:int, k:int, l:int) -> float:
381
+ """Get dihedral angle (i-j-k-l) in degrees.
382
+
383
+ Args:
384
+ i (int): atom index
385
+ j (int): atom index
386
+ k (int): atom index
387
+ l (int): atom index
388
+
389
+ Returns:
390
+ float: dihedral angle in degrees.
391
+ """
392
+ degree = rdMolTransforms.GetDihedralDeg(self.rdmol.GetConformer(), i, j, k, l)
393
+
394
+ return degree
395
+
396
+
397
+ def dumps(self, key:str='') -> str:
302
398
  """Returns JSON dumps of the `props`.
303
399
 
304
400
  Args:
305
401
  key (str): a key for the `props` dictionary. Defaults to '' (all).
306
402
 
307
403
  Returns:
308
- dict: JSON dumps.
404
+ str: JSON dumps.
309
405
  """
310
406
  if key:
311
407
  return json.dumps({key:self.props[key]})
@@ -313,6 +409,59 @@ class Conf:
313
409
  return json.dumps(self.props)
314
410
 
315
411
 
412
+ def serialize(self, decimals:int=3) -> str:
413
+ """Serialize information necessary to rebuild.
414
+
415
+ Returns:
416
+ str: serialized string for json.loads()
417
+ """
418
+ serialized = json.dumps({
419
+ 'name' : self.name,
420
+ 'natoms': self.natoms,
421
+ 'props' : recursive_round(self.props, decimals),
422
+ 'molblock' : self.to_molblock(),
423
+ })
424
+
425
+ return serialized
426
+
427
+
428
+ def deserialize(self, serialized:str) -> Self:
429
+ """De-serialize information and rebuild.
430
+
431
+ Args:
432
+ serialized (str): _description_
433
+
434
+ Returns:
435
+ Self: _description_
436
+ """
437
+ data = json.loads(serialized)
438
+
439
+ self.name = data['name']
440
+ self.natoms = data['natoms']
441
+ self.props = data['props']
442
+ self.rdmol = Chem.MolFromMolBlock(data['molblock'], sanitize=False, removeHs=False)
443
+
444
+ return self
445
+
446
+
447
+ def to_molblock(self) -> str:
448
+ """Returns MolBlock"""
449
+ return Chem.MolToMolBlock(self.rdmol)
450
+
451
+
452
+ def to_xyz(self) -> str:
453
+ """Returns XYZ formatted strings.
454
+
455
+ Returns:
456
+ str: XYZ formatted strings.
457
+ """
458
+ lines = [f'{self.natoms}', ' ']
459
+ for e, (x, y, z) in zip(self.symbols(), self.positions()):
460
+ lines.append(f'{e:5} {x:23.14f} {y:23.14f} {z:23.14f}')
461
+
462
+ return '\n'.join(lines)
463
+
464
+
316
465
  def to_sdf(self, props:bool=True) -> str:
317
466
  """Returns the SDF-formatted strings.
318
467
 
@@ -332,43 +481,85 @@ class Conf:
332
481
  f.write(rdmol)
333
482
  return in_memory.getvalue()
334
483
 
484
+
485
+ def to_png(self,
486
+ width: int = 300,
487
+ height: int = 300,
488
+ legend: str = '',
489
+ atom_index: bool = False,
490
+ highlight_atoms: list[int] | None = None,
491
+ highlight_bonds: list[int] | None = None,
492
+ redraw: bool = False,
493
+ coordgen: bool = False,
494
+ trim: bool = True) -> Image.Image:
495
+ """Draw 2D molecule in PNG format.
496
+
497
+ Args:
498
+ width (int, optional): width. Defaults to 300.
499
+ height (int, optional): height. Defaults to 300.
500
+ legend (str, optional): legend. Defaults to ''.
501
+ atom_index (bool, optional): whether to show atom index. Defaults to False.
502
+ highlight_atoms (list[int] | None, optional): atom(s) to highlight. Defaults to None.
503
+ highlight_bonds (list[int] | None, optional): bond(s) to highlight. Defaults to None.
504
+ redraw (bool, optional): whether to redraw. Defaults to False.
505
+ coordgen (bool, optional): whether to use coordgen. Defaults to False.
506
+ trim (bool, optional): whether to trim white margins. Default to True.
507
+
508
+ Returns:
509
+ Image.Image: output PIL Image object.
510
+ """
511
+
512
+ return render_png(self.rdmol,
513
+ width = width,
514
+ height = height,
515
+ legend = legend,
516
+ atom_index = atom_index,
517
+ highlight_atoms = highlight_atoms,
518
+ highlight_bonds = highlight_bonds,
519
+ redraw = redraw,
520
+ coordgen = coordgen,
521
+ trim = trim)
522
+
335
523
 
336
- def to_svg(self,
337
- width:int=400,
338
- height:int=400,
339
- legend:Optional[str]=None,
340
- atom_index:bool=False,
341
- highlight:Optional[List[int]]=None) -> str:
342
- """Returns 2D SVG depiction of the molecule.
524
+ def to_svg(self,
525
+ width: int = 300,
526
+ height: int = 300,
527
+ legend: str = '',
528
+ atom_index: bool = False,
529
+ highlight_atoms: list[int] | None = None,
530
+ highlight_bonds: list[int] | None = None,
531
+ redraw: bool = False,
532
+ coordgen: bool = False,
533
+ optimize: bool = True) -> str:
534
+ """Draw 2D molecule in SVG format.
343
535
 
344
536
  Examples:
537
+ For Jupyternotebook, wrap the output with SVG:
538
+
345
539
  >>> from IPython.display import SVG
346
- >>> SVG(libr[0].confs[0].to_svg(atom_index=True))
540
+ >>> SVG(libr[0].to_svg())
347
541
 
348
542
  Args:
349
- width (int): width (default:400)
350
- height (int): height (default:400)
351
- legend (str, optional): title or Mol.name if not given
352
- atom_index (bool): True/False whether to display atom index
353
- highlight (list): list of atom indices to highlight
354
-
543
+ width (int, optional): width. Defaults to 300.
544
+ height (int, optional): height. Defaults to 300.
545
+ legend (str, optional): legend. Defaults to ''.
546
+ atom_index (bool, optional): whether to show atom index. Defaults to False.
547
+ highlight_atoms (list[int] | None, optional): atom(s) to highlight. Defaults to None.
548
+ highlight_bonds (list[int] | None, optional): bond(s) to highlight. Defaults to None.
549
+ redraw (bool, optional): whether to redraw. Defaults to False.
550
+ coordgen (bool, optional): whether to use coordgen. Defaults to False.
551
+ optimize (bool, optional): whether to optimize SVG string. Defaults to True.
552
+
355
553
  Returns:
356
- str: SVG text
554
+ str: SVG string
357
555
  """
358
- rdmol_3d = Chem.Mol(self.rdmol) # a copy of self.rdmol (3D, with hydrogens)
359
- AllChem.Compute2DCoords(rdmol_3d) # 2D depiction
360
- for atom in rdmol_3d.GetAtoms():
361
- for key in atom.GetPropsAsDict():
362
- atom.ClearProp(key)
363
- drawer = rdMolDraw2D.MolDraw2DSVG(width, height)
364
- if not legend:
365
- legend = self.name
366
- if atom_index:
367
- for atom in rdmol_3d.GetAtoms():
368
- atom.SetProp("atomLabel", str(atom.GetIdx()))
369
- if highlight:
370
- drawer.DrawMolecule(rdmol_3d, legend=legend, highlightAtoms=highlight)
371
- else:
372
- drawer.DrawMolecule(rdmol_3d, legend=legend)
373
- drawer.FinishDrawing()
374
- return drawer.GetDrawingText()
556
+ return render_svg(self.rdmol,
557
+ width = width,
558
+ height = height,
559
+ legend = legend,
560
+ atom_index = atom_index,
561
+ highlight_atoms = highlight_atoms,
562
+ highlight_bonds = highlight_bonds,
563
+ redraw = redraw,
564
+ coordgen = coordgen,
565
+ optimize = optimize)