rdworks 0.25.8__py3-none-any.whl → 0.36.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.8'
1
+ __version__ = '0.36.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
@@ -2,45 +2,71 @@ import io
2
2
  import copy
3
3
  import json
4
4
  import numpy as np
5
-
6
5
  import ase
7
- from ase.optimize import FIRE
8
6
 
9
7
  from collections.abc import Callable
8
+ from typing import Self
9
+
10
+ from mendeleev import element
11
+ from ase.optimize import FIRE
10
12
 
11
13
  from rdkit import Chem
12
- from rdkit.Chem import rdMolTransforms, AllChem, rdMolAlign
14
+ from rdkit.Chem import rdMolTransforms, AllChem, rdMolAlign, rdmolops
13
15
  from rdkit.Chem.Draw import rdMolDraw2D
16
+ from PIL import Image
14
17
 
15
- from typing import List, Optional, Union, Self
16
-
17
- from rdworks.units import ev2kcalpermol
18
- from rdworks.units import radii
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
19
22
 
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
42
+ self.charge = 0 # molecular charge
43
+ self.spin = 1 # molecular spin multiplicity for ASE
37
44
  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')
45
+
46
+ if molecule is None:
47
+ return
48
+
49
+ if isinstance(molecule, str): # 3-D MolBLock string
50
+ try:
51
+ self.rdmol = Chem.MolFromMolBlock(molecule)
52
+ except:
53
+ ValueError(f'Conf() Error: invalid MolBlock string')
54
+
55
+ elif isinstance(molecule, Chem.Mol): # 3-D
56
+ try:
57
+ self.rdmol = molecule
58
+ except:
59
+ ValueError(f'Conf() Error: invalid Chem.Mol object')
60
+
61
+ num_atoms = self.rdmol.GetNumAtoms()
62
+ tot_atoms = self.rdmol.GetNumAtoms(onlyExplicit=False)
63
+ assert num_atoms == tot_atoms, "Conf() Error: missing hydrogens"
64
+ self.natoms = num_atoms
65
+ self.charge = rdmolops.GetFormalCharge(self.rdmol)
66
+ self.props.update({'atoms': num_atoms})
67
+
68
+ assert self.rdmol.GetConformer().Is3D(), "Conf() Error: not 3D"
69
+
44
70
 
45
71
 
46
72
  def __str__(self) -> str:
@@ -50,12 +76,7 @@ class Conf:
50
76
  str: string representation.
51
77
  """
52
78
  return f"<rdworks.Conf({self.rdmol} name={self.name} atoms={self.natoms})>"
53
-
54
-
55
- ##################################################
56
- ### Cascading methods
57
- ##################################################
58
-
79
+
59
80
 
60
81
  def copy(self) -> Self:
61
82
  """Returns a copy of self.
@@ -85,7 +106,7 @@ class Conf:
85
106
  return self
86
107
 
87
108
 
88
- def sync(self, coord:Union[np.ndarray, list]) -> Self:
109
+ def sync(self, coord: np.ndarray | list) -> Self:
89
110
  """Synchronize the conformer coordinates with the provided `coord`.
90
111
 
91
112
  Args:
@@ -107,56 +128,31 @@ class Conf:
107
128
  return self
108
129
 
109
130
 
110
- def get_potential_energy(self, calculator: str | Callable = 'MMFF94') -> float:
111
- """Get potential energy in kcal/mol.
131
+ def set_torsion(self, i:int, j:int, k:int, l:int, degree:float) -> Self:
132
+ """Set dihedral angle (i-j-k-l) in degrees.
112
133
 
113
134
  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.
135
+ i (int): atom index
136
+ j (int): atom index
137
+ k (int): atom index
138
+ l (int): atom index
139
+ degree (float): dihedral angle in degrees
128
140
 
129
141
  Returns:
130
- float: potential energy in kcal/mol.
142
+ Self: modified Conf object
131
143
  """
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
144
+ rdMolTransforms.SetDihedralDeg(self.rdmol.GetConformer(), i, j, k, l, degree)
145
+
146
+ return self
156
147
 
157
148
 
158
- def optimize(self, calculator: str | Callable = 'MMFF94', fmax:float=0.05) -> Self:
159
- """Optimize conformation using a callable.
149
+
150
+ def optimize(self,
151
+ calculator: str | Callable = 'MMFF94',
152
+ fmax:float=0.05,
153
+ max_iter:int=1000,
154
+ **kwargs) -> Self:
155
+ """Optimize 3D geometry.
160
156
 
161
157
  Args:
162
158
  calculator (str | Callable): MMFF94 (= MMFF), MMFF94s, UFF, or ASE calculator.
@@ -173,33 +169,56 @@ class Conf:
173
169
  UFF is often used to refine conformers generated by other methods,
174
170
  such as random conformer generation, to produce more physically plausible
175
171
  and stable structures.
176
- fmax (float, optional): fmax for the calculator. Defaults to 0.05.
172
+ fmax (float, optional): fmax for the calculator convergence. Defaults to 0.05.
173
+ max_iter (int, optional): max iterations for the calculator. Defaults to 1000.
174
+
175
+ Args for xTB:
176
+ water (str, optional): water solvation model (choose 'gbsa' or 'alpb')
177
+ alpb: ALPB solvation model (Analytical Linearized Poisson-Boltzmann).
178
+ gbsa: generalized Born (GB) model with Surface Area contributions.
177
179
 
178
180
  Returns:
179
181
  Self: self
180
182
  """
181
183
  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')
184
+
185
+ PE_start = self.potential_energy(calculator)
186
+
187
+ if calculator.lower() == 'xTB'.lower():
188
+ water = kwargs.get('water', None)
189
+ PE_final, self.rdmol = GFN2xTB(self.rdmol).optimize(water=water)
190
+
191
+ elif calculator.lower() == 'MMFF94'.lower() or calculator.lower() == 'MMFF'.lower():
192
+ retcode = Chem.rdForceFieldHelpers.MMFFOptimizeMolecule(self.rdmol,
193
+ mmffVariant='MMFF94',
194
+ maxIters=max_iter)
185
195
  # returns 0 if the optimization converged
186
- elif calculator == 'MMFF94s':
187
- retcode = AllChem.MMFFOptimizeMolecule(self.rdmol, mmffVariant='MMFF94s')
196
+ elif calculator.lower() == 'MMFF94s'.lower():
197
+ retcode = Chem.rdForceFieldHelpers.MMFFOptimizeMolecule(self.rdmol,
198
+ mmffVariant='MMFF94s',
199
+ maxIters=max_iter)
188
200
  # returns 0 if the optimization converged
189
- elif calculator == 'UFF':
190
- retcode = AllChem.UFFOptimizeMolecule(self.rdmol)
201
+ elif calculator.lower() == 'UFF'.lower():
202
+ retcode = Chem.rdForceFieldHelpers.UFFOptimizeMolecule(self.rdmol,
203
+ maxIters=max_iter)
191
204
  # returns 0 if the optimization converged
192
- final = self.get_potential_energy(calculator)
205
+
206
+ PE_final = self.potential_energy(calculator)
207
+
193
208
  self.props.update({
194
- 'E_tot_init(kcal/mol)': init , # energy before optimization
195
- 'E_tot(kcal/mol)': final, # energy after optimization
209
+ 'E_tot_init(kcal/mol)': PE_start , # energy before optimization
210
+ 'E_tot(kcal/mol)': PE_final, # energy after optimization
196
211
  'Converged' : retcode == 0, # True or False
197
212
  })
213
+
198
214
  return self
199
215
 
200
216
  else:
217
+ # assuming ASE calculator
201
218
  with io.StringIO() as logfile:
202
219
  ase_atoms = ase.Atoms(symbols=self.symbols(), positions=self.positions())
220
+ ase_atoms.info['charge'] = self.charge
221
+ ase_atoms.info['spin'] = self.spin
203
222
  ase_atoms.calc = calculator
204
223
  FIRE(ase_atoms, logfile=logfile).run(fmax=fmax)
205
224
  lines = [l.strip().split()[1:] for l in logfile.getvalue().split('\n') if l.startswith('FIRE')]
@@ -209,9 +228,11 @@ class Conf:
209
228
  'E_tot(kcal/mol)': data[-1][0] * ev2kcalpermol, # energy after optimization
210
229
  'Converged' : data[-1][1] < fmax, # True or False
211
230
  })
231
+
212
232
  # update atomic coordinates
213
233
  return self.sync(ase_atoms.get_positions())
214
234
 
235
+
215
236
 
216
237
  ##################################################
217
238
  ### Endpoint methods
@@ -233,13 +254,13 @@ class Conf:
233
254
  idx2 = bond.GetEndAtomIdx()
234
255
  nuc1 = self.rdmol.GetAtomWithIdx(idx1).GetAtomicNum()
235
256
  nuc2 = self.rdmol.GetAtomWithIdx(idx2).GetAtomicNum()
236
- sum_radii = (radii[nuc1] + radii[nuc2])
257
+ sum_radii = (element(nuc1).vdw_radius + element(nuc2).vdw_radius) * pm2angstrom
237
258
  bond_length = rdMolTransforms.GetBondLength(self.rdmol.GetConformer(), idx1, idx2)
238
259
  if abs(bond_length - sum_radii) > tolerance:
239
260
  return False
240
261
 
241
262
  return True
242
-
263
+
243
264
 
244
265
  def positions(self) -> np.array:
245
266
  """Returns the coordinates.
@@ -298,14 +319,99 @@ class Conf:
298
319
  return np.sqrt(np.mean(b))
299
320
 
300
321
 
301
- def serialize(self, key:str='') -> dict:
322
+ def potential_energy(self, calculator: str | Callable = 'MMFF94', **kwargs) -> float:
323
+ """Get potential energy and set `E_tot(kcal/mol)` in the self.props.
324
+
325
+ Args:
326
+ calculator (str | Callable): MMFF94 (= MMFF), MMFF94s, UFF, or ASE calculator.
327
+ `MMFF94` or `MMFF` - Intended for general use, including organic molecules and proteins,
328
+ and primarily relies on data from quantum mechanical calculations.
329
+ It's often used in molecular dynamics simulations.
330
+ `MMFF94s` - A "static" variant of MMFF94, with adjusted parameters for out-of-plane
331
+ bending and dihedral torsions to favor planar geometries for specific nitrogen atoms.
332
+ This makes it better suited for geometry optimization studies where a static,
333
+ time-averaged structure is desired. The "s" stands for "static".
334
+ `UFF` - UFF refers to the "Universal Force Field," a force field model used for
335
+ molecular mechanics calculations. It's a tool for geometry optimization,
336
+ energy minimization, and exploring molecular conformations in 3D space.
337
+ UFF is often used to refine conformers generated by other methods,
338
+ such as random conformer generation, to produce more physically plausible
339
+ and stable structures.
340
+
341
+ Returns:
342
+ float | None: potential energy in kcal/mol or None.
343
+ """
344
+
345
+ if isinstance(calculator, str):
346
+ if calculator.lower() == 'xTB'.lower():
347
+ water = kwargs.get('water', None)
348
+ PE = GFN2xTB(self.rdmol).singlepoint(water=water)
349
+
350
+ elif calculator.lower() == 'MMFF94'.lower() or calculator.lower() == 'MMFF'.lower():
351
+ mp = Chem.rdForceFieldHelpers.MMFFGetMoleculeProperties(self.rdmol, mmffVariant='MMFF94')
352
+ ff = Chem.rdForceFieldHelpers.MMFFGetMoleculeForceField(self.rdmol, mp)
353
+ PE = ff.CalcEnergy()
354
+
355
+ elif calculator.lower() == 'MMFF94s'.lower():
356
+ mp = Chem.rdForceFieldHelpers.MMFFGetMoleculeProperties(self.rdmol, mmffVariant='MMFF94s')
357
+ ff = Chem.rdForceFieldHelpers.MMFFGetMoleculeForceField(self.rdmol, mp)
358
+ PE = ff.CalcEnergy()
359
+
360
+ elif calculator.lower() == 'UFF'.lower():
361
+ ff = Chem.rdForceFieldHelpers.UFFGetMoleculeForceField(self.rdmol)
362
+ PE = ff.CalcEnergy()
363
+
364
+ else:
365
+ raise ValueError("Unsupported calculator")
366
+
367
+ self.props.update({'E_tot(kcal/mol)': PE})
368
+
369
+ return PE
370
+
371
+ else:
372
+ try:
373
+ ase_atoms = ase.Atoms(symbols=self.symbols(), positions=self.positions())
374
+ ase_atoms.info['charge'] = self.charge
375
+ ase_atoms.info['spin'] = self.spin
376
+ ase_atoms.calc = calculator
377
+ PE = ase_atoms.get_potential_energy() # np.array
378
+ if isinstance(PE, float):
379
+ PE = ev2kcalpermol * PE
380
+ elif isinstance(PE, np.ndarray | list):
381
+ PE = ev2kcalpermol * float(PE[0]) # np.float64 to float
382
+ self.props.update({'E_tot(kcal/mol)': PE})
383
+
384
+ return PE
385
+
386
+ except:
387
+ raise RuntimeError("ASE calculator error")
388
+
389
+
390
+ def torsion_angle(self, i:int, j:int, k:int, l:int) -> float:
391
+ """Get dihedral angle (i-j-k-l) in degrees.
392
+
393
+ Args:
394
+ i (int): atom index
395
+ j (int): atom index
396
+ k (int): atom index
397
+ l (int): atom index
398
+
399
+ Returns:
400
+ float: dihedral angle in degrees.
401
+ """
402
+ degree = rdMolTransforms.GetDihedralDeg(self.rdmol.GetConformer(), i, j, k, l)
403
+
404
+ return degree
405
+
406
+
407
+ def dumps(self, key:str='') -> str:
302
408
  """Returns JSON dumps of the `props`.
303
409
 
304
410
  Args:
305
411
  key (str): a key for the `props` dictionary. Defaults to '' (all).
306
412
 
307
413
  Returns:
308
- dict: JSON dumps.
414
+ str: JSON dumps.
309
415
  """
310
416
  if key:
311
417
  return json.dumps({key:self.props[key]})
@@ -313,6 +419,59 @@ class Conf:
313
419
  return json.dumps(self.props)
314
420
 
315
421
 
422
+ def serialize(self, decimals:int=3) -> str:
423
+ """Serialize information necessary to rebuild.
424
+
425
+ Returns:
426
+ str: serialized string for json.loads()
427
+ """
428
+ serialized = json.dumps({
429
+ 'name' : self.name,
430
+ 'natoms': self.natoms,
431
+ 'props' : recursive_round(self.props, decimals),
432
+ 'molblock' : self.to_molblock(),
433
+ })
434
+
435
+ return serialized
436
+
437
+
438
+ def deserialize(self, serialized:str) -> Self:
439
+ """De-serialize information and rebuild.
440
+
441
+ Args:
442
+ serialized (str): _description_
443
+
444
+ Returns:
445
+ Self: _description_
446
+ """
447
+ data = json.loads(serialized)
448
+
449
+ self.name = data['name']
450
+ self.natoms = data['natoms']
451
+ self.props = data['props']
452
+ self.rdmol = Chem.MolFromMolBlock(data['molblock'], sanitize=False, removeHs=False)
453
+
454
+ return self
455
+
456
+
457
+ def to_molblock(self) -> str:
458
+ """Returns MolBlock"""
459
+ return Chem.MolToMolBlock(self.rdmol)
460
+
461
+
462
+ def to_xyz(self) -> str:
463
+ """Returns XYZ formatted strings.
464
+
465
+ Returns:
466
+ str: XYZ formatted strings.
467
+ """
468
+ lines = [f'{self.natoms}', ' ']
469
+ for e, (x, y, z) in zip(self.symbols(), self.positions()):
470
+ lines.append(f'{e:5} {x:23.14f} {y:23.14f} {z:23.14f}')
471
+
472
+ return '\n'.join(lines)
473
+
474
+
316
475
  def to_sdf(self, props:bool=True) -> str:
317
476
  """Returns the SDF-formatted strings.
318
477
 
@@ -332,43 +491,85 @@ class Conf:
332
491
  f.write(rdmol)
333
492
  return in_memory.getvalue()
334
493
 
494
+
495
+ def to_png(self,
496
+ width: int = 300,
497
+ height: int = 300,
498
+ legend: str = '',
499
+ atom_index: bool = False,
500
+ highlight_atoms: list[int] | None = None,
501
+ highlight_bonds: list[int] | None = None,
502
+ redraw: bool = False,
503
+ coordgen: bool = False,
504
+ trim: bool = True) -> Image.Image:
505
+ """Draw 2D molecule in PNG format.
506
+
507
+ Args:
508
+ width (int, optional): width. Defaults to 300.
509
+ height (int, optional): height. Defaults to 300.
510
+ legend (str, optional): legend. Defaults to ''.
511
+ atom_index (bool, optional): whether to show atom index. Defaults to False.
512
+ highlight_atoms (list[int] | None, optional): atom(s) to highlight. Defaults to None.
513
+ highlight_bonds (list[int] | None, optional): bond(s) to highlight. Defaults to None.
514
+ redraw (bool, optional): whether to redraw. Defaults to False.
515
+ coordgen (bool, optional): whether to use coordgen. Defaults to False.
516
+ trim (bool, optional): whether to trim white margins. Default to True.
517
+
518
+ Returns:
519
+ Image.Image: output PIL Image object.
520
+ """
521
+
522
+ return render_png(self.rdmol,
523
+ width = width,
524
+ height = height,
525
+ legend = legend,
526
+ atom_index = atom_index,
527
+ highlight_atoms = highlight_atoms,
528
+ highlight_bonds = highlight_bonds,
529
+ redraw = redraw,
530
+ coordgen = coordgen,
531
+ trim = trim)
532
+
335
533
 
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.
534
+ def to_svg(self,
535
+ width: int = 300,
536
+ height: int = 300,
537
+ legend: str = '',
538
+ atom_index: bool = False,
539
+ highlight_atoms: list[int] | None = None,
540
+ highlight_bonds: list[int] | None = None,
541
+ redraw: bool = False,
542
+ coordgen: bool = False,
543
+ optimize: bool = True) -> str:
544
+ """Draw 2D molecule in SVG format.
343
545
 
344
546
  Examples:
547
+ For Jupyternotebook, wrap the output with SVG:
548
+
345
549
  >>> from IPython.display import SVG
346
- >>> SVG(libr[0].confs[0].to_svg(atom_index=True))
550
+ >>> SVG(libr[0].to_svg())
347
551
 
348
552
  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
-
553
+ width (int, optional): width. Defaults to 300.
554
+ height (int, optional): height. Defaults to 300.
555
+ legend (str, optional): legend. Defaults to ''.
556
+ atom_index (bool, optional): whether to show atom index. Defaults to False.
557
+ highlight_atoms (list[int] | None, optional): atom(s) to highlight. Defaults to None.
558
+ highlight_bonds (list[int] | None, optional): bond(s) to highlight. Defaults to None.
559
+ redraw (bool, optional): whether to redraw. Defaults to False.
560
+ coordgen (bool, optional): whether to use coordgen. Defaults to False.
561
+ optimize (bool, optional): whether to optimize SVG string. Defaults to True.
562
+
355
563
  Returns:
356
- str: SVG text
564
+ str: SVG string
357
565
  """
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()
566
+ return render_svg(self.rdmol,
567
+ width = width,
568
+ height = height,
569
+ legend = legend,
570
+ atom_index = atom_index,
571
+ highlight_atoms = highlight_atoms,
572
+ highlight_bonds = highlight_bonds,
573
+ redraw = redraw,
574
+ coordgen = coordgen,
575
+ optimize = optimize)