Feature request: Save 2D structure

Ran into the need of saving my molecules in 2D (ChemDraw-like) format. I was wondering if Avogadro2 can do this?

Openbabel could «flatten» 3D structures for you, too. See, for example

obabel -:"CCOCC"  --gen3d -h -O ether_3d.xyz

for a structure which is 3D; however you can use this (here: intermediate) result for a subsequent

obabel ether_3d.xyz --gen2d -O ether_2d.xyz

with all coordinates in z equal to zero.

(The export of flat/2D structures into .mol/.sdf is not unique to ChemDraw. The absolute configuration equally can be described by e.g., a CFG=2 in the atom block.)

1 Like

Yeah, you can also export to .mol or .sdf or CDXML to read in ChemDraw.

But my suggestion is to Copy As => SMILES and then paste that into ChemDraw or another program to make sure it depicts in a “standard” depiction.

I’ll also be adding in an RDKit package, which would include a “export to SVG” using RDKit’s excellent depiction code.

1 Like

Thank you both for your suggestions, they prompted me to investigate openbabel’s svg saving capabilities and with a simple piece of code I can incorporate it into my workflow.

import pybel

def save_SVG(your_xyz_string, directory = './'):
    mol = pybel.readstring("xyz", your_xyz_string)
    SVG_options = {'d' : False, # Supress printing of name
                   'u' : False} # Color in black
    #mol.removeh()              # Activate if you wish to remove hydrogens
    with open('{}{}.svg'.format(directory, "your_file"), "w") as SVG_file:
        SVG_file.write(mol.write(format = 'svg', opt = SVG_options))

This way I can bypass ChemDraw altogether :slight_smile:

2 Likes

@Juanes If your workflow were about automated documentation/report generation with openbabel’s .svg, then maybe chemobabel is interesting for you.

1 Like

My suggestion honestly, is to use RDKit, e.g.

mol = Chem.MolFromXYZBlock(your_xyz_string)
rdDetermineBonds.DetermineConnectivity(mol)
rdDetermineBonds.DetermineBondOrders(mol)
# and finally the stereochemistry
Chem.AssignStereochemistryFrom3D(mol)

d2d = rdMolDraw2D.MolDraw2DSVG(256,256,-1,-1,True)
opts = d2d.drawOptions()
# change options if you want
d2d.DrawMolecule(mol)
d2d.FinishDrawing()
svg = d2d.GetDrawingText()

There are a few other open source packages for 2D depiction, but IMHO RDKit’s is the best and most polished.

https://greglandrum.github.io/rdkit-blog/index.html#category=drawing

1 Like

I managed to do exactly what I needed with RDKit. Read in a large number of .xyz files, set certain atoms as arbitrary functional groups, and save them in 2D format in a standard format. Here’s the code in case anyone finds it useful:

from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit.Chem import rdDetermineBonds

from rdkit.Chem.Draw import rdMolDraw2D

def save_svg(xyz_string, directory = './', substitutions = []):
    # Create RDKit Mol object
    raw_mol = Chem.MolFromXYZBlock(xyz_string)
    mol = Chem.Mol(raw_mol)

    rdDetermineBonds.DetermineBonds(mol, charge = 0)

    # Change atoms in SUBSTITUTIONS to R_1, R_2, ...
    idx_label = 1
    for s in substitutions:
        s_atom = mol.GetAtomWithIdx(s - 1)
        s_atom.SetAtomicNum(0)
        s_atom.SetProp('atomLabel', r'R<sub>{}</sub>'.format(idx_label))
        idx_label += 1

    AllChem.Compute2DCoords(mol)

    # Optional: remove hydrogens for cleaner images
    mol = Chem.RemoveHs(mol)

    # Generate the SVG string. (-1, -1) sets up an image of dynamic size. 
    # This way the size of the molecules is consistent
    drawer = rdMolDraw2D.MolDraw2DSVG(-1, -1)

    # Draw the molecule in ACS 1996 format. 
    rdMolDraw2D.DrawMoleculeACS1996(drawer, mol)
    drawer.FinishDrawing()

    # Generate data and save
    svg_data = drawer.GetDrawingText()

    # Optional: Make background transparent by removing the white rectangle definition
    svg_data = svg_data.replace('fill:#FFFFFF', 'fill:none')

    with open('{}{}.svg'.format(directory, xyz_string.split('\n')[1].split('.')[0]), "w") as SVG_file:
        SVG_file.write(svg_data)

coronene_xyz = '''36
coronene. Charge: 0 Multiplicity: 1
  C  -2.814239    -2.433172     0.000525
  C  -3.513376    -1.223602     0.002701
  C  -0.697550    -3.653345    -0.002618
  C   0.698759    -3.652519    -0.001820
  C  -3.513586     1.223537     0.000527
  C  -2.814145     2.433955    -0.002756
  C   2.812425    -2.431717    -0.000080
  C   3.510775    -1.222823    -0.000368
  C  -0.693728     3.655328    -0.002757
  C   3.513380     1.217812    -0.000093
  C   0.703619     3.653094     0.000099
  C   2.817285     2.428028     0.001718
  C  -1.413713    -2.448903    -0.000374
  C  -2.828768     0.000371     0.001923
  C   1.411873    -2.447319    -0.000016
  C  -1.412347     2.451259    -0.001785
  C   2.825145    -0.001786     0.000107
  C   1.415885     2.447462     0.001370
  C  -0.709153    -1.223993     0.000860
  C  -1.416107     0.001258     0.001239
  C   0.704852    -1.223993     0.000716
  C  -0.708427     1.226774     0.000442
  C   1.412492     0.000086     0.000957
  C   0.706772     1.225249     0.000733
  H  -3.375091    -3.365350    -0.000588
  H  -4.600904    -1.247566     0.003981
  H  -1.221003    -4.606899    -0.005194
  H   1.225395    -4.604113    -0.003085
  H  -4.601016     1.245985     0.001760
  H  -3.376485     3.364890    -0.006057
  H   3.374359    -3.362811    -0.000380
  H   4.598225    -1.244815    -0.001362
  H  -1.215405     4.609787    -0.005562
  H   4.600919     1.236595    -0.002001
  H   1.231371     4.604264     0.000723
  H   3.381985     3.357602     0.002887'''
coronene_substitutions = [25, 26]

save_svg(coronene_xyz, './', coronene_substitutions)
save_svg(DPTTA_xyz, './', DPTTA_substitutions)

coronene

@Thomas, yes! I am indeed trying to document the many molecules I am generating in my project automatically. chemobabel looks very interesting and useful and will use it, if not for this workflow / project, at some point certainly.

Once again, thank you both for your suggestions.

2 Likes