1.1.4.4. 2D Structure Generation

The script gen_2d_layout.py reads molecules from a given input file, computes a 2D layout and writes the result to the specified output file.

Synopsis

python gen_2d_layout.py [-h] -i <file> -o <file> [-d] [-c] [-q]

Mandatory options

-i <file>

Molecule input file

-o <file>

Laid out molecule output file

Other options

-h, --help

Show help message and exit

-d

Remove ordinary explicit hydrogens (default: false)

-c

Saturate free valences with explicit hydrogens (default: false)

-q

Disable progress output (default: false)

Code

  1import sys
  2import argparse
  3
  4import CDPL.Chem as Chem
  5import CDPL.Math as Math
  6import CDPL.Util as Util
  7
  8
  9# computes a 2D structure layout of the argument molecule using the provided
 10# Chem.Atom2DCoordinatesCalculator and Chem.BondStereoFlagCalculator instances
 11def gen2DLayout(mol: Chem.Molecule, coords_calc: Chem.Atom2DCoordinatesCalculator, bs_flags_calc: Chem.BondStereoFlagCalculator, args: argparse.Namespace) -> None:
 12    # calculate several properties required for 2D structure layout
 13    Chem.calcBasicProperties(mol, False) 
 14    Chem.calcCIPPriorities(mol, False)
 15    Chem.perceiveAtomStereoCenters(mol, False, True)
 16    Chem.perceiveBondStereoCenters(mol, False, True)
 17    Chem.calcAtomStereoDescriptors(mol, False)
 18    Chem.calcBondStereoDescriptors(mol, False)
 19
 20    changes = False
 21    
 22    if args.rem_h:   # remove ordinary (with standard form. charge, isotope, connectivity) hydrogens, if desired
 23        changes = Chem.makeOrdinaryHydrogenDeplete(mol, Chem.AtomPropertyFlag.ISOTOPE | Chem.AtomPropertyFlag.FORMAL_CHARGE | Chem.AtomPropertyFlag.EXPLICIT_BOND_COUNT, True)
 24    elif args.add_h: # make hydrogen complete, if desired
 25        changes = Chem.makeHydrogenComplete(mol)
 26
 27    if changes:      # if expl. hydrogen count has changed -> recompute dependent properties
 28        Chem.perceiveComponents(mol, True)
 29        Chem.calcTopologicalDistanceMatrix(mol, True)
 30        Chem.calcAtomStereoDescriptors(mol, True, 0, False)
 31        Chem.calcBondStereoDescriptors(mol, True, 0, False)
 32    else:
 33        Chem.calcTopologicalDistanceMatrix(mol, False)
 34
 35    coords = Math.Vector2DArray() # will store the computed 2D coordinates
 36    bs_flags = Util.UIArray()     # will store the computed bond stereo flags (up, down) specifying atom (and bond) configurations
 37
 38    # calculate 2D coordinates
 39    coords_calc.calculate(mol, coords)
 40
 41    # store calculated 2D coordinates as corr. atom properties
 42    Chem.set2DCoordinates(mol, coords)
 43
 44    # calculate bond stereo flags for the given 2D coordinates
 45    bs_flags_calc.calculate(mol, bs_flags)
 46
 47    # store bond stereo flags as corr. bond properties 
 48    for i in range(mol.numBonds):
 49        Chem.set2DStereoFlag(mol.getBond(i), bs_flags[i])
 50        
 51def parseArgs() -> argparse.Namespace:
 52    parser = argparse.ArgumentParser(description='Performs a 2D structure layout for the given input molecules.')
 53
 54    parser.add_argument('-i',
 55                        dest='in_file',
 56                        required=True,
 57                        metavar='<file>',
 58                        help='Molecule input file')
 59    parser.add_argument('-o',
 60                        dest='out_file',
 61                        required=True,
 62                        metavar='<file>',
 63                        help='Laid out molecule output file')
 64    parser.add_argument('-d',
 65                        dest='rem_h',
 66                        required=False,
 67                        action='store_true',
 68                        default=False,
 69                        help='Remove ordinary explicit hydrogens (default: false)')
 70    parser.add_argument('-c',
 71                        dest='add_h',
 72                        required=False,
 73                        action='store_true',
 74                        default=False,
 75                        help='Saturate free valences with explicit hydrogens (default: false)')
 76    parser.add_argument('-q',
 77                        dest='quiet',
 78                        required=False,
 79                        action='store_true',
 80                        default=False,
 81                        help='Disable progress output (default: false)')
 82      
 83    return parser.parse_args()
 84
 85def main() -> None:
 86    args = parseArgs()
 87    
 88    # create reader for input molecules (format specified by file extension)
 89    reader = Chem.MoleculeReader(args.in_file) 
 90
 91    # create writer for the generated 3D structures (format specified by file extension)
 92    writer = Chem.MolecularGraphWriter(args.out_file) 
 93 
 94    # create and initialize an instance of the class Chem.Atom2DCoordinatesCalculator which will
 95    # perform the 2D coordinates calculation
 96    coords_calc = Chem.Atom2DCoordinatesCalculator()
 97
 98    # create and initialize an instance of the class Chem.BondStereoFlagsCalculator which will
 99    # perform the calculation 2D bond stereo flags (= up, down, ...)
100    bs_flags_calc = Chem.BondStereoFlagCalculator()
101    
102    # create an instance of the default implementation of the Chem.Molecule interface
103    mol = Chem.BasicMolecule()
104    i = 1
105    
106    # read and process molecules one after the other until the end of input has been reached
107    try:
108        while reader.read(mol):
109            # compose a simple molecule identifier
110            mol_id = Chem.getName(mol).strip() 
111
112            if mol_id == '':
113                mol_id = '#' + str(i) # fallback if name is empty
114            else:
115                mol_id = f'\'{mol_id}\' (#{i})'
116
117            if not args.quiet:
118                print(f'- Computing 2D layout of molecule {mol_id}...')
119
120            try:
121                # generate a 2D structure layout of the read molecule
122                gen2DLayout(mol, coords_calc, bs_flags_calc, args) 
123
124                # enforce the output of 2D coordinates in case of MDL file formats
125                Chem.setMDLDimensionality(mol, 2)
126
127                # output the laid out molecule
128                if not writer.write(mol):   
129                    sys.exit(f'Error: writing molecule {mol_id} failed')
130                        
131            except Exception as e:
132                sys.exit(f'Error: 2D structure layout generation or output for molecule {mol_id} failed: {str(e)}')
133
134            i += 1
135                
136    except Exception as e: # handle exception raised in case of severe read errors
137        sys.exit(f'Error: reading molecule failed: {str(e)}')
138
139    writer.close()
140    sys.exit(0)
141
142if __name__ == '__main__':
143    main()

Download source file