Example #1
0
File: test.py Project: prokia/htmd
    def test_restrained_dihedrals(self):

        quad = [2, 0, 1, 3]
        angle = np.rad2deg(dihedralAngle(self.h2o2_90.coords[quad, :, 0]))
        self.assertAlmostEqual(89.999544881803772, angle, places=5)

        with TemporaryDirectory(dir=os.getcwd()) as tmpDir:
            self.qm.molecule = self.h2o2_90
            self.qm.theory = 'BLYP'  # HF fails
            self.qm.basis = '3-21G'
            self.qm.optimize = True
            self.qm.restrained_dihedrals = np.array([quad])
            self.qm.directory = tmpDir
            result = self.qm.run()[0]
            self.assertFalse(result.errored)
            angle = np.rad2deg(dihedralAngle(result.coords[quad, :, 0]))
            self.assertAlmostEqual(89.999541178019271, angle, places=5)

        with TemporaryDirectory(dir=os.getcwd()) as tmpDir:
            self.qm.restrained_dihedrals = None
            self.qm.directory = tmpDir
            result = self.qm.run()[0]
            self.assertFalse(result.errored)
            angle = np.rad2deg(dihedralAngle(result.coords[quad, :, 0]))
            self.assertAlmostEqual(179.51690845119924, angle,
                                   places=3)  # Unstable results
Example #2
0
    def test_restrained_dihedrals(self):

        quad = [2, 0, 1, 3]
        angle = np.rad2deg(dihedralAngle(self.h2o2_90.coords[quad, :, 0]))
        self.assertAlmostEqual(89.999544881803772, angle, places=5)

        with TemporaryDirectory(dir=os.getcwd()) as tmpDir:
            self.qm.molecule = self.h2o2_90
            self.qm.theory = 'BLYP' # HF fails
            self.qm.basis = '3-21G'
            self.qm.optimize = True
            self.qm.restrained_dihedrals = np.array([quad])
            self.qm.directory = tmpDir
            result = self.qm.run()[0]
            self.assertFalse(result.errored)
            angle = np.rad2deg(dihedralAngle(result.coords[quad, :, 0]))
            self.assertAlmostEqual(89.999541178019271, angle, places=5)

        with TemporaryDirectory(dir=os.getcwd()) as tmpDir:
            self.qm.restrained_dihedrals = None
            self.qm.directory = tmpDir
            result = self.qm.run()[0]
            self.assertFalse(result.errored)
            angle = np.rad2deg(dihedralAngle(result.coords[quad, :, 0]))
            self.assertAlmostEqual(179.51690845119924, angle, places=3) # Unstable results
Example #3
0
    def retrieve(self):

        ff = FFEvaluate(self.molecule, self._parameters)

        results = []
        for iframe in range(self.molecule.numFrames):
            self.molecule.frame = iframe

            directory = os.path.join(self.directory, '%05d' % iframe)
            os.makedirs(directory, exist_ok=True)
            pickleFile = os.path.join(directory, 'data.pkl')

            if self._completed(directory):
                with open(pickleFile, 'rb') as fd:
                    result = pickle.load(fd)
                logger.info('Loading QM data from %s' % pickleFile)

            else:
                result = QMResult()
                result.errored = False
                result.coords = self.molecule.coords[:, :, iframe:iframe + 1].copy()

                if self.optimize:
                    opt = nlopt.opt(nlopt.LN_COBYLA, result.coords.size)
                    opt.set_min_objective(lambda x, _: ff.calculateEnergies(x.reshape((-1, 3)))['total'])
                    if self.restrained_dihedrals is not None:
                        for dihedral in self.restrained_dihedrals:
                            indices = dihedral.copy()
                            ref_angle = dihedralAngle(self.molecule.coords[indices, :, iframe])
                            def constraint(x, _):
                                coords = x.reshape((-1, 3))
                                angle = dihedralAngle(coords[indices])
                                return np.sin(.5*(angle - ref_angle))
                            opt.add_equality_constraint(constraint)
                    opt.set_xtol_abs(1e-3) # Similar to Psi4 default
                    opt.set_maxeval(1000*opt.get_dimension())
                    opt.set_initial_step(1e-3)
                    result.coords = opt.optimize(result.coords.ravel()).reshape((-1, 3, 1))
                    logger.info('Optimization status: %d' % opt.last_optimize_result())

                result.energy = ff.calculateEnergies(result.coords[:, :, 0])['total']
                result.dipole = getDipole(self.molecule)

                if self.optimize:
                    assert opt.last_optimum_value() == result.energy # A self-consistency test

                # Compute ESP values
                if self.esp_points is not None:
                    assert self.molecule.numFrames == 1
                    result.esp_points = self.esp_points
                    distances = cdist(result.esp_points, result.coords[:, :, 0])  # Angstrom
                    distances *= const.physical_constants['Bohr radius'][0] / const.angstrom  # Angstrom --> Bohr
                    result.esp_values = np.dot(np.reciprocal(distances), self.molecule.charge)  # Hartree/Bohr

                with open(pickleFile, 'wb') as fd:
                    pickle.dump(result, fd)

            results.append(result)

        return results
Example #4
0
    def minimize(self, coords, restrained_dihedrals=None, maxeval=None):
        from simtk import unit
        from simtk.openmm import app, PeriodicTorsionForce
        import simtk.openmm as mm
        from scipy.optimize import minimize

        forceidx = []
        if restrained_dihedrals:
            f = PeriodicTorsionForce()

            for dihedral in restrained_dihedrals:
                theta0 = dihedralAngle(coords[dihedral])
                f.addTorsion(*tuple(map(int, dihedral)),
                             periodicity=1,
                             phase=theta0,
                             k=-10000 * unit.kilocalories_per_mole)

            fidx = self.system.addForce(f)
            forceidx.append(fidx)

        if coords.ndim == 3:
            coords = coords[:, :, 0]

        natoms = coords.shape[0]

        integrator = mm.LangevinIntegrator(0, 0, 0)
        sim = app.Simulation(self.structure.topology, self.system, integrator,
                             self.platform, self.platprop)

        def goalFunc(x):
            sim.context.setPositions(x.reshape((natoms, 3)) * unit.angstrom)
            state = sim.context.getState(getEnergy=True, getForces=True)
            energy = state.getPotentialEnergy().value_in_unit(
                unit.kilocalories_per_mole)
            forces = state.getForces(asNumpy=True).value_in_unit(
                unit.kilocalories_per_mole / unit.angstrom)
            grad = -forces.reshape(-1)
            return energy, grad

        res = minimize(goalFunc,
                       coords.reshape(-1),
                       method='L-BFGS-B',
                       jac=True,
                       options={
                           'ftol': 0,
                           'gtol': 0.05
                       })
        endcoords = res.x.reshape((natoms, 3)).copy()

        if restrained_dihedrals:
            for fi in forceidx[::-1]:
                self.system.removeForce(fi)

        return endcoords
Example #5
0
    def _calcDihedralAngles(self, mol, dihedrals, sincos=True):
        from htmd.numbautil import dihedralAngle
        metric = np.zeros((np.size(mol.coords, 2), len(dihedrals)))

        for i, dih in enumerate(dihedrals):
            metric[:, i] = np.rad2deg(dihedralAngle(mol.coords[dih, :, :]))

        if sincos:
            sc_metric = np.zeros((np.size(metric, 0), np.size(metric, 1) * 2))
            sc_metric[:, 0::2] = np.sin(metric * np.pi / 180.)
            sc_metric[:, 1::2] = np.cos(metric * np.pi / 180.)
            metric = sc_metric
        return metric.astype(np.float32)
Example #6
0
    def _calcDihedralAngles(self, mol, dihedrals, sincos=True):
        from htmd.numbautil import dihedralAngle
        metric = np.zeros((np.size(mol.coords, 2), len(dihedrals)))

        for i, dih in enumerate(dihedrals):
            metric[:, i] = np.rad2deg(dihedralAngle(mol.coords[dih, :, :]))

        if sincos:
            sc_metric = np.zeros((np.size(metric, 0), np.size(metric, 1) * 2))
            sc_metric[:, 0::2] = np.sin(metric * np.pi / 180.)
            sc_metric[:, 1::2] = np.cos(metric * np.pi / 180.)
            metric = sc_metric
        return metric.astype(np.float32)
Example #7
0
    def minimize(self, coords, restrained_dihedrals):
        if restrained_dihedrals is not None:
            for dihedral in restrained_dihedrals:
                indices = dihedral.copy()
                ref_angle = dihedralAngle(coords[indices, :, 0])

                def constraint(x, _):
                    coords = x.reshape((-1, 3))
                    angle = dihedralAngle(coords[indices])
                    return np.sin(.5 * (angle - ref_angle))

                self.opt.add_equality_constraint(constraint)

        self.opt.set_xtol_abs(1e-3)  # Similar to Psi4 default
        self.opt.set_maxeval(1000 * self.opt.get_dimension())
        self.opt.set_initial_step(1e-3)
        return self.opt.optimize(coords.ravel()).reshape((-1, 3, 1))
Example #8
0
    def _setup(self):

        if len(self.dihedrals) != len(self.qm_results):
            raise ValueError('The number of dihedral and QM result sets has to be the same!')

        # Get dihedral names
        self._names = ['-'.join(self.molecule.name[dihedral]) for dihedral in self.dihedrals]

        # Get equivalent dihedral atom indices
        self._equivalent_indices = []
        for idihed, dihedral in enumerate(self.dihedrals):
            found = False
            for parameterizableDihedral in self._parameterizable_dihedrals:
                if np.all(list(parameterizableDihedral[0]) == dihedral):
                    self._equivalent_indices.append(parameterizableDihedral)
                    found = True
                    break
            if not found:
                raise ValueError('%s is not recognized as a parameterizable dihedral\n' % self._names[idihed])

        # Get reference QM energies and rotamer coordinates
        self._valid_qm_results = self._getValidQMResults()
        self._reference_energies = []
        self._coords = []
        for results in self._valid_qm_results:
            self._reference_energies.append(np.array([result.energy for result in results]))
            self._coords.append([result.coords for result in results])

        # Calculate dihedral angle values for the fitted equivalent dihedral
        self._angle_values = []
        for rotamer_coords, equivalent_indices in zip(self._coords, self._equivalent_indices):
            angle_values = []
            for coords in rotamer_coords:
                angle_values.append([np.rad2deg(dihedralAngle(coords[indices, :, 0])) for indices in equivalent_indices])
            self._angle_values.append(np.array(angle_values))
        self._angle_values_rad = [np.deg2rad(angle_values)[:, :, None] for angle_values in self._angle_values]

        self._parameterizable_dihedral_atomtypes = [tuple(self.molecule.atomtype[idx]) for idx in self.dihedrals]

        # Calculated initial MM energies
        ff = FFEvaluate(self.molecule, self.parameters)
        self._initial_energies = []
        for rotamer_coords in self._coords:
            self._initial_energies.append(np.array([ff.calculateEnergies(coords[:, :, 0])['total'] for coords in rotamer_coords]))
Example #9
0
    def _setup(self):

        if len(self.dihedrals) != len(self.qm_results):
            raise ValueError('The number of dihedral and QM result sets has to be the same!')

        # Get dihedral names
        self._names = ['-'.join(self.molecule.name[dihedral]) for dihedral in self.dihedrals]

        # Get equivalent dihedral atom indices
        self._equivalent_indices = []
        for idihed, dihedral in enumerate(self.dihedrals):
            found = False
            for parameterizableDihedral in self._parameterizable_dihedrals:
                if np.all(list(parameterizableDihedral[0]) == dihedral):
                    self._equivalent_indices.append(parameterizableDihedral)
                    found = True
                    break
            if not found:
                raise ValueError('%s is not recognized as a parameterizable dihedral\n' % self._names[idihed])

        # Get reference QM energies and rotamer coordinates
        self._valid_qm_results = self._getValidQMResults()
        self._reference_energies = []
        self._coords = []
        for results in self._valid_qm_results:
            self._reference_energies.append(np.array([result.energy for result in results]))
            self._coords.append([result.coords for result in results])

        # Calculate dihedral angle values for the fitted equivalent dihedral
        self._angle_values = []
        for rotamer_coords, equivalent_indices in zip(self._coords, self._equivalent_indices):
            angle_values = []
            for coords in rotamer_coords:
                angle_values.append([np.rad2deg(dihedralAngle(coords[indices, :, 0])) for indices in equivalent_indices])
            self._angle_values.append(np.array(angle_values))
        self._angle_values_rad = [np.deg2rad(angle_values)[:, :, None] for angle_values in self._angle_values]

        self._parameterizable_dihedral_atomtypes = [tuple(self.molecule.atomtype[idx]) for idx in self.dihedrals]

        # Calculated initial MM energies
        ff = FFEvaluate(self.molecule, self.parameters)
        self._initial_energies = []
        for rotamer_coords in self._coords:
            self._initial_energies.append(np.array([ff.run(coords[:, :, 0])['total'] for coords in rotamer_coords]))
Example #10
0
    def _setup(self):

        if len(self.dihedrals) != len(self.qm_results):
            raise ValueError(
                'The number of dihedral and QM result sets has to be the same!'
            )

        # Get dihedral names
        self._names = [
            '-'.join(self.molecule.name[dihedral])
            for dihedral in self.dihedrals
        ]

        # Get all equivalent dihedrals
        all_equivalent_dihedrals = detectParameterizableDihedrals(
            self.molecule)
        all_equivalent_dihedrals = {
            tuple(dihedrals[0]): dihedrals
            for dihedrals in all_equivalent_dihedrals
        }

        # Choose the selected dihedrals
        self._equivalent_dihedrals = []
        for dihedral, name in zip(self.dihedrals, self._names):
            if tuple(dihedral) not in all_equivalent_dihedrals:
                raise ValueError(
                    '{} is not a parameterizable dihedral!'.format(name))
            self._equivalent_dihedrals.append(
                all_equivalent_dihedrals[tuple(dihedral)])

        # Get dihedral atom types
        self._dihedral_atomtypes = [
            findDihedralType(tuple(self.molecule.atomtype[dihedral]),
                             self.parameters) for dihedral in self.dihedrals
        ]

        # Get reference QM energies and rotamer coordinates
        self._reference_energies = []
        self._coords = []
        for results in self.qm_results:
            self._reference_energies.append(
                np.array([result.energy for result in results]))
            self._coords.append([result.coords for result in results])

        # Calculate dihedral angle values
        # [# of scans, # of dihedrals, # of conformations, # of equivalents]
        self._angle_values = []
        for scan_coords in self._coords:
            scan_angle_values = []
            for equivalent_indices in self._equivalent_dihedrals:
                angle_values = []
                for coords in scan_coords:
                    angle_values.append([
                        dihedralAngle(coords[indices, :, 0])
                        for indices in equivalent_indices
                    ])
                scan_angle_values.append(np.array(angle_values))
            self._angle_values.append(scan_angle_values)

        # Calculated initial MM energies
        ff = FFEvaluate(self.molecule, self.parameters)
        self._initial_energies = []
        for scan_coords in self._coords:
            energies = [
                ff.calculateEnergies(coords[:, :, 0])['total']
                for coords in scan_coords
            ]
            self._initial_energies.append(np.array(energies))

        # Make result directories
        os.makedirs(self.result_directory, exist_ok=True)
Example #11
0
    def retrieve(self):

        prmtop = self._get_prmtop()
        system = prmtop.createSystem()
        groups = {force.getForceGroup() for force in system.getForces()}

        if self.optimize:
            if self.restrained_dihedrals is not None:
                restraint = openmm.PeriodicTorsionForce()
                restraint.setForceGroup(max(groups) + 1)

                for dihedral in self.restrained_dihedrals:
                    restraint.addTorsion(*tuple(map(int, dihedral)), periodicity=1, phase=0,
                                         k=-1000 * unit.kilocalorie_per_mole)

                system.addForce(restraint)

        simulation = app.Simulation(prmtop.topology, system,
                                    openmm.VerletIntegrator(1 * unit.femtosecond),
                                    openmm.Platform.getPlatformByName('CPU'))

        results = []
        molecule_copy = self.molecule.copy()
        for iframe in range(self.molecule.numFrames):
            self.molecule.frame = iframe
            molecule_copy.frame = iframe

            directory = os.path.join(self.directory, '%05d' % iframe)
            os.makedirs(directory, exist_ok=True)
            pickleFile = os.path.join(directory, 'data.pkl')

            if self._completed(directory):
                with open(pickleFile, 'rb') as fd:
                    results.append(pickle.load(fd))
                logger.info('Loading QM data from %s' % pickleFile)
                continue

            simulation.context.setPositions(self.molecule.coords[:, :, iframe] * unit.angstrom)
            if self.optimize:
                if self.restrained_dihedrals is not None:
                    for i, dihedral in enumerate(self.restrained_dihedrals):
                        ref_angle = np.rad2deg(dihedralAngle(self.molecule.coords[dihedral, :, iframe]))
                        parameters = restraint.getTorsionParameters(i)
                        parameters[5] = ref_angle * unit.degree
                        restraint.setTorsionParameters(i, *parameters)
                    restraint.updateParametersInContext(simulation.context)
                simulation.minimizeEnergy(tolerance=0.001 * unit.kilocalorie_per_mole)
            state = simulation.context.getState(getEnergy=True, getPositions=True, groups=groups)

            result = QMResult()
            result.charge = self.charge
            result.errored = False
            result.energy = state.getPotentialEnergy().value_in_unit(unit.kilocalorie_per_mole)
            result.coords = state.getPositions(asNumpy=True).value_in_unit(unit.angstrom).reshape((-1, 3, 1))
            result.dipole = getDipole(self.molecule)

            if self.esp_points is not None:
                assert self.molecule.numFrames == 1
                result.esp_points = self.esp_points
                distances = cdist(result.esp_points, result.coords[:, :, 0])  # Angstrom
                distances *= const.physical_constants['Bohr radius'][0] / const.angstrom  # Angstrom --> Bohr
                result.esp_values = np.dot(np.reciprocal(distances), self.molecule.charge)  # Hartree/Bohr

            results.append(result)

            with open(pickleFile, 'wb') as fd:
                pickle.dump(result, fd)

            self.molecule.write(os.path.join(directory, 'mol-init.mol2'))  # Write an optimiz
            molecule_copy.coords[:, :, iframe] = result.coords[:, :, 0]
            molecule_copy.write(os.path.join(directory, 'mol.mol2'))  # Write an optimiz

        return results
Example #12
0
 def constraint(x, _):
     coords = x.reshape((-1, 3))
     angle = dihedralAngle(coords[indices])
     return np.sin(.5*(angle - ref_angle))