Source code for molsystem.molfile

# -*- coding: utf-8 -*-

"""Functions for handling MDL molfiles"""

import logging
import time

logger = logging.getLogger(__name__)


[docs]class MolFileMixin: """A mixin for handling MDL Molfiles."""
[docs] def to_molfile_text( self, configuration=None, title=None, comment='Exported from SEAMM' ): """Create the text of the Molfile from the system. Parameters ---------- configuration : int = None The configuration to use, defaults to the current configuration. title : str = None The title for the structure, by default the system name. comment : str = 'Exported from SEAMM' Comment line Returns ------- text : str The text of the file. """ lines = [] atoms = self.atoms bonds = self.bonds if configuration is None: configuration = self.current_configuration n_atoms = atoms.n_atoms(configuration=configuration) n_bonds = bonds.n_bonds(configuration=configuration) nsgroups = 0 n3d = 0 is_chiral = 0 # may need to think about this later. if title is None: lines.append(self.name) else: lines.append(title) date_time = time.strftime('%m%d%y%H%M') lines.append('PS' + 'SEAMM_WF' + date_time + '3D') lines.append(comment) lines.append(' 0 0 0 0 0 999 V3000') lines.append('M V30 BEGIN CTAB') lines.append( 'M V30 COUNTS {} {} {} {} {}'.format( n_atoms, n_bonds, nsgroups, n3d, is_chiral ) ) lines.append('M V30 BEGIN ATOM') count = 0 if 'formal charges' in atoms: for row in atoms.atoms(configuration): count += 1 symbol = self.to_symbols([row['atno']]) lines.append( f"M V30 {count} {symbol} {row['x']} {row['y']} {row['z']}" " 0 CHG={row['formal charge']}" ) else: for row in atoms.atoms(configuration=configuration): count += 1 symbol = self.to_symbols([row['atno']])[0] lines.append( f"M V30 {count} {symbol} {row['x']} {row['y']} {row['z']}" " 0" ) lines.append('M V30 END ATOM') lines.append('M V30 BEGIN BOND') count = 0 for row in bonds.bonds(configuration=configuration): count += 1 lines.append( f"M V30 {count} {row['bondorder']} " f"{row['i']} {row['j']}" ) lines.append('M V30 END BOND') lines.append('M V30 END CTAB') lines.append('M END') return '\n'.join(lines)
[docs] def from_molfile_text(self, data, configuration=None): """Create the system from an MDL Molfile, version 3 Parameters ---------- data : str The complete text of the Molfile. configuration : int = None The configuration to use, defaults to the current configuration. """ self.clear(configuration=configuration) self.periodicity = 0 n_molecules = 0 lines = enumerate(data.splitlines()) # title lineno, title = next(lines) self.name = title.strip() # header next(lines) # comment next(lines) lineno, line = next(lines) if line.split()[6] != 'V3000': raise RuntimeError( f"molfile:to_seamm -- the file is not version 3: '{line}'" ) for lineno, line in lines: logger.debug(f'{lineno}: {line}') if 'M END' in line: break elif 'M V30 BEGIN CTAB' in line: n_molecules += 1 if n_molecules > 1: raise NotImplementedError('Multiple molecules?') elif 'M V30 END CTAB' in line: pass elif 'M V30 COUNTS' in line: natoms, nbonds, nsgroups, n3d, is_chiral = line.split()[3:] natoms = int(natoms) nbonds = int(nbonds) # not used, yet. # nsgroups = int(nsgroups) # n3d = int(n3d) # is_chiral = bool(is_chiral) elif 'M V30 BEGIN ATOM' in line: logger.debug('In atom table') xs = [] ys = [] zs = [] symbols = [] formal_charges = [] have_formal_charges = False for lineno, line in lines: if 'M V30 END ATOM' in line: logger.debug(f'Saving {len(xs)} atoms to system') if ( have_formal_charges and 'formal_charges' not in self.atoms ): logger.debug(' with formal charges') self.atoms.add_attribute( 'formal_charge', coltype='int', default=0 ) atom_ids = self.atoms.append( configuration=configuration, x=xs, y=ys, z=zs, symbol=symbols, formal_charge=formal_charges ) else: atom_ids = self.atoms.append( configuration=configuration, x=xs, y=ys, z=zs, symbol=symbols ) break i, symbol, x, y, z, q = line.split()[2:8] xs.append(float(x)) ys.append(float(y)) zs.append(float(z)) symbols.append(symbol) if 'CHG=' in line: for tmp in line.split()[8:]: if 'CHG=' in tmp: formal_charges.append(int(tmp[4:])) have_formal_charges = True else: formal_charges.append(0) elif 'M V30 BEGIN BOND' in line: logger.debug('In bond table') iatoms = [] jatoms = [] bondorders = [] for lineno, line in lines: if 'M V30 END BOND' in line: if len(iatoms) > 0: self.bonds.append( i=iatoms, j=jatoms, bondorder=bondorders, configuration=configuration ) break bondorder, iatom, jatom = line.split()[3:6] iatoms.append(atom_ids[int(iatom) - 1]) jatoms.append(atom_ids[int(jatom) - 1]) bondorders.append(int(bondorder))