Exemple #1
0
def run_amber(program, params):
    """
    Simple wrapper to execute external AMBER programs through subprocess.

    :param program: AMBER program file name
    :type program: string
    :param params: paramters to the AMBER program
    :type params: string
    :raises: SetupError
    :returns: True on failure
    """

    cmd = shlex.split(program)
    cmd.extend(shlex.split(params))

    logger.write('Executing command:\n%s %s\n' % (program, params))

    env = _setenv()
    proc = subp.Popen(cmd, stdout=subp.PIPE, stderr=subp.PIPE, env=env)
    out, err = proc.communicate()

    for stream in out, err:
        text = _cleanup_string(stream)

        if text:
            logger.write('  %s {\n    %s\n  }\n' %
                         ('stdout' if stream is out else 'stderr', text))

    if proc.returncode:
        return out, err

    return False
Exemple #2
0
    def copy_files(srcs, filenames=None, overwrite=False):
        """
        Copy files from basedir to the current directory.

        :param src: the source directories to copy from
        :type src: list of str
        :param filenames: the filenames to be copied
        :type filenames: list of str
        :param overwrite: overwrite the files in the current dir?
        :type overwrite: bool
        """

        try:
            for src in srcs:
                logger.write('%sopying directory contents of %s to %s' %
                             ('Overwrite mode: c' if overwrite else 'C', src,
                              os.getcwd()))

                if not filenames:
                    filenames = os.listdir(src)

                for filename in filenames:
                    if overwrite or not os.access(filename, os.F_OK):
                        src_file = os.path.join(src, filename)

                        # FIXME: only here to accommodate Complex
                        if os.access(src_file, os.F_OK):
                            shutil.copy(src_file, '.')

        except OSError as why:
            raise errors.SetupError(why)
Exemple #3
0
    def __init__(self, FE_sub_type, separate, ff, con_morph, atoms_initial,
                 atoms_final, lig_initial, lig_final, atom_map,
                 reverse_atom_map, zz_atoms, gaff):

        self.separate = separate
        self.ff = ff
        self.gaff = gaff
        self.atoms_final = atoms_final
        self.lig_initial = lig_initial
        self.lig_final = lig_final
        self.atom_map = atom_map
        self.reverse_atom_map = reverse_atom_map
        self.zz_atoms = zz_atoms

        self.files_created = []

        self.frcmod = None

        self.dummies0 = not all([a.atom for a in atom_map.keys()])
        self.dummies1 = not all([a.atom for a in atom_map.values()])

        if self.separate and self.dummies0 and self.dummies1:
            self.FE_sub_type = 'dummy3'
        elif self.separate and (self.dummies0 or self.dummies1):
            self.FE_sub_type = 'dummy2'
        else:
            self.FE_sub_type = 'dummy'

            if self.separate:
                logger.write('Warning: linear transformation, not separating '
                             'into vdw and electrostatic step\n')
Exemple #4
0
    def get_charge(self):
        """
        Get the protein charge via leap.

        :raises: SetupError
        """

        mol_file = self.mol_file

        if not os.access(mol_file, os.R_OK):
            raise errors.SetupError(
                'the protein start file %s does not exist ' % mol_file)

        out = utils.run_leap(
            '', '', 'tleap',
            '%s\np = loadpdb %s\ncharge p\n' % (self.ff_cmd, mol_file))

        charge = None

        for line in out.split('\n'):
            if 'Total unperturbed charge:' in line[0:]:
                charge = line.split(':')[1]
                break

        if charge:
            try:
                self.charge = float(charge)
            except ValueError:
                raise errors.SetupError(
                    'Cannot convert charge from string: %s' % charge)
        else:
            raise errors.SetupError('leap cannot compute charge')

        logger.write('Protein charge: %.3f' % self.charge)
Exemple #5
0
    def _run_mdprog(self, suffix, config, mask, restr_force):
        """
        Run DL_POLY executable.
        """

        if mask:
            self._make_restraints(mask, restr_force)

        self.dlpoly.writeField(FIELD_FILENAME)

        with open(CONTROL_FILENAME, 'w') as mdin:
            mdin.writelines(config)

        retc, out, err = utils.run_exe(' '.join(
            (self.mdpref, self.mdprog, self.mdpost)))

        if retc:
            logger.write(err)
            raise errors.SetupError('%s has failed (see logfile)' %
                                    self.mdprog)

        for mfile in MOVE_LIST:
            try:
                shutil.move(mfile, mfile + os.extsep + suffix)
            except IOError:  # some files may not be created
                continue

        try:
            shutil.copy2(REVCON_FILENAME, REVCON_FILENAME + os.extsep + suffix)
            shutil.move(REVCON_FILENAME, CONFIG_FILENAME)
        except IOError as why:
            raise errors.SetupError(why)

        self.run_no += 1
Exemple #6
0
def prelude(opts):
    lff = len(opts[SECT_DEF]['forcefield'])
    deff = defaults[SECT_DEF]['forcefield'][0]

    if lff < 1 or lff > 5:
        raise dGprepError('malformed "forcefield" key, must have 1-5 strings '
                          'required')

    if len(opts[SECT_DEF]['mdengine']) != 2:
        raise dGprepError('malformed "mdengine" key, 2 strings required')


    create_logger(opts[SECT_DEF]['logfile'])
    logger.write('\n%s\n\n%s\n' % (vstring, istring))
    atexit.register(lambda : logger.finalize() )

    ff_opts = list(opts[SECT_DEF]['forcefield'])
    lu = len(ff_opts)
    ff_opts[lu:] = deff[lu:]

    ff_opts.append(opts[SECT_DEF]['ff_addons'])
    ff_opts.append(opts[SECT_DEF]['mdengine'][0])
    ff_opts.append(opts[SECT_DEF]['parmchk_version'])
    ff_opts.append(opts[SECT_DEF]['gaff'])

    return prep.ForceField(*ff_opts)
Exemple #7
0
    def md(self, namelist='', nsteps=1000, T=300.0, p=1.0,
           restr_str='', restr_force=5.0, nrel=1, wrap=True, dt=0.002):
        """
        Use the sander module from AMBER to run molecular dynamics on a system.

        :param namelist: MD paramers, if start with '%' respective default is
           chosen, otherwise a full sander namelist as string
        :type namelist: string
        :param nsteps: maximum number of MD steps
        :type nsteps: integer
        :param T: temperature
        :type T: float
        :param p: pressure
        :type p: float
        :param restr_str: pre-defined restraints or AMBER mask
        :type restr_str: string
        :param restr_force: force constant for the restraints in kcal/mol/AA
        :type restr_force: float
        :param nrel: number of restraint relaxation steps
        :type nrel: integer
        :param wrap: wrap coordinates in a periodic system
        :type wrap: bool
        :param dt: timestep in ps
        :type dt: float
        :raises: SetupError
        """

        constp = False

        if namelist[0] == '%':
            pname = namelist[1:]
            logger.write('Running MD with protocol %s' % pname)

            if pname == 'PRESS' or pname == 'SHRINK':
                constp = True

            try:
                namelist = PROTOCOLS['MD_%s' % pname]
            except KeyError:
                raise errors.SetupError('no such MD protocol predefined: '
                                        '%s' % namelist)

            restraint = ''

            if restr_str:
                try:
                    mask = mdebase._restraint_table[restr_str]
                except KeyError:
                    mask = restr_str

                restraint = (mdebase._rs % (restr_force, mask) )

            namelist = namelist.format(nsteps, nsteps / 5, nsteps / 10,
                                       self.md_periodic + restraint,
                                       '{:d}'.format(wrap), T, p, dt)


        self._run_mdprog(mdebase.MD_PREFIX, namelist, mask, constp)
Exemple #8
0
    def md(self, config='', nsteps=1000, T=300.0, p=1.0, mask='',
           restr_force=5.0, nrel=1, wrap=True, dt=0.002):
        """
        Run molecular dynamics on a system.

        :param config: MD paramers, if start with '%' respective default is
           chosen, otherwise a full mdrun input as string
        :type config: string
        :param nsteps: maximum number of MD steps
        :type nsteps: integer
        :param T: temperature
        :type T: float
        :param p: pressure
        :type p: float
        :param mask: pre-defined restraints or AMBER mask
        :type mask: string
        :param restr_force: force constant for the restraints in kcal/mol/AA
        :type restr_force: float
        :param nrel: number of restraint relaxation steps
        :type nrel: integer
        :param wrap: wrap coordinates in a periodic system
        :type wrap: bool
        :param dt: timestep in ps
        :type dt: float
        :raises: SetupError
        """

        prefix = mdebase.MD_PREFIX + '%05i' % self.run_no

        if self.run_no == 1:
            gen_vel = 'yes'
        else:
            gen_vel = 'no'

        if config[0] == '%':
            pname = config[1:]
            logger.write('Running MD with protocol %s' % pname)

            try:
                config = PROTOCOLS['MD_%s' % pname]
            except KeyError:
                raise errors.SetupError('no such MD protocol predefined: '
                                        '%s' % config)

            if mask:
                cons = 'define = -DPOSRES'
            else:
                cons = ''

            # read coordinates from self.amber_pdb because origin is at center
            # of cell, it is on one edge in the parm file!
            #
            # FIXME: water model, box shape
            config = config.format(cons, nsteps, nsteps / 10, nsteps / 5,
                                   dt, nsteps * dt, gen_vel, T, p)

        self._run_mdprog(prefix, config, mask, restr_force)
        self.prefix = prefix
Exemple #9
0
def run_leap(top, crd, program='tleap', script=''):
    """
    Simple wrapper to execute the AMBER leap program.

    :param top: topology file name, used to check if created
    :type top: string
    :param crd: coordinate file name, used to check if created
    :type crd: string
    :param program: program file name, either tleap or sleap (not recommended)
    :type program: string
    :param script: leap script as string, if 'leap.in' read from respective file
      name
    :type script: string
    :returns: output from leap
    :raises: SetupError
    """

    leap = check_amber(program)
    cmd = [leap, '-f']

    env = _setenv()

    if script == 'leap.in':
        cmd.append(script)
        logger.write('Executing command:\n%s' % ' '.join(cmd))

        proc = subp.Popen(cmd,
                          stdin=None,
                          stdout=subp.PIPE,
                          stderr=subp.PIPE,
                          env=env)
        out = proc.communicate()[0]
    else:
        cmd.append('-')
        logger.write('Executing command:\n%s -f - <<_EOF \n%s\n_EOF\n' %
                     (leap, script))

        proc = subp.Popen(cmd,
                          stdin=subp.PIPE,
                          stdout=subp.PIPE,
                          stderr=subp.PIPE,
                          env=env)
        out = proc.communicate(script)[0]

    if top and crd:
        if os.path.getsize(top) == 0 or os.path.getsize(crd) == 0:
            raise errors.SetupError(
                'Leap did not create the topology and/or coordinate '
                'file(s): %s, %s' % (top, crd))

    return out
Exemple #10
0
    def __init__(self, FE_sub_type, separate, ff, con_morph, atoms_initial,
                 atoms_final, lig_initial, lig_final, atom_map,
                 reverse_atom_map, zz_atoms, gaff):

        self.separate = separate
        self.ff = ff
        self.gaff = gaff
        self.con_morph = con_morph
        self.atoms_initial = atoms_initial
        self.atoms_final = atoms_final
        self.lig_initial = lig_initial
        self.lig_final = lig_final
        self.atom_map = atom_map
        self.reverse_atom_map = reverse_atom_map
        self.zz_atoms = zz_atoms

        self.files_created = []

        self.frcmod0 = None
        self.frcmod1 = None

        self.dummies0 = not all([a.atom for a in atom_map.keys()])
        self.dummies1 = not all([a.atom for a in atom_map.values()])

        want_softcore = FE_sub_type[:8] == 'softcore'
        self.FE_sub_type = ''

        # overwrite user choice, also for backward compatibility
        if self.separate and self.dummies0 and self.dummies1:
            if want_softcore:
                self.FE_sub_type = 'softcore3'
            else:
                self.FE_sub_type = 'dummy3'
        elif self.separate and (self.dummies0 or self.dummies1):
            if want_softcore:
                self.FE_sub_type = 'softcore2'
            else:
                self.FE_sub_type = 'dummy2'
        else:
            if want_softcore:
                self.FE_sub_type = 'softcore'
            else:
                self.FE_sub_type = 'dummy'

            if self.separate:
                logger.write('Warning: linear transformation, not separating '
                             'into vdw and electrostatic step\n')
Exemple #11
0
    def _run_mdprog(self, prefix, config, mask, restr_force):
        """
        Run mdrun from the Gromacs package.
        """

        filename = prefix + os.extsep
        config_filename = '_' + filename + 'mdp'

        with open(config_filename, 'w') as mdin:
            mdin.writelines(config)

        if self.run_no == 1:
            params = ('-f %s -c %s -r %s -p %s -o %s -po %s' %
                      (config_filename, self.gro, self.gro, self.top,
                       filename + 'tpr', filename + 'mdp') )
        else:
            edr = self.prev + os.extsep + 'edr'
            trr = self.prev + os.extsep + 'trr'

            params = ('-f %s -c %s -r %s -e %s -t %s -p %s -o %s -po %s' %
                      (config_filename, self.prev + os.extsep + 'gro',
                       self.gro, edr, trr, self.top, filename + 'tpr',
                       filename + 'mdp'))

        if mask:
             self._make_restraints(mask, restr_force)

        retc, out, err = utils.run_exe(' '.join((self.grompp, params)))

        if retc:
            logger.write(err)
            raise errors.SetupError('%s has failed (see logfile)' %
                                    self.grompp)

        params = '-deffnm %s' % prefix

        retc, out, err = utils.run_exe(' '.join((self.mdpref, self.mdprog,
                                                 self.mdpost, params)))

        if retc:
            logger.write(err)
            raise errors.SetupError('%s has failed (see logfile) has failed' %
                                    self.mdprog)

        self.run_no += 1
        self.prev = prefix
Exemple #12
0
    def __init__(self, FE_sub_type, separate, ff, con_morph, atoms_initial,
                 atoms_final, lig_initial, lig_final, atom_map,
                 reverse_atom_map, zz_atoms, gaff):

        if FE_sub_type[:4] != 'pert':
            raise errors.SetupError('Morph code only supports the '
                                    'CHARMM/PERT module')
        self.separate = separate
        self.ff = ff
        self.gaff = gaff
        self.con_morph = con_morph
        self.atoms_initial = atoms_initial
        self.atoms_final = atoms_final
        self.lig_initial = lig_initial
        self.lig_final = lig_final
        self.atom_map = atom_map
        self.reverse_atom_map = reverse_atom_map
        self.zz_atoms = zz_atoms

        self.topol = None
        self.itypes = []
        self.ftypes = []

        self.files_created = []

        self.dummies0 = not all([a.atom for a in self.atom_map.keys()])
        self.dummies1 = not all([a.atom for a in self.atom_map.values()])

        if not self.dummies0 and not self.dummies1:
            self.softcore = 'nopssp'
        else:
            self.softcore = 'pssp'

        if self.separate and self.dummies0 and self.dummies1:
            self.FE_sub_type = 'pert3'
        elif self.separate and (self.dummies0 or self.dummies1):
            self.FE_sub_type = 'pert2'
        else:
            self.FE_sub_type = 'pert'

            if self.separate:
                logger.write('Warning: linear transformation, not separated '
                             'into vdw and electrostatic step\n')

        self.stype = self.FE_sub_type
Exemple #13
0
    def _parmchk(self, infile, informat, outfile):
        """
        Run parmcheck to generate missing parameters.

        
        :param infile: input file name
        :type infile: string
        :param informat: input file format
        :type informat: string
        :param outfile: output frcmod file
        :type outifle: string
        :param gaff: GAFF version (needs parmchk2)
        :type gaff: string
        """

        params = ''

        if self.parmchk_version > 1:
            parmchk = utils.check_amber('parmchk%s' %
                                        str(self.parmchk_version))
            if self.gaff == 'gaff2':
                params += '-s gaff2 '
        else:
            parmchk = utils.check_amber('parmchk')

        logger.write('Creating frcmod file')

        params += '-i %s -f %s -o %s -a N ' % (infile, informat, outfile)

        # FIXME: parmchk only reads one parmfile since AmberTools 16,
        #        and ignores -p, not sure what the thinking her is...
        #        possible solution: write temporary frcmod files and paste
        #                           together?
        #       if self.ff_addons:
        #           addon = self.ff_addons[0]

        #           # FIXME: Can it get any uglier? Consistent file naming, ey...
        #           if addon.startswith('GLYCAM_06'):
        #               addon = addon[:10]

        #           params += ' -p %s' % (os.path.join(os.environ['AMBERHOME'], 'dat',
        #                                             'leap', 'parm', addon) +
        #                                 os.extsep + 'dat')

        utils.run_amber(parmchk, params)
Exemple #14
0
    def minimize(self, config='%STD', nsteps=100, ncyc=100, mask='',
                 restr_force=5.0):
        """
        Minimize a system.

        :param config: MD paramers, if start with '%' respective default is
           chosen, otherwise a full mdrun input as string
        :type config: string
        :param nsteps: maximum number of conjugated gradient steps
        :type nsteps: integer
        :param ncyc: number of initial steepest decent steps
        :type ncyc: float
        :param mask: pre-defined restraints or AMBER mask
        :type mask: string
        :param restr_force: force constant for the restraints in kcal/mol/AA
        :type restr_force: float
        :raises: SetupError
        """

        prefix = mdebase.MIN_PREFIX + '%05i' % self.run_no

        if config[0] == '%':
            pname = config[1:]
            logger.write('Running minimisation with protocol %s' % pname)

            try:
                config = PROTOCOLS['MIN_%s' % pname]
            except KeyError:
                raise errors.SetupError('no such min protocol predefined: '
                                        '%s' % config)

            if mask:
                cons = 'define = -DPOSRES'
            else:
                cons = ''

            # read coordinates from self.amber_pdb because origin is at center
            # of cell, it is on one edge in the parm file!
            #
            # FIXME: water model, box shape
            config = config.format(cons, nsteps, ncyc, nsteps / 10, nsteps / 5)

        self._run_mdprog(prefix, config, mask, restr_force)
        self.prefix = prefix
Exemple #15
0
    def minimize(self, namelist='%ALL', nsteps=100, ncyc=10, restr_str='',
                 restr_force=5.0):
        """
        Use the AMBER/sander module to minimize a system.

        :param config: MD paramers, if start with '%' respective default is
           chosen, otherwise a full sander namelist as string
        :type config: string
        :param nsteps: maximum number of conjugated gradient steps
        :type nsteps: integer
        :param ncyc: number of initial steepest decent steps
        :type ncyc: float
        :param restr_str: pre-defined restraints or AMBER mask
        :type restr_str: string
        :param restr_force: force constant for the restraints in kcal/mol/AA
        :type restr_force: float
        :raises: SetupError
        """

        if namelist[0] == '%':
            pname = namelist[1:]
            logger.write('Running minimisation with protocol %s' % pname)

            try:
                namelist = PROTOCOLS['MIN_%s' % pname]
            except KeyError:
                raise errors.SetupError('no such min protocol predefined: '
                                        '%s' % namelist)

            restraint = ''

            if restr_str:
                try:
                    mask = mdebase._restraint_table[restr_str]
                except KeyError:
                    mask = restr_str

                restraint = (mdebase._rs % (restr_force, mask) )

            namelist = namelist.format(nsteps, ncyc, nsteps / 5,
                                       self.min_periodic + restraint)

        self._run_mdprog(mdebase.MIN_PREFIX, namelist, mask, False)
Exemple #16
0
def run_exe(cmdline):
    """
    Simple wrapper to execute the external programs through subprocess.

    :param cmdline: complete command line as given on a shell prompt
    :type cmdline: str
    """

    logger.write('Executing command:\n%s\n' % cmdline)

    env = os.environ.copy()

    if 'COPY_LD_LIBRARY_PATH' in os.environ:
        env['LD_LIBRARY_PATH'] = os.environ['COPY_LD_LIBRARY_PATH']
    else:
        env['LD_LIBRARY_PATH'] = ''

    proc = subp.Popen(shlex.split(cmdline),
                      stdout=subp.PIPE,
                      stderr=subp.PIPE,
                      env=env)
    out, err = proc.communicate()

    return proc.returncode, out, err
Exemple #17
0
    def _run_mdprog(self, prefix, namelist, mask, constp):
        """
        Run sander/pmemd from the Amber package.
        """

        prefix += '%05i'

        prefix = prefix % self.run_no
        self.sander_rst = prefix + mdebase.RST_EXT

        with open(prefix + os.extsep + 'in', 'w') as mdin:
            mdin.writelines(namelist)

        # NOTE: we assume trajectory will be written in NetCDF
        #       format (ioutfm = 1)
        flags = ('-i {0}.in -p {1} -c {2} '
                 '-O -o {0}.out -e {0}.en -x {0}.nc -inf {0}.info -r {3}')

        if mask:
            # Constant pressure with positional restraints shifts coordinates.
            if constp:
                flags += ' -ref %s' % self.sander_crd
            else:
                flags += ' -ref %s' % self.amber_crd

        err = utils.run_amber(self.mdpref + ' ' + self.mdprog,
                              flags.format(prefix, self.amber_top,
                                           self.sander_crd, self.sander_rst) )

        if err:
            logger.write('sander/pmemd failed with message %s' % err[1])
            raise errors.SetupError('error in sander run %s: %s' %
                                    (prefix, err[1]) )

        self.sander_crd = self.sander_rst
        self.run_no += 1
Exemple #18
0
    def minimize(self,
                 config='%STD',
                 nsteps=100,
                 ncyc=100,
                 mask='',
                 restr_force=5.0):
        """
        Minimize a system.

        :param config: MD paramers, if start with '%' respective default is
           chosen, otherwise a full mdrun input as string
        :type config: string
        :param nsteps: maximum number of conjugated gradient steps
        :type nsteps: integer
        :param ncyc: number of initial steepest decent steps
        :type ncyc: float
        :param mask: pre-defined restraints or AMBER mask
        :type mask: string
        :param restr_force: force constant for the restraints in kcal/mol/AA
        :type restr_force: float
        :raises: SetupError
        """

        suffix = '%05i' % self.run_no

        if config[0] == '%':
            pname = config[1:]
            logger.write('Running minimisation with protocol %s' % pname)

            try:
                config = PROTOCOLS['MIN_%s' % pname]
            except KeyError:
                raise errors.SetupError('no such min protocol predefined: '
                                        '%s' % config)

        self._run_mdprog(suffix, config, mask, restr_force)
Exemple #19
0
def preminimize(self, add_hyd = False, ffield = 'mmff94', nsteps = 10):
    """
    Preminimize mol2 file using the small molecule force fields of OpenBabel.
    Typically usage is to avoid convergence problems with sqm due to a 'bad'
    starting structure.

    :param add_hyd: add hydrogens
    :type add_hyd: bool
    :param ffield: small molecule force field supported by OpenBabel
    :type ffield: string
    :param nsteps: number of minimisation steps
    :type nsteps: integer
    :raises: SetupError
    """

    logger.write('Minimizing %s (%s format)' %
                 (self.mol_file, self.mol_fmt) )

    # NOTE: Openbabel may miscalculate charges from mol2 files
    try:
        mol = pybel.readfile(self.mol_fmt, self.mol_file).next()
    except IOError as why:
        raise errors.SetupError(why)

    if add_hyd:
        logger.write('Adding hydrogens')
        mol.addh()

    mol.localopt(forcefield = ffield, steps = nsteps)

    logger.write('Writing structure to %s (%s format)' %
                 (self.mol_file, self.mol_fmt) )

    try:
        mol.write(self.mol_fmt, self.mol_file, overwrite = True)
    except IOError as why:
        raise errors.SetupError(why)

    self.ref_file = self.mol_file
    self.ref_fmt = self.mol_fmt
Exemple #20
0
def prepare(self, to_format = 'mol2', addH = False, calc_charge = False,
            correct_for_pH = False, pH = 7.4):
    """
    Read a molecule structure with Openbabel and get basic information from
    it.  If requested modifiy the data and convert the file.

    :param to_format: format to convert to if not empty
    :type to_format: string
    :param addH: add hydrogens? (experimental)
    :type addH: bool
    :param calc_charge: force total formal charge calculation if True,
                        leave it to Openbabel if False
    :type calc_charge: bool
    :param correct_for_pH: correct for pH, i.e. determine protonation state?
                           (very experimental!)
    :type correct_for_pH: bool
    :param pH: pH to be considered when correct_for_pH is True
               (very experimental!)
    :type pH: float
    :raises: SetupError
    """

    conv = ob.OBConversion()
    mol = ob.OBMol()
    ob_read_one(conv, self.mol_file, mol, self.mol_fmt, to_format)

    # FIXME: does this test for the right thing?
    if mol.GetDimension() != 3:
        raise errors.SetupError('input cooridnates (%s) must have 3 dimensions'
                                % self.mol_file)

    orig_atoms = []

    etab = ob.OBElementTable()
    acnt = 0

    for atom in ob.OBMolAtomIter(mol):
        acnt += 1

        res = atom.GetResidue()     # when is this NULL?
        element = etab.GetSymbol(atom.GetAtomicNum() )
        orig_atoms.append( (res.GetAtomID(atom).strip(), element) )

        if to_format:
            res.SetAtomID(atom, '%s%d' % (element, acnt) )

    if addH:
        logger.write('Adding hydrogens')

        mol.DeleteHydrogens()

        if correct_for_pH:              # reimplementation from phmodel.cpp
            mol.SetAutomaticFormalCharge(correct_for_pH)

            transform = ob.OBChemTsfm()

            logger.write('Applying transforms')

            for reactant, product, pKa in const.TRANSFORM_SMARTS:
                success = transform.Init(reactant, product)

                if not success:
                    raise ValueError('BUG in SMARTS transform Init %s >> %s',
                                     (reactant, product) )

                if (transform.IsAcid() and pH > pKa) or \
                       (transform.IsBase() and pH < pKa):
                    success = transform.Apply(mol)

        mol.AddHydrogens(False, False, 0.0) # valence filling


    if mol.GetTotalSpinMultiplicity() != 1:
        raise errors.SetupError('only multiplicity=1 supported (%s)'
                                % self.mol_file)

    if to_format:
        logger.write('Converting/Writing %s (%s format) to %s format' %
                     (self.mol_file, self.mol_fmt, to_format) )

        # NOTE: this relies on a modified Openbabel MOL2 writer
        conv.AddOption('r', ob.OBConversion.OUTOPTIONS)  # do not append resnum

        self.mol_file = const.CONV_MOL2_FILE % to_format
        self.mol_fmt = to_format

        try:
            conv.WriteFile(mol, self.mol_file)
        except IOError as why:
            raise errors.SetupError(why)
    else:
        logger.write('Leaving %s unmodified.' % self.mol_file)


    # some formats allow formal charge definition:
    # PDB (col 79-80), SDF(M CHG, atom block), MOL2 (UNITY_ATOM_ATTR?)
    if calc_charge:
        logger.write('Computing total formal charge\n')

        # set formal charge for some functional groups by hand because
        # OpenBabel doesn't support it in C++ for formats like mol2
        formal_charge = 0
        sm = ob.OBSmartsPattern()

        for smarts, fchg in const.CHARGE_SMARTS:
            sm.Init(smarts)

            if sm.Match(mol):
                m = list(sm.GetUMapList() )
                formal_charge += fchg * len(m)

        mol.SetTotalCharge(formal_charge)
        self.charge = formal_charge
    else:
        logger.write('Total formal charge taken from coordinate file\n')
        self.charge = mol.GetTotalCharge()  # trust Openbabel...

    self.atomtype = 'sybyl'     # Openbabel will try to convert to Sybyl format

    conv.SetOutFormat('smi')
    conv.AddOption('n')
    conv.AddOption('c')
    smiles = conv.WriteString(mol).rstrip()

    conv.SetOutFormat('inchikey')
    errlev = ob.obErrorLog.GetOutputLevel()
    ob.obErrorLog.SetOutputLevel(0)

    inchi_key = conv.WriteString(mol).rstrip()

    ob.obErrorLog.SetOutputLevel(errlev)

    logger.write('''Ligand key data:
Formula: %s
SMILES: %s
InChIKey: %s
Net charge: %i
Molecular weight: %f\n''' % (mol.GetFormula(), smiles, inchi_key, self.charge,
                             mol.GetMolWt() ) )
Exemple #21
0
    def param(self, gb_charges=False, sqm_strategy=None):
        """
        Compute symmetrized AM1/BCC charges and generate missing forcefield
        parameters. Runs antechamber, parmchk. Finally generated MOL2 file
        is in Sybyl format.  GAFF atom names are needed internally by AMBER.

        :param gb_charges: use a GB model for parameterisation
        :type gb_charges: bool
        :param sqm_strategy: a strategy pattern using preminimize() and setting
           the SCF convergence criterion for sqm
        :type sqm_strategy: list of 2-tuples
        :raises: SetupError
        """

        logger.write('Deriving AMBER/GAFF force field parameters')

        antechamber = utils.check_amber('antechamber')

        ac_cmd = [
            '-i %s' % self.mol_file,  # input file
            '-fi %s' % self.mol_fmt,  # input file format
            '-o %s' %
            const.LIGAND_AC_FILE,  # output file, crds from input file
            '-fo ac',  # output file format
            '-c bcc',  # charge method
            '-nc %s' % str(self.charge),  # net molecular charge
            '-m 1',  # FIXME: spin multiplicity (sqm only 1)
            '-df 2',  # 0 = mopac, 2 = sqm, (1 was divcon)
            '-at %s' % self.gaff,  # write GAFF types
            '-du y',  # fix duplicate atom names
            '-an y',  # adjust atom names
            '-j 4',  # atom/bond type prediction = full
            '-s 2',  # status information = verbose
            '-eq 2',  # equalise atom charges (path+geometry)
            '-pf y',  # clean up temporary files
            '-rn %s' % const.LIGAND_NAME  # overwrite ligand name
        ]

        tmp_file = const.LIGAND_TMP + os.extsep + self.mol_fmt
        shutil.copyfile(self.mol_file, tmp_file)

        # NOTE: The main problem is SCF convergence. If this happens MM
        #       minimisation is used to hope to obtain a better structure with a
        #       better wavefunction.  This obviously depends on a sensible
        #       assignment of force field parameters which may fail if the
        #       structure is "too" distorted and no bonding information, etc. are
        #       available a priori.
        #       A test on a few thousand ZINC structures showed that this feature
        #       is rarly useful.  It also complicates the code because the
        #       coordinates are changed and this mus be guarded against.
        if not sqm_strategy:
            if not gb_charges:
                sqm_strategy = ((0, '1.0d-10', 1, 500, 1000,
                                 ''), (50, '1.0d-10', 1, 500, 1000,
                                       ''), (0, '1.0d-9', 1, 500, 1000, ''),
                                (50, '1.0d-9', 1, 500, 1000,
                                 ''), (50, '1.0d-9', 0, 500, 1000, ''))
            else:
                # harder cases like ZINC03814826/28/31/32/38 may be parameterised
                # with a GB model and a more elaborate name list, vshift=0.1
                # may later be of use for some cases too
                sqm_strategy = (
                    #(0, '1.0d-10', 1, 1000, 0, ''),
                    #(50, '1.0d-10', 1, 1000, 0, ''),
                    (0, '1.0d-9', 1, 1000, 0, 'ndiis_attempts=100'),
                    (50, '1.0d-9', 1, 1000, 0,
                     'ndiis_attempts=200,ndiis_matrices=10'),
                    (50, '1.0d-9', 0, 1000, 0,
                     'ndiis_attempts=200,ndiis_matrices=20'))

        logger.write('Optimizing structure and creating AM1/BCC charges')
        premin_done = False

        for premin, scfconv, tight, itrmax, maxcyc, sqm_extra in sqm_strategy:
            converged = False

            if premin:
                self.preminimize(nsteps=premin)
                premin_done = True

            sqm_nlv = ("qm_theory='AM1',grms_tol=0.0002,tight_p_conv=%i,\n  "
                       "scfconv=%s,itrmax=%i,pseudo_diag=1,\n  "
                       "maxcyc=%i,\n%s" %
                       (tight, scfconv, itrmax, maxcyc, sqm_extra))
            ek = ['-ek "%s"' % sqm_nlv]  # sqm namelist variables

            # FIXME: Buffering messes with the stdout output order of
            #        antechamber (last line comes first).  Use stdbuf, pexpect
            #        or pty (probably Linux only)?
            err = utils.run_amber(antechamber, ' '.join(ac_cmd + ek))

            if err:
                if 'the assigned bond types may be wrong' in err[0]:
                    logger.write('Error: antechamber failed to assign '
                                 'atom/bond types properly\n')
                    raise errors.SetupError('antechamber cannot assign atom '
                                            'and/or bond types, check input '
                                            'structure, e.g. with acdoctor')

                sce = False

                with open(SQM_OUT, 'r') as sqm:
                    for line in sqm:
                        if 'Unable to achieve self consistency' in line:
                            logger.write('Warning: SCF has not converged '
                                         'with %i %s\n' % (premin, scfconv))
                            sce = True
                            break

                        if 'odd number of electrons' in line:
                            logger.write('Error: odd electron number\n')
                            raise errors.SetupError('wrong ligand charge, or '
                                                    'radical')

                if not sce:
                    raise errors.SetupError('unknown error see log file '
                                            'and %s file' %
                                            os.path.join(self.dst, SQM_OUT))
            else:
                converged = True
                break

        if not converged:
            if sce:
                logger.write('Error: SCF has not converged\n')
                raise errors.SetupError('SCF has not converged')
            else:
                logger.write('Error: failed to produce atom charges\n')
                raise errors.SetupError('failed to produce atom charges')

        # make sure we do not carry over the coordinates from a possible
        # preminimisation step above
        if premin_done:
            utils.run_amber(antechamber, '-i %s -fi ac '
                            '-a %s -fa %s -ao crd '
                            '-o %s -fo ac' %
                            (const.LIGAND_AC_FILE, tmp_file, self.mol_fmt,
                             const.LIGAND_AC_FILE))  # FIXME: dangerous?

        if not gb_charges:
            logger.write('SCF has converged with %i preminimisation steps and '
                         'scfconv = %s kcal/mol\n' % (premin, scfconv))

            ngconv = 0
            H_form = 'unknown'
            grad = 'unknown'

            with open(SQM_OUT, 'r') as sqm:
                for line in sqm:
                    if line.startswith('xmin'):
                        ngconv = int(line[4:10].strip())
                        H_form = line[10:33].strip()
                        grad = line[33:].strip()

            if ngconv >= maxcyc:
                logger.write(
                    'Warning: maximum number of geometry optimisation '
                    'steps reached (%i), gradient = %s '
                    '(grms_tol=0.0002), check %s file\n' %
                    (maxcyc, grad, SQM_OUT))
            else:
                logger.write('Geometry has converged after %i steps, heat of '
                             'formation: %s and gradient = %s\n' %
                             (ngconv, H_form, grad))
        else:
            if self.parmchk_version > 1:
                parmchk = utils.check_amber('parmchk%s' %
                                            str(self.parmchk_version))

                if self.gaff == 'gaff2':
                    params += '-s gaff2 '
            else:
                parmchk = utils.check_amber('parmchk')

            utils.run_amber(
                parmchk, '-i %s -f ac -o %s' %
                (const.LIGAND_AC_FILE, const.GB_FRCMOD_FILE))

            converged = _calc_gb_charge(const.LIGAND_AC_FILE,
                                        const.GB_FRCMOD_FILE, self.charge,
                                        scfconv, tight, sqm_extra, antechamber,
                                        self.gaff)

            if not converged:
                logger.write('Error: GB parameterisation failed\n')
                raise errors.SetupError('failed to produce atom charges')

        self._parmchk(const.LIGAND_AC_FILE, 'ac', self.frcmod)

        charges = []

        with open(const.LIGAND_AC_FILE, 'r') as acfile:
            for line in acfile:
                if line[:4] == 'ATOM':
                    charges.append(float(line[54:64]))

        if filter(lambda ch: math.fabs(ch) > const.MAX_CHARGE, charges):
            logger.write('Warning: some atom charges > %.2f' %
                         const.MAX_CHARGE)

        total_charge = sum(charges)
        dec_frac = total_charge - round(total_charge)

        if abs(dec_frac) > const.MAX_CHARGE_DIFF:
            logger.write('Warning: total molecule charge (%f) is far from '
                         'being an integer' % total_charge)

        corr = dec_frac / len(charges)

        for idx, charge in enumerate(charges):
            charges[idx] = charge - corr

        with open(const.CORR_CH_FILE, 'w') as chfile:
            for charge in charges:
                chfile.write('%.9f\n' % charge)

        utils.run_amber(
            antechamber, '-i %s -fi ac '
            '-o %s -fo ac '
            '-cf %s -c rc '
            '-s 2 -pf y -at %s' % (const.LIGAND_AC_FILE, const.CORR_AC_FILE,
                                   const.CORR_CH_FILE, self.gaff))

        # FIXME: Do we really need this? It only documents the charge orginally
        #        derived via antechamber.
        shutil.copyfile(const.LIGAND_AC_FILE,
                        const.LIGAND_AC_FILE + os.extsep + '0')

        shutil.move(const.CORR_AC_FILE, const.LIGAND_AC_FILE)

        self.charge = float('%.12f' % sum(charges))
        logger.write('Total molecule charge is %.2f\n' % self.charge)

        self.ref_file = self.mol_file
        self.ref_fmt = self.mol_fmt
Exemple #22
0
def mixer(top0, top1, filename = const.GROMACS_PERT_ITP,
          typename = const.GROMACS_PERT_ATP):
    """
    Creates a perturbed Gromacs topology file from two top file mix-ins.

    :param parmtop: Gromacs topology object for state0
    :type parmtop: GromacsTop
    :param inpcrd: Gromacs topology object for state1
    :type inprcd: GromacsTop
    :param filename: itp file name for writing
    :type filename: string
    :raises: SetupError
    """

    import copy

    # FIXME: is this check sufficient?
    if len(top0) != len(top1):
        raise errors.SetupError('topologies of different lengths: %s %s' %
                                (top0.parmtop, top1.parmtop) )

    with open(typename, 'w') as atype:
        #atype.write('\n[ atomtypes ]\n;name btype   mass   charge '
        #            'ptype      sigma      epsilon\n')

        combined = copy.copy(top0.top.atomtypes)
        combined.update(top1.top.atomtypes)

        for typ in sorted(combined):
            atype.write('%-4s %-4s %8.3f   0.0000  A  %12.6e %12.6e\n' %
                        ( (typ, typ) + combined[typ][:]) )


    with open(filename, 'w') as itp:
        for mt0, mt1 in zip(top0.top.moleculetype, top1.top.moleculetype):
            # FIXME: sensible molecule name
            itp.write('\n[ moleculetype ]\n%s 3\n' % mt0.molname)

            natom = 0
            cgnr = 0

            itp.write('\n[ atoms ]\n;   nr  type resno resnm '
                      'atom    cgnr      charge        mass'
                      ' typeB   chargeB       massB\n')

            for atom0, atom1 in zip(mt0.atoms, mt1.atoms):
                natom +=1
                cgnr += 1

                itp.write('%7i %3s %6i %5s %4s %7i %11.6f %11.4f %3s '
                          '%11.6f %11.4f\n' %
                          ( (natom, atom0[0], 1) + atom0[1:3] + (cgnr,) +
                            atom0[3:] + (atom1[0],) + atom1[3:]) )


            if mt0.bonds:
                itp.write('\n[ bonds ]\n;  ai        aj f           r0'
                          '            k            r0B           kB\n')

            for bond0, bond1 in zip(mt0.bonds, mt1.bonds):
                if bond0[2] == 0.0 or bond1[2] == 0.0:
                    logger.write('Warning: zero bonds in %s, %s' %
                                 (' '.join(str(i) for i in bond0),
                                  ' '.join(str(i) for i in bond1)))

                itp.write('%7i %7i 1 %12.6e %12.6e   %12.6e %12.6e\n' %
                          (bond0 + bond1[2:]) )


            if mt0.angles:
                itp.write('\n[ angles ]\n;  ai        aj      ak f'
                          '           a0            k'
                          '            a0B           kB\n')

            for angle0, angle1 in zip(mt0.angles, mt1.angles):
                if angle0[3] == 0.0 or angle1[3] == 0.0:
                    logger.write('Warning: zero angles in %s, %s' %
                                 (' '.join(str(i) for i in angle0),
                                  ' '.join(str(i) for i in angle1)))

                itp.write('%7i %7i %7i 1 %12.6e %12.6e   %12.6e %12.6e\n' %
                          (angle0 + angle1[3:]) )


            if mt0.pairs:
                itp.write('\n[ pairs ]\n;    ai      aj f\n')

            if len(mt0.pairs) != len(mt1.pairs):
                raise errors.SetupError('pairs of different size')

            for pair0, pair1 in zip(sorted(mt0.pairs), sorted(mt1.pairs) ):
                if pair0 != pair1:
                    raise errors.SetupError('different pairs')
                    
                itp.write('%7i %7i 1\n' % pair0)


            if mt0.propers:
                itp.write('\n[ dihedrals ] ;propers\n;    i        j       '
                          'k       l f       phase          pk pn'
                          '        phase          pk pn\n')

            for proper0, proper1 in zip(mt0.propers, mt1.propers):
                for dih0, dih1 in zip(proper0[4], proper1[4]):
                    # for some reason, leap creates null entries
                    if dih0[0] == 0.0 and dih1[0] == 0.0:
                        continue

                    itp.write('%7i %7i %7i %7i 9 %11.2f %11.5f %i   '
                              '%11.2f %11.5f %i\n' %
                              (proper0[:4] + (dih0[2] * const.RAD2DEG,
                                              dih0[0] * const.CAL2J) +
                               (dih0[1],) + (dih1[2] * const.RAD2DEG,
                                             dih1[0] * const.CAL2J) +
                               (dih1[1],)) )


            if mt0.impropers:
                itp.write('\n[ dihedrals ] ;impropers\n;    i        j    '
                          '   k       l f       phase          pk pn'
                          '        phase          pk pn\n')

            for improper0, improper1 in zip(mt0.impropers, mt1.impropers):
                        itp.write('%7i %7i %7i %7i 4 %11.2f %11.5f %i   '
                                  '%11.2f %11.5f %i\n' %
                                  (improper0 + improper1[4:]) )

        itp.write('\n\n')
Exemple #23
0
def protonate_propka(self, pH = 7.0):
    """
    Protonate a protein with PROPKA 3.1.

    :param pH: desired pH for protein protonation
    :type pH: float
    """

    import os
    import sys
    import  StringIO

    import propka.lib as plib
    import FESetup.propka.newmc as pmc

    PROT_RES = ('HIS', 'ASP', 'GLU')
    DEPROT_RES = ('LYS', 'CYS', 'ARG', 'TYR')

    # add PDB file name to options to avoid warning of missing file, also
    # set config file path to module path to find propka.cfg
    options, dummy = plib.loadOptions( ['--pH', pH, '-q', self.mol_file] )
    options.parameters = os.path.join(os.path.dirname(pmc.__file__),
                                      options.parameters)

    with CaptureOutput() as output:
        mol = pmc.Molecular_container_new(self.mol_file, options)
        pKas = mol.calculate_pka()

    logger.write('%s%s' % (output[0], output[1]) )

    protres = []

    for resName, resSeq, chainID, pKa in pKas:
        # NOTE: currently we ignore termini 'N+' and 'C-'
        if pH < pKa:
            if resName in PROT_RES:
                protres.append( (resName, resSeq, chainID) )
        else:
            if resName in DEPROT_RES:
                protres.append( (resName, resSeq, chainID) )

    logger.write('pH = %.2f' % pH)

    msg_res = set()

    with open(const.PROTONATED_PDB_FILE, 'w') as newfile:
        with open(self.mol_file, 'r') as pdbfile:
            for line in pdbfile:
                if line[:6] in ('ATOM  ', 'HETATM'):
                    resName = line[17:21].strip()
                    resSeq = int(line[22:26])
                    chainID = line[20:22].strip()

                    if (resName, resSeq, chainID) in protres:
                        msg_res.add( (resName, resSeq, chainID) )
                        newfile.write(line[:17] +
                                      '{:3s} '.format(self.PROT_MAP[resName]) +
                                      line[21:])
                    else:
                        newfile.write(line)

    for resName, resSeq, chainID in msg_res:
        logger.write('Changing %s %i %s to %s' %
                          (resName, resSeq, chainID, self.PROT_MAP[resName]) )

    self.mol_file = const.PROTONATED_PDB_FILE
Exemple #24
0
def make_ligand(name, ff, opts):
    """
    Prepare ligands for simulation: charge parameters, vacuum top/crd,
    confomer search + alignment (both optional), optionally hydrated
    top/crd and minimisation/MD simulation.

    :param name: the name of the ligandx
    :type name: str
    :param ff: the ForceField class holding all relevant data for setup and
               also simulation
    :type ff: ForceField
    :param opts: the name of the ligandx
    :type opts: IniParser
    """

    logger.write('*** Working on %s ***\n' % name)

    lig = opts[SECT_LIG]

    load_cmds = ''

    if opts[SECT_DEF]['user_params']:
        load_cmds = _param_glob(PARAM_CMDS)

    vac_model_filename = name + const.MODEL_EXT
    sol_model_filename = 'solv_' + name + const.MODEL_EXT
    from_scratch = True

    workdir = os.path.join(os.getcwd(), const.LIGAND_WORKDIR, name)

    if not opts[SECT_DEF]['remake']:
        model_path = \
                   _search_for_model([sol_model_filename, vac_model_filename],
                                     const.LIGAND_WORKDIR)

        # FIXME: check for KeyError
        if model_path:
            model = read_model(model_path)
            name = model['name']

            # FIXME: only extract when const.LIGAND_WORKDIR not present?
            model.extract(direc = workdir)

            ligand = ff.Ligand(name)

            # invoke context manager to preset directory for absolute file
            # creation below
            with DirManager(workdir):
                logger.write('Found model %s, extracting data' % name)

                ligand.charge = float(model['charge.total'])
                ligand.gaff = model['forcefield']
                ligand.amber_top = model['top.filename']
                ligand.amber_crd = model['crd.filename']
                ligand.orig_file = model['crd.original']
                ligand.mol_file = ligand.orig_file
                ligand.mol_fmt ='mol2'

                # this file will not be created when skip_param = True
                try:
                    ligand.frcmod = model['frcmod']
                except KeyError:
                    pass

                if 'box.dimensions' in model:
                    ligand.box_dims = [float(b) for b in
                                       model['box.dimensions'].strip('[]')\
                                       .split(',')]

                if lig['morph.absolute'] and \
                       opts[SECT_DEF]['AFE.type'] == 'Sire':
                    logger.write('Creating input files for absolute '
                                 'transformations with Sire')
                    ligand.create_absolute_Sire()

            if os.path.basename(model_path) == vac_model_filename:
                from_scratch = False
            else:
                return ligand, load_cmds

    print('Making ligand %s...' % name)

    if not lig['basedir']:
        raise dGprepError('[%s] "basedir" must be set' % SECT_LIG)

    if not lig['file.format']:
        fmt = os.path.splitext(lig['file.name'])[1][1:]
    else:
        fmt = lig['file.format']

    if from_scratch:
        model = ModelConfig(name)
        ligand = ff.Ligand(name, lig['file.name'], fmt)

    # this file will not be created when skip_param = True
    if os.path.isfile(ligand.frcmod):
        model['frcmod'] = ligand.frcmod
        model.add_file(ligand.frcmod)

    if os.path.isabs(lig['basedir']):
        src = os.path.join(lig['basedir'], name)
    else:
        src = os.path.join(os.getcwd(), lig['basedir'], name)

    with DirManager(workdir):
        if from_scratch:
            ligand.copy_files((src,), None, opts[SECT_DEF]['overwrite'])

            if not os.access(lig['file.name'], os.F_OK):
                raise errors.SetupError('start file %s does not exist in %s' %
                                        (lig['file.name'], os.getcwd() ) )

            if lig['skip_param']:
                if fmt != 'pdb' and fmt != 'mol2':
                    raise dGprepError('When parameterisation is skipped, the input '
                                      'format must be PDB or MOL2')

                ligand.prepare('', lig['add_hydrogens'], lig['calc_charge'],
                               lig['correct_for_pH'], lig['pH'])
            elif not os.access(os.path.join(workdir, const.GAFF_MOL2_FILE),
                               os.F_OK):
                # IMPORTANT: do not allow OpenBabel to add Hs, it may mess up
                # everything
                ligand.prepare('mol2', lig['add_hydrogens'], lig['calc_charge'],
                               lig['correct_for_pH'], lig['pH'])
                ligand.param(lig['gb_charges'])
            else: # FIXME: ugly
                ligand.prepare('', lig['add_hydrogens'], lig['calc_charge'],
                               lig['correct_for_pH'], lig['pH'])
                ligand.mol_file = const.GAFF_MOL2_FILE
                ligand.mol_fmt = 'mol2'

            ligand.prepare_top()
            ligand.create_top(boxtype='', addcmd=load_cmds,
                              write_dlf=lig['write_dlf'])

            # this file will not be created when skip_param = True
            if os.path.isfile(const.LIGAND_AC_FILE):
                model['charge.filename'] = const.LIGAND_AC_FILE
                model.add_file(const.LIGAND_AC_FILE)

            model['charge.total'] = ligand.charge
            model['charge.filetype'] = 'ac'
            model['charge.method'] = 'AM1-BCC'
            model['forcefield'] = ligand.gaff
            model['molecule.type'] = 'ligand'

            model.add_file(ligand.mol_file)
            model['crd.original'] = ligand.mol_file

            save_model(model, ligand, vac_model_filename, '..')

            if opts[SECT_DEF]['MC_prep']:
                ligand.flex()

            nconf = lig['conf_search.numconf']

            if lig['morph.absolute'] and opts[SECT_DEF]['AFE.type'] == 'Sire':
                ligand.create_absolute_Sire()

            if nconf > 0:
                ligand.conf_search(numconf = nconf,
                                   geomsteps = lig['conf_search.geomsteps'],
                                   steep_steps = lig['conf_search.steep_steps'],
                                   steep_econv = lig['conf_search.steep_econv'],
                                   conj_steps = lig['conf_search.conj_steps'],
                                   conj_econv = lig['conf_search.conj_econv'],
                                   ffield = lig['conf_search.ffield'])
                ligand.align()

        # FIXME: also check for boxlength and neutralize
        if lig['box.type']:
            ligand.prepare_top()
            ligand.create_top(boxtype = lig['box.type'],
                              boxlength = lig['box.length'],
                              neutralize = lig['neutralize'],
                              addcmd = load_cmds, remove_first = False)

            if lig['ions.conc'] > 0.0:
                ligand.create_top(boxtype = lig['box.type'],
                                  boxlength = lig['box.length'],
                                  neutralize = 2,
                                  addcmd = load_cmds, remove_first = False,
                                  conc = lig['ions.conc'],
                                  dens = lig['ions.dens'])

            restr_force = lig['min.restr_force']
            nsteps = lig['min.nsteps']

            ligand.setup_MDEngine(opts[SECT_DEF]['mdengine'][1],
                                  opts[SECT_DEF]['mdengine.prefix'],
                                  opts[SECT_DEF]['mdengine.postfix'])

            if nsteps > 0:
                do_min(ligand, lig)

            press_done = False
            nsteps = lig['md.heat.nsteps']

            if nsteps > 0:
                restr_force = lig['md.heat.restr_force']
                do_md(ligand, lig, 'heat')

            nsteps = lig['md.constT.nsteps']

            if nsteps > 0:
                restr_force = lig['md.constT.restr_force']
                do_md(ligand, lig, 'constT')

            nsteps = lig['md.press.nsteps']

            if nsteps > 0:
                restr_force = lig['md.press.restr_force']
                do_md(ligand, lig, 'press')
                press_done = True

            nrestr = lig['md.relax.nrestr']

            if nrestr > 0:
                # FIXME: unify with AMBER mdengine
                if opts[SECT_DEF]['mdengine'][0] == 'namd':
                    ligand.md('%RELRES', lig['md.relax.nsteps'],
                              lig['md.relax.T'], lig['md.relax.p'],
                              lig['md.relax.restraint'], restr_force,
                              nrestr, wrap = True)
                else:
                    sp = restr_force / (nrestr - 1)

                    for k in range(nrestr - 2, -1, -1):
                        if press_done:
                            nmlist = '%PRESS'
                        else:
                            nmlist = '%CONSTT'

                        ligand.md(nmlist, lig['md.relax.nsteps'],
                                  lig['md.relax.T'], lig['md.relax.p'],
                                  lig['md.relax.restraint'], sp * k,
                                  wrap = True)

            if opts[SECT_DEF]['mdengine'][0] != 'amber':
                if _minmd_done(lig):
                   ligand.to_rst7()

            save_model(model, ligand, sol_model_filename, '..')

    return ligand, load_cmds
Exemple #25
0
    def md(self,
           config='',
           nsteps=1000,
           T=300.0,
           p=1.0,
           mask='',
           restr_force=5.0,
           nrel=1,
           wrap=True,
           dt=0.002):
        """
        Run molecular dynamics on a system.

        :param config: MD paramers, if start with '%' respective default is
           chosen, otherwise a full mdrun input as string
        :type config: string
        :param nsteps: maximum number of MD steps
        :type nsteps: integer
        :param T: temperature
        :type T: float
        :param p: pressure
        :type p: float
        :param mask: pre-defined restraints or AMBER mask
        :type mask: string
        :param restr_force: force constant for the restraints in kcal/mol/AA
        :type restr_force: float
        :param nrel: number of restraint relaxation steps
        :type nrel: integer
        :param wrap: wrap coordinates in a periodic system
        :type wrap: bool
        :param dt: timestep in ps
        :type dt: float
        :raises: SetupError
        """

        suffix = '%05i' % self.run_no
        cfg = config

        if config[0] == '%':
            pname = config[1:]
            logger.write('Running MD (%s) with protocol %s' % (suffix, pname))

            try:
                config = PROTOCOLS['MD_%s' % pname]
            except KeyError:
                raise errors.SetupError('no such MD protocol predefined: '
                                        '%s' % config)

        p /= const.ATM2BAR * 1000  # convert to kbar

        # DL_POLY does not have a built-in heating protocol
        if cfg == '%HEAT':
            ninc = int(NO_STEPS_PER_100DEG * T / 100.0)
            tinc = T / (ninc - 1)
            nst = nsteps / ninc

            for i in range(0, ninc):
                if i == 0:
                    T_curr = START_T
                else:
                    T_curr = i * tinc

                ctrl = config.format(T_curr, nst, nst / 5, nst / 10, nst / 5,
                                     p, dt)

                self._run_mdprog(suffix, ctrl, mask, restr_force)

                suffix = '%05i' % self.run_no
        else:
            ctrl = config.format(T, nsteps, nsteps / 5, nsteps / 10,
                                 nsteps / 5, p, dt)

            self._run_mdprog(suffix, ctrl, mask, restr_force)
Exemple #26
0
def align(self, inc_hyd = False, symmetry = False, filt = False):
    """
    Align two structures via OpenBabel.

    :param inc_hyd: include hydrogens
    :type inc_hyd: bool
    :param symmetry: consider symmetry of the molecule
    :type symmetry: bool
    :param filt: invoke primitive filter
    :type filt: bool
    :raises: SetupError
    """

    if not self.ref_file:
        raise errors.SetupError('reference file not set yet')

    if self.ref_file ==  self.mol_file:
        logger.write('Identical files: will not perform alignment')
        return


    conv = ob.OBConversion()
    conv.SetInAndOutFormats(self.ref_fmt, self.mol_fmt)

    ref = ob.OBMol()
    tgt = ob.OBMol()

    # ignore warning messages about non-standard PDB
    errlev = ob.obErrorLog.GetOutputLevel()
    ob.obErrorLog.SetOutputLevel(0)

    conv.ReadFile(ref, self.ref_file)
    conv.ReadFile(tgt, self.mol_file)

    ob.obErrorLog.SetOutputLevel(errlev)


    if filt:
        # delete unwanted atoms in reference
        delat = []

        for atom in ob.OBMolAtomIter(ref):
            resname = atom.GetResidue().GetName()

            # FIXME: replace with proper function
            if resname in const.IGNORE_RESIDUES:
                delat.append(atom)

        ref.BeginModify()

        for atom in delat:
            ref.DeleteAtom(atom, True)

        ref.EndModify()

        # copy wanted atoms from target to new OBMol
        cpy = ob.OBMol()

        for atom in ob.OBMolAtomIter(tgt):
            resname = atom.GetResidue().GetName()

            # FIXME: replace with proper function
            if resname not in const.IGNORE_RESIDUES:
                # NOTE: this copies only some info but incl. coordinates
                cpy.AddAtom(atom)

        molecs = ob.OBAlign(ref, cpy, inc_hyd, symmetry)
    else:
        molecs = ob.OBAlign(ref, tgt, inc_hyd, symmetry)

    logger.write('Aligning %s with %s as reference' %
                      (self.mol_file, self.ref_file) )

    if not molecs.Align():
        logger.write('Alignment failed')
        return

    logger.write('RMSD is %.2f' % molecs.GetRMSD() )

    if filt:
        rotate = molecs.GetRotMatrix()
        molecs.UpdateCoords(ref)
        first = True

        for atom in ob.OBMolAtomIter(tgt):
            tmpvec = ob.vector3(atom.GetVector())
            tmpvec *= rotate

            if first:
                # we obviously can't do this directly: OB's vector3 does not
                # support the '-' but only the '-=' operator.  But the
                # latter leads to a memory corruption bug...
                # shift = ref.GetAtom(1).GetVector()
                # shift -= cpy.GetAtom(1).GetVector()

                # we assume atoms 1 are equivalent in both molecules...
                v1 = ref.GetAtom(1).GetVector()
                v2 = cpy.GetAtom(1).GetVector()

                x = v1.GetX() - v2.GetX()
                y = v1.GetY() - v2.GetY()
                z = v1.GetZ() - v2.GetZ()

                shift = ob.vector3(x, y, z)
                first = False

            tmpvec += shift
            atom.SetVector(tmpvec)

    else:
        if not molecs.UpdateCoords(tgt):
            logger.write('Coordinate update failed')
            return

    try:
        conv.WriteFile(tgt, self.mol_file)
    except IOError as why:
        raise errors.SetupError(why)

    self.mol_atomtype = 'sybyl'
Exemple #27
0
    options.parse(args.infile, 'globals')

    # backward compatibility
    if options[SECT_DEF]['FE_type']:
        options[SECT_DEF]['AFE.type'] = options[SECT_DEF]['FE_type']

    if options[SECT_DEF]['gaff'] == 'gaff1':
        options[SECT_DEF]['gaff'] = 'gaff'


    for section in ALL_SECTIONS:
        check_dict(section, options)

    ff = prelude(options)

    logger.write('Command line: %s\n\nOptions:\n--------' % ' '.join(sys.argv))
    logger.write('\n'.join(options.format()))
    logger.write('--------\n\nForce field and MD engine:\n%s\n' % ff)


    # FIXME: We keep all molecule objects in memory.  For 2000 morph pairs
    #        this may mean more than 1 GB on a 64 bit machine.

    ### proteins

    proteins = {}
    prot_failed = []

    for prot_name in options[SECT_PROT]['molecules']:
        try:
            protein, cmds = make_protein(prot_name, ff, options)
Exemple #28
0
def make_pert_file(old_morph,
                   new_morph,
                   stepname,
                   qprop0,
                   qprop1,
                   LJprop0,
                   LJprop1,
                   atprop0,
                   atprop1,
                   lig_initial,
                   lig_final,
                   atoms_final,
                   atom_map,
                   reverse_atom_map,
                   zz_atoms,
                   qonly,
                   turnoffdummyangles=False,
                   shrinkdummybonds=False,
                   zero_dih_dummies=False):
    """
    Create a perturbation file for Sire.

    :param old_morph: the original morph molecule
    :type old_morph: Sire.Mol.Molecule
    :param new_morph: a new morph molecule for manipulations
    :type new_morph: Sire.Mol.Molecule
    :param stepname: name of the current morph step
    :type stepname: str
    :param qprop0: name of inital charge property
    :type qprop0: str
    :param qprop1: name of final charge property
    :type qprop1: str
    :param LJprop0: name of inital LJ property
    :type LJprop0: str
    :param LJprop1: name of final LJ property
    :type LJprop1: str
    :param atprop0: name of inital atom type property
    :type atprop0: str
    :param atprop1: name of final atom type property
    :type atprop1: str
    :param lig_initial: the initial state molecule
    :type lig_initial: Sire.Mol.Molecule
    :param lig_final: the final state molecule
    :type lig_final: Sire.Mol.Molecule
    :param atoms_final: set of final atoms
    :type atoms_final: Sire.Mol.Selector_Atom
    :param atom_map: the forward atom map
    :type atom_map: dict of _AtomInfo to _AtomInfo
    :param reverse_atom_map: the reverse atom map
    :type reverse_atom_map: dict of _AtomInfo to _AtomInfo
    :param zz_atoms: rename atoms in list to 'zz' to circumvent leap valency check
    :type zz_atoms: list of Sire.Mol.AtomName
    :param charge_only: write only charges or also vdW+bonded terms
    :type charge_only: bool
    :param turnoffdummyangles: turn off dummy angles
    :type turnoffdummyangles: bool
    :param shrinkdummybonds: shrink dummy bonds
    :type shrinkdummybonds: bool
    :param zero_dih_dummies: use zero dihedrals and impropers when all atoms are
    dummies
    :type zero_dih_dummies: bool
    :raises: SetupError
    """

    # FIXME: change name according to step protocol
    pert_fname = const.MORPH_NAME + os.extsep + stepname + os.extsep + 'pert'
    logger.write('Writing perturbation file %s...\n' % pert_fname)

    pertfile = open(pert_fname, 'w')

    outstr = 'version 1\n'
    outstr += 'molecule %s\n' % (const.LIGAND_NAME)
    pertfile.write(outstr)

    # Write atom perts
    morph_natoms = old_morph.nAtoms()

    for atom in old_morph.atoms():
        outstr = ''

        #if ((atom.property(atprop0) !=
        #     atom.property(atprop1))
        #    or (atom.property(LJprop0) !=
        #        atom.property(LJprop1))):

        outstr += '\t\tinitial_type    %s\n' % atom.property(atprop0)
        outstr += '\t\tfinal_type      %s\n' % atom.property(atprop1)
        outstr += '\t\tinitial_LJ     %8.5f %8.5f\n' % (atom.property(
            LJprop0).sigma().value(), atom.property(LJprop0).epsilon().value())
        outstr += '\t\tfinal_LJ       %8.5f %8.5f\n' % (atom.property(
            LJprop1).sigma().value(), atom.property(LJprop1).epsilon().value())

        #if (atom.property(qprop0) != atom.property(qprop1)):
        if qprop0 == 'zero_all' and qprop1 == 'zero_all':
            outstr += '\t\tinitial_charge  0.0\n'
            outstr += '\t\tfinal_charge    0.0\n'
        elif qprop1 == 'zero_all':
            outstr += '\t\tinitial_charge %8.5f\n' % \
                      atom.property(qprop0).value()
            outstr += '\t\tfinal_charge    0.0\n'
        elif qprop0 == 'zero_all':
            outstr += '\t\tinitial_charge  0.0\n'
            outstr += '\t\tfinal_charge   %8.5f\n' % \
                      atom.property(qprop1).value()
        elif qprop1 == 'zero_dummy':
            pass
        elif qprop0 == 'zero_dummy':
            pass
        else:
            outstr += '\t\tinitial_charge %8.5f\n' % \
                      atom.property(qprop0).value()
            outstr += '\t\tfinal_charge   %8.5f\n' % \
                      atom.property(qprop1).value()

        if outstr:
            atom_name = '\t\tname %s\n' % atom.name().value()
            pertfile.write('\tatom\n' + atom_name + outstr + '\tendatom\n')

    if qonly:
        pertfile.write('endmolecule\n')
        pertfile.close()
        return

    # Figure out which bond, angles, dihedrals have their potential variable
    params_initial = lig_initial.property('amberparameters')
    bonds_initial = params_initial.getAllBonds()
    angles_initial = params_initial.getAllAngles()
    dihedrals_initial = params_initial.getAllDihedrals()
    impropers_initial = params_initial.getAllImpropers()

    params_final = lig_final.property('amberparameters')
    bonds_final = params_final.getAllBonds()
    angles_final = params_final.getAllAngles()
    dihedrals_final = params_final.getAllDihedrals()
    impropers_final = params_final.getAllImpropers()

    params_morph = new_morph.property('amberparameters')
    bonds_morph = params_morph.getAllBonds()
    angles_morph = params_morph.getAllAngles()
    dihedrals_morph = params_morph.getAllDihedrals()
    impropers_morph = params_morph.getAllImpropers()

    # For each pair of atoms making a bond in the morph we find
    # the equivalent pair in the initial topology. If there
    # are no matches this should be because one of the two atoms is a dummy
    # atom. If not, this sounds like a bug and the code aborts.

    for bond in bonds_morph:
        mpot = params_morph.getParams(bond)

        idummy = False
        fdummy = False
        ipot = None

        at0 = new_morph.select(bond.atom0())
        at1 = new_morph.select(bond.atom1())

        at0i = at0.index()
        at1i = at1.index()

        for ibond in bonds_initial:
            iat0 = lig_initial.select(ibond.atom0()).index()
            iat1 = lig_initial.select(ibond.atom1()).index()

            if ((at0i == iat0 and at1i == iat1)
                    or (at0i == iat1 and at1i == iat0)):
                ipot = params_initial.getParams(ibond)
                break

        if not ipot:
            if (at0.name().value().startsWith('DU')
                    or at1.name().value().startsWith('DU')):
                ipot = 'todefine'
                idummy = True
            else:
                raise errors.SetupError('Could not locate bond parameters for '
                                        'the final state. This is most likely '
                                        'because the atom mapping would open '
                                        'up a ring in the intial state. ')

        fpot = None

        map_at0 = util.search_atom(at0i, atom_map)
        map_at1 = util.search_atom(at1i, atom_map)

        for fbond in bonds_final:
            fat0 = lig_final.select(fbond.atom0())
            fat1 = lig_final.select(fbond.atom1())

            if ((map_at0 == fat0 and map_at1 == fat1)
                    or (map_at0 == fat1 and map_at1 == fat0)):
                fpot = params_final.getParams(fbond)
                break

        if fpot is None:
            if (not map_at0 or not map_at1):
                fpot = 'todefine'
                fdummy = True
            else:
                raise errors.SetupError('Could not locate bond parameters for '
                                        'the final state. This is most likely '
                                        'because the atom mapping would open '
                                        'up a ring in the intial state.')

        samepotential = _isSameBondAnglePotential(ipot, fpot)

        if (not samepotential and ipot != 'todefine'
                and not _isSameBondAnglePotential(ipot, mpot)):
            if (at0.name().value() in zz_atoms
                    or at1.name().value() in zz_atoms):
                samepotential = False
            else:
                raise errors.SetupError('The initial and morph potentials '
                                        'are different, but the potential '
                                        'does not involve a dummy atom. '
                                        'This case is not handled by the '
                                        'code.')

        #
        # If either atom in the initial/final states is a dummy, then the
        # parameters are kept constant throughout the perturbation
        #
        if idummy and fdummy:
            raise errors.SetupError('BUG: Both the initial and final states '
                                    'involve dummy atoms. (bond)')
        elif idummy:
            ipot = fpot
        elif fdummy:
            fpot = ipot

        if (idummy or fdummy) or (not samepotential):
            i_force = ipot[0]
            i_eq = ipot[1]
            f_force = fpot[0]
            f_eq = fpot[1]

            if shrinkdummybonds and idummy:
                i_eq = 0.2  # Angstrom
            if shrinkdummybonds and fdummy:
                f_eq = 0.2  # Angstrom

            outstr = '\tbond\n'
            outstr += '\t\tatom0   %s\n' % at0.name().value()
            outstr += '\t\tatom1   %s\n' % at1.name().value()
            outstr += '\t\tinitial_force %s\n' % i_force
            outstr += '\t\tinitial_equil %s\n' % i_eq
            outstr += '\t\tfinal_force   %s\n' % f_force
            outstr += '\t\tfinal_equil   %s\n' % f_eq
            outstr += '\tendbond\n'

            pertfile.write(outstr)

    # Now angles...

    for angle in angles_morph:
        mpot = params_morph.getParams(angle)

        idummy = False
        fdummy = False
        ipot = None

        at0 = new_morph.select(angle.atom0())
        at1 = new_morph.select(angle.atom1())
        at2 = new_morph.select(angle.atom2())

        at0i = at0.index()
        at1i = at1.index()
        at2i = at2.index()

        for iangle in angles_initial:
            iat0 = lig_initial.select(iangle.atom0()).index()
            iat1 = lig_initial.select(iangle.atom1()).index()
            iat2 = lig_initial.select(iangle.atom2()).index()

            if ((at0i == iat0 and at1i == iat1 and at2i == iat2)
                    or (at0i == iat2 and at1i == iat1 and at2i == iat0)):
                ipot = params_initial.getParams(iangle)
                break

        if ipot is None:
            if (at0.name().value().startsWith('DU')
                    or at1.name().value().startsWith('DU')
                    or at2.name().value().startsWith('DU')):
                ipot = 'todefine'
                idummy = True
            else:
                raise errors.SetupError(
                    'can not determine ipot for angle %s ' % angle)

        fpot = None
        map_at0 = util.search_atom(at0i, atom_map)
        map_at1 = util.search_atom(at1i, atom_map)
        map_at2 = util.search_atom(at2i, atom_map)

        for fangle in angles_final:
            fat0 = lig_final.select(fangle.atom0())
            fat1 = lig_final.select(fangle.atom1())
            fat2 = lig_final.select(fangle.atom2())

            if ((map_at0 == fat0 and map_at1 == fat1 and map_at2 == fat2) or
                (map_at0 == fat2 and map_at1 == fat1 and map_at2 == fat0)):
                fpot = params_final.getParams(fangle)
                break

        if fpot is None:
            if (not map_at0 or not map_at1 or not map_at2):
                fpot = 'todefine'
                fdummy = True
            else:
                raise errors.SetupError(
                    'can not determine fpot for angle %s ' % angle)

        samepotential = _isSameBondAnglePotential(ipot, fpot)

        if (not samepotential and ipot != 'todefine'
                and (not _isSameBondAnglePotential(ipot, mpot))):
            if (at0.name().value() not in zz_atoms
                    or at1.name().value() not in zz_atoms
                    or at2.name().value() not in zz_atoms):
                samepotential = False
            else:
                raise errors.SetupError('BUG: The initial and morph angles '
                                        'are different, but the potential does'
                                        'involve a dummy atom.')

        if idummy and fdummy:
            # This happens when we create 1-3 interactions involving two groups
            # of dummy atoms with some dummy in the initial state and some dummy
            # in the final state.  The best option is to use null parameters so
            # as not to artificially restraint the morph
            ipot = (0, 0)
            fpot = ipot
        elif idummy:
            ipot = fpot
        elif fdummy:
            fpot = ipot

        if (idummy or fdummy) or (not samepotential):
            i_force = ipot[0]
            i_eq = ipot[1]
            f_force = fpot[0]
            f_eq = fpot[1]

            if turnoffdummyangles and idummy:
                if (not at0.name().value().startsWith('DU')
                        or not at2.name().value().startsWith('DU')):
                    i_force = 0.0
            if turnoffdummyangles and fdummy:
                if (not at0.name().value().startsWith('DU')
                        or not at2.name().value().startsWith('DU')):
                    i_force = 0.0

            outstr = '\tangle\n'
            outstr += '\t\tatom0   %s\n' % at0.name().value()
            outstr += '\t\tatom1   %s\n' % at1.name().value()
            outstr += '\t\tatom2   %s\n' % at2.name().value()
            outstr += '\t\tinitial_force %s\n' % i_force
            outstr += '\t\tinitial_equil %s\n' % i_eq
            outstr += '\t\tfinal_force   %s\n' % f_force
            outstr += '\t\tfinal_equil   %s\n' % f_eq
            outstr += '\tendangle\n'

            pertfile.write(outstr)

    # Now dihedrals...
    #
    # JM 03/13
    # Problem: there could be some dihedrals/impropers that are not contained
    # in dihedrals_morph or dihedrals_initial
    # BUT do exist in dihedrals_final. These cases can be detected by looking
    # for dihedrals in the final topology that haven't been matched to
    # dihedrals in the morph
    #

    unmapped_fdihedrals = dihedrals_final

    for dihedral in dihedrals_morph:
        mpot = params_morph.getParams(dihedral)

        idummy = False
        fdummy = False
        allidummy = False
        allfdummy = False
        ipot = None

        at0 = new_morph.select(dihedral.atom0())
        at1 = new_morph.select(dihedral.atom1())
        at2 = new_morph.select(dihedral.atom2())
        at3 = new_morph.select(dihedral.atom3())

        at0i = at0.index()
        at1i = at1.index()
        at2i = at2.index()
        at3i = at3.index()

        for idihedral in dihedrals_initial:
            iat0 = lig_initial.select(idihedral.atom0()).index()
            iat1 = lig_initial.select(idihedral.atom1()).index()
            iat2 = lig_initial.select(idihedral.atom2()).index()
            iat3 = lig_initial.select(idihedral.atom3()).index()

            if ((at0i == iat0 and at1i == iat1 and at2i == iat2
                 and at3i == iat3) or (at0i == iat3 and at1i == iat2
                                       and at2i == iat1 and at3i == iat0)):
                ipot = params_initial.getParams(idihedral)

                break

        if not ipot:
            is_dummy = [
                at.name().value().startsWith('DU')
                for at in (at0, at1, at2, at3)
            ]

            if any(is_dummy):
                ipot = 'todefine'
                idummy = True
            else:
                raise errors.SetupError(
                    'Could not locate torsion parameters for '
                    'the final state. This is most likely '
                    'because the atom mapping would open '
                    'up a ring in the intiial state.')

            allidummy = all(is_dummy)

        fpot = None

        map_at0 = util.search_atom(at0i, atom_map)
        map_at1 = util.search_atom(at1i, atom_map)
        map_at2 = util.search_atom(at2i, atom_map)
        map_at3 = util.search_atom(at3i, atom_map)

        for fdihedral in dihedrals_final:
            fat0 = lig_final.select(fdihedral.atom0())
            fat1 = lig_final.select(fdihedral.atom1())
            fat2 = lig_final.select(fdihedral.atom2())
            fat3 = lig_final.select(fdihedral.atom3())

            if ((map_at0 == fat0 and map_at1 == fat1 and map_at2 == fat2
                 and map_at3 == fat3)
                    or (map_at0 == fat3 and map_at1 == fat2 and map_at2 == fat1
                        and map_at3 == fat0)):
                fpot = params_final.getParams(fdihedral)
                unmapped_fdihedrals.remove(fdihedral)
                break

        if not fpot:
            if not map_at0 or not map_at1 or not map_at2 or not map_at3:
                fpot = 'todefine'
                fdummy = True
            else:
                # This could be because the dihedral has a 0 force constant and
                # was not loaded from the top file. Flaw in the amber top parser?
                raise errors.SetupError(
                    'Cannot determine fpot for dihedral %s' % dihedral)

            # Check if all atoms are dummies in final torsion
            allfdummy = (not map_at0 and not map_at1 and not map_at2
                         and not map_at3)

        samepotential = _isSameDihedralPotential(ipot, fpot)

        if (not samepotential and ipot != 'todefine'
                and (not _isSameDihedralPotential(ipot, mpot))):
            if (at0.name().value() not in zz_atoms
                    or at1.name().value() not in zz_atoms
                    or at2.name().value() not in zz_atoms
                    or at3.name().value() not in zz_atoms):
                samepotential = False
            else:
                raise errors.SetupError(
                    'BUG: The initial and morph dihedrals '
                    'are different, but the potential does '
                    'not involve a dummy atom.')

        #
        # Unlike bonds/angles, dihedrals with dummies have no energy
        #
        if idummy and fdummy:
            # JM 03/13 This can happen in some morphs. Then use a null potential
            # throughout.
            ipot = [0.0, 0.0, 0.0]
            fpot = [0.0, 0.0, 0.0]
        elif idummy:
            if allidummy and not zero_dih_dummies:
                ipot = fpot
            else:
                ipot = [0.0, 0.0, 0.0]
        elif fdummy:
            if allfdummy and not zero_dih_dummies:
                fpot = ipot
            else:
                fpot = [0.0, 0.0, 0.0]

        # leap creates for some unkown reason zero torsions
        if len(ipot) == 3 and len(fpot) == 3 and \
               ipot[0] == 0.0 and fpot[0] == 0.0:
            continue

        if (idummy or fdummy) or (not samepotential):
            outstr = '\tdihedral\n'
            outstr += '\t\tatom0   %s\n' % at0.name().value()
            outstr += '\t\tatom1   %s\n' % at1.name().value()
            outstr += '\t\tatom2   %s\n' % at2.name().value()
            outstr += '\t\tatom3   %s\n' % at3.name().value()
            outstr += '\t\tinitial_form %s\n' % ' '.join(
                [str(i) for i in ipot])
            outstr += '\t\tfinal_form %s\n' % ' '.join([str(f) for f in fpot])
            outstr += '\tenddihedral\n'

            pertfile.write(outstr)

    if unmapped_fdihedrals:
        logger.write("\ndihedrals in the final topology that haven't been "
                     "mapped to the initial topology:")
        logger.write(unmapped_fdihedrals)

    for fdihedral in unmapped_fdihedrals:
        fat0 = lig_final.select(fdihedral.atom0()).index()
        fat1 = lig_final.select(fdihedral.atom1()).index()
        fat2 = lig_final.select(fdihedral.atom2()).index()
        fat3 = lig_final.select(fdihedral.atom3()).index()

        fpot = params_final.getParams(fdihedral)
        ipot = [0.0, 0.0, 0.0]

        reversemap_at0 = util.search_atom(fat0, reverse_atom_map)
        reversemap_at1 = util.search_atom(fat1, reverse_atom_map)
        reversemap_at2 = util.search_atom(fat2, reverse_atom_map)
        reversemap_at3 = util.search_atom(fat3, reverse_atom_map)

        outstr = '\tdihedral\n'
        outstr += '\t\tatom0   %s\n' % reversemap_at0.value()
        outstr += '\t\tatom1   %s\n' % reversemap_at1.value()
        outstr += '\t\tatom2   %s\n' % reversemap_at2.value()
        outstr += '\t\tatom3   %s\n' % reversemap_at3.value()
        outstr += '\t\tinitial_form %s\n' % ' '.join([str(i) for i in ipot])
        outstr += '\t\tfinal_form %s\n' % ' '.join([str(f) for f in fpot])
        outstr += '\tenddihedral\n'

        pertfile.write(outstr)

    #
    # Now impropers...
    #

    unmapped_fimpropers = impropers_final
    unmapped_iimpropers = impropers_initial

    logger.write('\nimpropers:')

    for improper in impropers_morph:
        logger.write(improper)

        mpot = params_morph.getParams(improper)
        idummy = False
        fdummy = False
        allidummy = False
        allfdummy = False
        ipot = None

        at0 = new_morph.select(improper.atom0())
        at1 = new_morph.select(improper.atom1())
        at2 = new_morph.select(improper.atom2())
        at3 = new_morph.select(improper.atom3())

        at0i = at0.index()
        at1i = at1.index()
        at2i = at2.index()
        at3i = at3.index()

        for iimproper in impropers_initial:
            iat0 = lig_initial.select(iimproper.atom0()).index()
            iat1 = lig_initial.select(iimproper.atom1()).index()
            iat2 = lig_initial.select(iimproper.atom2()).index()
            iat3 = lig_initial.select(iimproper.atom3()).index()

            # Need different matching rules
            if ((at0i == iat0 or at0i == iat1 or at0i == iat2 or at0i == iat3)
                    and
                (at1i == iat0 or at1i == iat1 or at1i == iat2 or at1i == iat3)
                    and
                (at2i == iat0 or at2i == iat1 or at2i == iat2 or at2i == iat3)
                    and (at3i == iat0 or at3i == iat1 or at3i == iat2
                         or at3i == iat3)):
                ipot = params_initial.getParams(iimproper)
                unmapped_iimpropers.remove(iimproper)
                break

        if not ipot:
            is_dummy = [
                at.name().value().startsWith('DU')
                for at in (at0, at1, at2, at3)
            ]

            if any(is_dummy):
                ipot = 'todefine'
                idummy = True
            else:
                raise errors.SetupError(
                    'Could not locate improper parameters for '
                    'the final state. This is most likely '
                    'because the atom mapping would open '
                    'up a ring in the intiial state.')
            allidummy = all(is_dummy)

        fpot = None

        map_at0 = util.search_atom(at0i, atom_map)
        map_at1 = util.search_atom(at1i, atom_map)
        map_at2 = util.search_atom(at2i, atom_map)
        map_at3 = util.search_atom(at3i, atom_map)

        for fimproper in impropers_final:
            fat0 = lig_final.select(fimproper.atom0())
            fat1 = lig_final.select(fimproper.atom1())
            fat2 = lig_final.select(fimproper.atom2())
            fat3 = lig_final.select(fimproper.atom3())
            # The two matching rules below are to catch impropers that have been
            # defined by walking around the ring in a reverse order as in the
            # initial ligand
            # There are many equivalent ways of defining an improper
            #
            #       2
            #       |
            #       1
            #      / \
            #     0   3
            #
            # 0123 *
            # 0132 *
            # 2103 *
            # 2130 *
            # 3120 *
            # 3102 *
            # 3210 *
            # 2310 *
            # 3012 *
            # 0312 *
            # 0213 *
            # 2013 *

            if ((map_at0 == fat0 or map_at0 == fat1 or map_at0 == fat2
                 or map_at0 == fat3)
                    and (map_at1 == fat0 or map_at1 == fat1 or map_at1 == fat2
                         or map_at1 == fat3)
                    and (map_at2 == fat0 or map_at2 == fat1 or map_at2 == fat2
                         or map_at2 == fat3)
                    and (map_at3 == fat0 or map_at3 == fat1 or map_at3 == fat2
                         or map_at3 == fat3)):
                fpot = params_final.getParams(fimproper)
                unmapped_fimpropers.remove(fimproper)

                break

        if not fpot:
            if not map_at0 or not map_at1 or not map_at2 or not map_at3:
                fpot = 'todefine'
                fdummy = True
            else:
                # JM 02/13 Found a case where one final improper does not exist,
                # but it does in the initial state...
                # R-NH2 --> R-NO2 this is at least for the gaff force field. It
                # seems then that a valid option is to guess a null improper in
                # those cases?

                fpot = [0.0, 0.0, 0.0]

            # Check if all atoms are dummies in final torsion
            allfdummy = (not map_at0 and not map_at1 and not map_at2
                         and not map_at3)

        samepotential = _isSameDihedralPotential(ipot, fpot)

        # FIXME: there is a logical problem here!
        if (not samepotential and ipot != 'todefine'
                and (not _isSameDihedralPotential(ipot, mpot))):
            if (at0.name().value() not in zz_atoms
                    or at1.name().value() not in zz_atoms
                    or at2.name().value() not in zz_atoms
                    or at3.name().value() not in zz_atoms):
                samepotential = False
            else:
                raise errors.SetupError(
                    'BUG: The initial and morph impropers '
                    'are different, but the potential does '
                    'not involve a dummy atom.')

        if idummy and fdummy:
            ipot = [0.0, 0.0, 0.0]
            fpot = [0.0, 0.0, 0.0]
        elif idummy:
            if allidummy and not zero_dih_dummies:
                ipot = fpot
            else:
                ipot = [0.0, 0.0, 0.0]
        elif fdummy:
            if allfdummy and not zero_dih_dummies:
                fpot = ipot
            else:
                fpot = [0.0, 0.0, 0.0]

        ipotstr = ''
        for val in ipot:
            ipotstr += '%s ' % val
        fpotstr = ''
        for val in fpot:
            fpotstr += '%s ' % val

        if ((idummy or fdummy) or (not samepotential)):
            outstr = '\timproper\n'
            outstr += '\t\tatom0   %s\n' % at0.name().value()
            outstr += '\t\tatom1   %s\n' % at1.name().value()
            outstr += '\t\tatom2   %s\n' % at2.name().value()
            outstr += '\t\tatom3   %s\n' % at3.name().value()
            outstr += '\t\tinitial_form %s\n' % ipotstr
            outstr += '\t\tfinal_form %s\n' % fpotstr
            outstr += '\tendimproper\n'
            pertfile.write(outstr)

    logger.write("Impropers in the final topology that haven't been mapped to "
                 "the initial topology")
    logger.write(unmapped_fimpropers)

    for fimproper in unmapped_fimpropers:
        fat0 = lig_final.select(fimproper.atom0()).index()
        fat1 = lig_final.select(fimproper.atom1()).index()
        fat2 = lig_final.select(fimproper.atom2()).index()
        fat3 = lig_final.select(fimproper.atom3()).index()

        at0_info = util.search_atominfo(fat0, reverse_atom_map)
        at1_info = util.search_atominfo(fat1, reverse_atom_map)
        at2_info = util.search_atominfo(fat2, reverse_atom_map)
        at3_info = util.search_atominfo(fat3, reverse_atom_map)

        fpot = params_final.getParams(fimproper)

        if (not at0_info.atom and not at1_info.atom and not at2_info.atom
                and not at3_info.atom):
            ipot = fpot
        else:
            ipot = [0.0, 0.0, 0.0]

        outstr = '\timproper\n'
        outstr += '\t\tatom0   %s\n' % at0_info.name.value()
        outstr += '\t\tatom1   %s\n' % at1_info.name.value()
        outstr += '\t\tatom2   %s\n' % at2_info.name.value()
        outstr += '\t\tatom3   %s\n' % at3_info.name.value()
        outstr += '\t\tinitial_form %s\n' % ' '.join(str(i) for i in ipot)
        outstr += '\t\tfinal_form %s\n' % ' '.join(str(f) for f in fpot)
        outstr += '\tendimproper\n'

        pertfile.write(outstr)

    #
    # This happens for for instance acetamide --> acetone.
    # This breaks the assumption of the code that the morph contains all the dofs
    # of the initial state.  It could be because leap applies different rules to
    # decide whether to include impropers in a molecule, and the morph gets a
    # different treatment than for the initial state (owing to dummy atoms atoms)
    #
    # This suggests that a more robust version of the code should scan every
    # initial dof (bond/angle/dihedral/improper) match with final dof, and then
    # build the pert file, using the morph atom names (as opposed to assuming
    # that morph==initial for dofs  and then match final dofs to morph).
    #
    # For now, the code below should fix the missing improper problem
    #
    logger.write("Impropers in the initial topology that haven't been mapped "
                 "to the morph")
    logger.write(unmapped_iimpropers)

    for iimproper in unmapped_iimpropers:
        iat0 = lig_initial.select(iimproper.atom0())
        iat1 = lig_initial.select(iimproper.atom1())
        iat2 = lig_initial.select(iimproper.atom2())
        iat3 = lig_initial.select(iimproper.atom3())

        logger.write('%s %s %s %s' % (iat0, iat1, iat2, iat3))

        ipot = params_initial.getParams(iimproper)
        fpot = [0.0, 0.0, 0.0]

        outstr = '\timproper\n'
        outstr += '\t\tatom0   %s\n' % iat0.name().value()
        outstr += '\t\tatom1   %s\n' % iat1.name().value()
        outstr += '\t\tatom2   %s\n' % iat2.name().value()
        outstr += '\t\tatom3   %s\n' % iat3.name().value()
        outstr += '\t\tinitial_form %s\n' % ' '.join(str(i) for i in ipot)
        outstr += '\t\tfinal_form %s\n' % ' '.join(str(f) for f in fpot)
        outstr += '\tendimproper\n'

        pertfile.write(outstr)

    pertfile.write('endmolecule\n')
    pertfile.close()
Exemple #29
0
def make_protein(name, ff, opts):
    """
    Prepare proteins for simulation.
    """

    logger.write('*** Working on %s ***\n' % name)

    prot = opts[SECT_PROT]

    load_cmds = ''

    if opts[SECT_DEF]['user_params']:
        load_cmds = _param_glob(PARAM_CMDS)

    vac_model_filename = name + const.MODEL_EXT
    sol_model_filename = 'solv_' + name + const.MODEL_EXT
    from_scratch = True

    workdir = os.path.join(os.getcwd(), const.PROTEIN_WORKDIR, name)

    if not opts[SECT_DEF]['remake']:
        model_path = _search_for_model([sol_model_filename, vac_model_filename],
                                       const.PROTEIN_WORKDIR)
        # FIXME: check for KeyError
        if model_path:
            model = read_model(model_path)
            name = model['name']

            logger.write('Found model %s, extracting data' % name)

            # FIXME: only extract when const.PROTEIN_WORKDIR not present?
            model.extract(direc = workdir)

            protein = ff.Protein(name, prot['basedir'])

            protein.charge = float(model['charge.total'])
            protein.amber_top = model['top.filename']
            protein.amber_crd = model['crd.filename']
            protein.orig_file = model['crd.original']

            if 'top.ssbond_file' in model:
                protein.ssbond_file = model['top.ssbond_file']

            if 'box.dimensions' in model:
                protein.box_dims = model['box.dimensions']

            if os.path.basename(model_path) == vac_model_filename:
                from_scratch = False
            else:
                return protein, load_cmds

    print('Making biomolecule %s...' % name)

    if not prot['basedir']:
        raise dGprepError('[%s] "basedir" must be set' % SECT_PROT)

    if os.path.isabs(prot['basedir']):
        src = os.path.join(prot['basedir'], name)
    else:
        src = os.path.join(os.getcwd(), prot['basedir'], name)

    if from_scratch:
        model = ModelConfig(name)
        protein = ff.Protein(name, prot['file.name'])
        model['crd.original'] = protein.mol_file
        model.add_file(protein.mol_file)

    with DirManager(workdir):
        if from_scratch:
            protein.copy_files((src,), None, opts[SECT_DEF]['overwrite'])

            if prot['propka']:
                protein.protonate_propka(pH = prot['propka.pH'])

            protein.get_charge()    # must be done explicitly
            protein.prepare_top()
            protein.create_top(boxtype = '')

            model['charge.total'] = protein.charge
            model['forcefield'] = 'AMBER'    # FIXME
            model['molecule.type'] = 'biomolecule'

            save_model(model, protein, vac_model_filename, '..')

        # FIXME: also check for boxlength and neutralize

        if prot['box.type']:
            protein.prepare_top()
            protein.create_top(boxtype = prot['box.type'],
                               boxlength = prot['box.length'],
                               neutralize = prot['neutralize'],
                               align = prot['align_axes'],
                               addcmd = load_cmds, remove_first = True)

            if prot['ions.conc'] > 0.0:
                protein.create_top(boxtype = prot['box.type'],
                                   boxlength = prot['box.length'],
                                   neutralize = 2,
                                   align = prot['align_axes'],
                                   addcmd = load_cmds, remove_first = False,
                                   conc = prot['ions.conc'],
                                   dens = prot['ions.dens'])

            restr_force = prot['min.restr_force']
            nsteps = prot['min.nsteps']

            protein.setup_MDEngine(opts[SECT_DEF]['mdengine'][1],
                                   opts[SECT_DEF]['mdengine.prefix'],
                                   opts[SECT_DEF]['mdengine.postfix'])

            if nsteps > 0:
                do_min(protein, prot)

            press_done = False

            #protein.md('%SHRINK', 200, 5.0, 1.0, ':LIG', 5.0, wrap = True)

            nsteps = prot['md.heat.nsteps']

            if nsteps > 0:
                restr_force = prot['md.heat.restr_force']
                do_md(protein, prot, 'heat')

            nsteps = prot['md.constT.nsteps']

            if nsteps > 0:
                restr_force = prot['md.constT.restr_force']
                do_md(protein, prot, 'constT')

            nsteps = prot['md.press.nsteps']

            if nsteps > 0:
                restr_force = prot['md.press.restr_force']
                do_md(protein, prot, 'press')
                press_done = True

            nrestr = prot['md.relax.nrestr']

            if nrestr > 0:
                if opts[SECT_DEF]['mdengine'][0] == 'namd':
                    protein.md('%RELRES', prot['md.relax.nsteps'],
                               prot['md.relax.T'], prot['md.relax.p'],
                               prot['md.relax.restraint'], restr_force,
                               nrestr, wrap = True)
                else:
                    sp = restr_force / (nrestr - 1)

                    for k in range(nrestr - 2, -1, -1):
                        if press_done:
                            nmlist = '%PRESS'
                        else:
                            nmlist = '%CONSTT'

                        protein.md(nmlist, prot['md.relax.nsteps'],
                                   prot['md.relax.T'],
                                   prot['md.relax.p'],
                                   prot['md.relax.restraint'], sp * k,
                                   wrap = True)

            if opts[SECT_DEF]['mdengine'][0] != 'amber':
                if _minmd_done(prot):
                   protein.to_rst7()

            save_model(model, protein, sol_model_filename, '..')

    return protein, load_cmds
Exemple #30
0
def make_complex(prot, lig, ff, opts, load_cmds):

    com = opts[SECT_COM]

    name = prot.mol_name + const.PROT_LIG_SEP + lig.mol_name
    vac_model_filename = name + const.MODEL_EXT
    sol_model_filename = 'solv_' + name + const.MODEL_EXT
    from_scratch = True

    workdir = os.path.join(os.getcwd(), const.COMPLEX_WORKDIR, name)

    if not opts[SECT_DEF]['remake']:
        model_path = _search_for_model([sol_model_filename, vac_model_filename],
                                       const.COMPLEX_WORKDIR)

        if model_path:
            model = read_model(model_path)
            name = model['name']

            logger.write('Found model %s, extracting data' % name)

            # FIXME: only extract when const.COMPLEX_WORKDIR not present?
            model.extract(direc = workdir)

            complex = ff.Complex(prot, lig)

            complex.charge = float(model['charge.total'])
            complex.amber_top = model['top.filename']
            complex.amber_crd = model['crd.filename']

            if 'top.ssbond_file' in model:
                complex.ssbond_file = model['top.ssbond_file']

            if 'box.dimensions' in model:
                complex.box_dims = model['box.dimensions']

            if os.path.basename(model_path) == vac_model_filename:
                from_scratch = False
            else:
                return complex, load_cmds

    print('Making complex from %s and %s...' % (prot.mol_name, lig.mol_name))

    if from_scratch:
        model = ModelConfig(name)

    if not model_path:
        complex = ff.Complex(prot, lig)

    lig_src = os.path.join(os.getcwd(), const.LIGAND_WORKDIR, lig.mol_name)
    prot_src = os.path.join(os.getcwd(), const.PROTEIN_WORKDIR, prot.mol_name)

    with DirManager(workdir):
        if from_scratch:
            complex.copy_files((lig_src, prot_src),
                               (ligand.orig_file, ligand.frcmod, protein.orig_file,
                                const.LIGAND_AC_FILE, const.SSBOND_FILE),
                               opts[SECT_DEF]['overwrite'])

            complex.ligand_fmt = lig.mol_fmt
            complex.prepare_top(gaff=options[SECT_DEF]['gaff'])
            complex.create_top(boxtype='', addcmd=load_cmds)

            model['name'] = complex.complex_name
            model['charge.total'] = complex.charge
            model['forcefield'] = 'AMBER'   # FIXME
            model['molecule.type'] = 'complex'  # FIXME

            save_model(model, complex, vac_model_filename, '..')

        # FIXME: also check for boxlength and neutralize
        if com['box.type']:
            complex.prepare_top(gaff=options[SECT_DEF]['gaff'])
            complex.create_top(boxtype=com['box.type'],
                               boxlength=com['box.length'],
                               neutralize=com['neutralize'],
                               align=com['align_axes'],
                               addcmd=load_cmds, remove_first = True)

            if com['ions.conc'] > 0.0:
                complex.create_top(boxtype=com['box.type'],
                                   boxlength=com['box.length'],
                                   neutralize=2,
                                   align=com['align_axes'],
                                   addcmd=load_cmds, remove_first=False,
                                   conc=com['ions.conc'],
                                   dens=com['ions.dens'])

            restr_force = com['min.restr_force']
            nsteps = com['min.nsteps']

            complex.setup_MDEngine(opts[SECT_DEF]['mdengine'][1],
                                   opts[SECT_DEF]['mdengine.prefix'],
                                   opts[SECT_DEF]['mdengine.postfix'])

            if nsteps > 0:
                do_min(complex, com)

            if opts[SECT_DEF]['MC_prep']:
                complex.prot_flex()
                complex.flatten_rings()

            press_done = False

            #complex.md('%SHRINK', 200, 5.0, 1.0, 'bb_lig', 5.0, wrap = True)

            nsteps = com['md.heat.nsteps']

            if nsteps > 0:
                restr_force = com['md.heat.restr_force']
                do_md(complex, com, 'heat')

            nsteps = com['md.constT.nsteps']

            if nsteps > 0:
                restr_force = com['md.constT.restr_force']
                do_md(complex, com, 'constT')

            nsteps = com['md.press.nsteps']

            if nsteps > 0:
                restr_force = com['md.press.restr_force']
                do_md(complex, com, 'press')
                press_done = True

            nrestr = com['md.relax.nrestr']

            if nrestr > 0:
                if opts[SECT_DEF]['mdengine'][0] == 'namd':
                    complex.md('%RELRES', com['md.relax.nsteps'],
                               com['md.relax.T'], com['md.relax.p'],
                               com['md.relax.restraint'], restr_force,
                               nrestr, wrap = True)
                else:
                    sp = restr_force / (nrestr - 1)

                    for k in range(nrestr - 2, -1, -1):
                        if press_done:
                            nmlist = '%PRESS'
                        else:
                            nmlist = '%CONSTT'

                        complex.md(nmlist, com['md.relax.nsteps'],
                                   com['md.relax.T'], com['md.relax.p'],
                                   com['md.relax.restraint'], sp * k,
                                   wrap = True)

            if opts[SECT_DEF]['mdengine'][0] != 'amber':
                if _minmd_done(com):
                    complex.to_rst7()

            save_model(model, complex, sol_model_filename, '..')

    return complex, load_cmds