Exemple #1
0
    def compute_partial_charges(self, molecule, method='am1bcc'):
        """
        It computes the partial charges using antechamber.

        Parameters
        ----------
        molecule : an offpele.topology.Molecule
            The offpele's Molecule object
        method : str
            The name of the method to use. One of ['gasteiger', 'am1bcc'].
            If None, 'am1bcc' will be used

        Returns
        -------
        charges : simtk.unit.Quantity
            The array of partial charges

        Raises
        ------
        ChargeMethodUnavailableError if the requested charge method can not
            be handled by this toolkit
        ChargeCalculationError if the charge method is supported by this
            toolkit, but fails
        """

        SUPPORTED_CHARGE_METHODS = {
            'am1bcc': {
                'antechamber_keyword': 'bcc'
            },
            'gasteiger': {
                'antechamber_keyword': 'gas'
            }
        }

        if method not in SUPPORTED_CHARGE_METHODS:
            raise ChargeMethodUnavailableError(
                'partial_charge_method ' +
                '{} is not available from '.format(method) +
                'AmberToolsToolkitWrapper. Available charge methods are ' +
                list(SUPPORTED_CHARGE_METHODS.keys()))

        off_molecule = molecule.off_molecule

        with tempfile.TemporaryDirectory() as tmpdir:
            with temporary_cd(tmpdir):
                net_charge = off_molecule.total_charge / \
                    unit.elementary_charge

                self._rdkit_toolkit_wrapper.to_sdf_file(
                    molecule, tmpdir + '/molecule.sdf')

                subprocess.check_output([
                    "antechamber", "-i", "molecule.sdf", "-fi", "sdf", "-o",
                    "charged.ac", "-fo", "ac", "-pf", "yes", "-dr", "n", "-c",
                    SUPPORTED_CHARGE_METHODS[method]['antechamber_keyword'],
                    "-nc",
                    str(net_charge)
                ])
                # Write out just charges
                subprocess.check_output([
                    "antechamber", "-dr", "n", "-i", "charged.ac", "-fi", "ac",
                    "-o", "charged2.ac", "-fo", "ac", "-c", "wc", "-cf",
                    "charges.txt", "-pf", "yes"
                ])

                if not os.path.exists('charges.txt'):
                    # TODO: copy files into local directory to aid debugging?
                    raise ChargeCalculationError(
                        "Antechamber/sqm partial charge calculation failed on "
                        "molecule {} (SMILES {})".format(
                            off_molecule.name, off_molecule.to_smiles()))

                # Read the charges
                with open('charges.txt', 'r') as infile:
                    contents = infile.read()

                text_charges = contents.split()
                charges = np.zeros([off_molecule.n_atoms], np.float64)
                for index, token in enumerate(text_charges):
                    charges[index] = float(token)

        charges = unit.Quantity(charges, unit.elementary_charge)

        assert len(charges) == len(molecule.rdkit_molecule.GetAtoms()), \
            'Partial charge computation failed as the length of ' \
            + 'resulting partial charges does not match with the ' \
            + 'number of atoms in molecule'

        return charges
Exemple #2
0
def validate_rjmc_work_variance(top_prop,
                                positions,
                                geometry_method=0,
                                num_iterations=10,
                                md_steps=250,
                                compute_timeseries=False,
                                md_system=None,
                                prespecified_conformers=None):
    """
    Arguments
    ----------
    top_prop : perses.rjmc.topology_proposal.TopologyProposal object
        topology_proposal
    md_system : openmm.System object, default None
        system from which md is conducted; the default is the top_prop._old_system
    geometry_method : int
        which geometry proposal method to use
            0: neglect_angles = True (this is supposed to be the zero-variance method)
            1: neglect_angles = False (this will accumulate variance)
            2: use_sterics = True (this is experimental)
    num_iterations: int
        number of times to run md_steps integrator
    md_steps: int
        number of md_steps to run in each num_iteration
    compute_timeseries = bool (default False)
        whether to use pymbar detectEquilibration and subsampleCorrelated data from the MD run (the potential energy is the data)
    prespecified_conformers = None or unit.Quantity(np.array([num_iterations, system.getNumParticles(), 3]), unit = unit.nanometers)
        whether to input a unit.Quantity of conformers and bypass the conformer_generation/pymbar stage; None will default conduct this phase

    Returns
    -------
    conformers : unit.Quantity(np.array([num_iterations, system.getNumParticles(), 3]), unit = unit.nanometers)
        decorrelated positions of the md run
    rj_works : list
        work from each conformer proposal
    """
    from openmmtools import integrators
    from perses.utils.openeye import smiles_to_oemol
    import simtk.unit as unit
    import simtk.openmm as openmm
    from openmmtools.constants import kB
    from perses.rjmc.geometry import FFAllAngleGeometryEngine
    import tqdm

    temperature = 300.0 * unit.kelvin  # unit-bearing temperature
    kT = kB * temperature  # unit-bearing thermal energy
    beta = 1.0 / kT  # unit-bearing inverse thermal energy

    #first, we must extract the top_prop relevant quantities
    topology = top_prop._old_topology
    if md_system == None:
        system = top_prop._old_system
    else:
        system = md_system

    if prespecified_conformers == None:

        #now we can specify conformations from MD
        integrator = integrators.LangevinIntegrator(
            collision_rate=1.0 / unit.picosecond,
            timestep=4.0 * unit.femtosecond,
            temperature=temperature)
        context = openmm.Context(system, integrator)
        context.setPositions(positions)
        openmm.LocalEnergyMinimizer.minimize(context)
        minimized_positions = context.getState(getPositions=True).getPositions(
            asNumpy=True)
        print(f"completed initial minimization")
        context.setPositions(minimized_positions)

        zeros = np.zeros([num_iterations, int(system.getNumParticles()), 3])
        conformers = unit.Quantity(zeros, unit=unit.nanometers)
        rps = np.zeros((num_iterations))

        print(f"conducting md sampling")
        for iteration in tqdm.trange(num_iterations):
            integrator.step(md_steps)
            state = context.getState(getPositions=True, getEnergy=True)
            new_positions = state.getPositions(asNumpy=True)
            conformers[iteration, :, :] = new_positions

            rp = state.getPotentialEnergy() * beta
            rps[iteration] = rp

        del context, integrator

        if compute_timeseries:
            print(f"computing production and data correlation")
            from pymbar import timeseries
            t0, g, Neff = timeseries.detectEquilibration(rps)
            series = timeseries.subsampleCorrelatedData(np.arange(
                t0, num_iterations),
                                                        g=g)
            print(f"production starts at index {t0} of {num_iterations}")
            print(f"the number of effective samples is {Neff}")
            indices = t0 + series
            print(f"the filtered indices are {indices}")

        else:
            indices = range(num_iterations)
    else:
        conformers = prespecified_conformers
        indices = range(len(conformers))

    #now we can define a geometry_engine
    if geometry_method == 0:
        geometry_engine = FFAllAngleGeometryEngine(
            metadata=None,
            use_sterics=False,
            n_bond_divisions=1000,
            n_angle_divisions=180,
            n_torsion_divisions=360,
            verbose=True,
            storage=None,
            bond_softening_constant=1.0,
            angle_softening_constant=1.0,
            neglect_angles=True)
    elif geometry_method == 1:
        geometry_engine = FFAllAngleGeometryEngine(
            metadata=None,
            use_sterics=False,
            n_bond_divisions=1000,
            n_angle_divisions=180,
            n_torsion_divisions=360,
            verbose=True,
            storage=None,
            bond_softening_constant=1.0,
            angle_softening_constant=1.0,
            neglect_angles=False)
    elif geometry_method == 2:
        geometry_engine = FFAllAngleGeometryEngine(
            metadata=None,
            use_sterics=True,
            n_bond_divisions=1000,
            n_angle_divisions=180,
            n_torsion_divisions=360,
            verbose=True,
            storage=None,
            bond_softening_constant=1.0,
            angle_softening_constant=1.0,
            neglect_angles=False)
    else:
        raise Exception(f"there is no geometry method for {geometry_method}")

    rj_works = []
    print(f"conducting geometry proposals...")
    for indx in tqdm.trange(len(indices)):
        index = indices[indx]
        print(f"index {indx}")
        new_positions, logp_forward = geometry_engine.propose(
            top_prop, conformers[index], beta)
        logp_backward = geometry_engine.logp_reverse(top_prop, new_positions,
                                                     conformers[index], beta)
        print(
            f"\tlogp_forward, logp_backward: {logp_forward}, {logp_backward}")
        added_energy = geometry_engine.forward_final_context_reduced_potential - geometry_engine.forward_atoms_with_positions_reduced_potential
        subtracted_energy = geometry_engine.reverse_final_context_reduced_potential - geometry_engine.reverse_atoms_with_positions_reduced_potential
        print(
            f"\tadded_energy, subtracted_energy: {added_energy}, {subtracted_energy}"
        )
        work = logp_forward - logp_backward + added_energy - subtracted_energy
        rj_works.append(work)
        print(f"\ttotal work: {work}")

    return conformers, rj_works
Exemple #3
0
    def run_task_ase_one_layer(self, settings, systems, n_sweeps, n_steps_per_sweep=100, verbose_freq=1, temperature_pot_mm=unit.Quantity(300, unit.kelvin),
                               temperature_kin_mm=unit.Quantity(300, unit.kelvin), label="0", checkpoint_freq=100, restart=False, ):
        """
        Method that runs a HMC sampler.

        Parameters
        ----------
        settings : dict
            Dictionary containing global ParaMol settings.
        systems : list of :obj:`ParaMol.System.system.ParaMolSystem`
            List containing instances of ParaMol systems.
        n_sweeps : int
            Number of MC sweeps to perform.
        n_steps_per_sweep : int
            Number of MD steps per MC sweep.
        verbose_freq : int
            Verbose frequency.
        temperature_pot_mm : unit.Quantity
            Temperature used for the MM potential part.
        temperature_kin_mm : unit.Quantity
            Temperature used for the MM kinetic part.
        label : int
            HMC sampler label. It has to be an integer number.
        checkpoint_freq : int
            Frequency at which checkpoint restart files are written.
        restart : bool
            Flag that controls whether or not to perform a restart.

        Returns
        -------
        systems : list
            List with the updated instances of ParaMol System.
        """
        from ase_interface import ANIENS
        from ase_interface import aniensloader
        from ase import units as ase_unit

        assert len(systems) == 1, "HMC task currently only supports one system at once."

        # Create QM Engines and initiate OpenMM
        for system in systems:
            system.convert_system_ref_arrays_to_list()

            # Create OpenMM system
            system.engine.init_openmm(create_system_params=system.engine._create_system_params)
            system.engine.get_masses()

            # Create QM Engine
            if system.interface is None:
                system.interface = ParaMolInterface()

            system.create_qm_engines(settings.qm_engine["qm_engine"], settings.qm_engine[settings.qm_engine["qm_engine"].lower()])

            # Create ASE NN calculator
            # Get atom list and atomic numbers list
            system.engine.get_atom_list()

            """
            mm_ase_engine = ASEWrapper(system_name=system.name,
                                       interface=system.interface,
                                       calculator=ANIENS(aniensloader('../ani_models/ani-1ccx_8x.info', 0)),
                                       n_atoms=system.n_atoms,
                                       atom_list=system.engine.atom_list,
                                       n_calculations=1,
                                       cell=None,
                                       work_dir_prefix="NN_ASEWorkDir_")
            """
            calc = ANIENS(aniensloader('/home/joao/programs/ASE_ANI/ani_models/ani-2x_8x.info',0))
            #calc = DFTD3(dft=calc, cutoff=np.sqrt(9000) * ase_units.Bohr, damping="bj", a1=0.5719, a2=3.6017, s8=0.5883, s6=1.000, alpha6=1.0)

            mm_ase_engine = ASEWrapper(system_name=system.name,
                                       interface=system.interface,
                                       calculator=calc,
                                       n_atoms=system.n_atoms,
                                       atom_list=system.engine.atom_list,
                                       n_calculations=1,
                                       cell=None,
                                       work_dir_prefix="NN_ASEWorkDir_")

        system = systems[0]

        self._label = label

        parameter_space, objective_function, optimizer = (None, None, None)
        # TODO: Once this is included in the main ParaMol version, add this line to the default dictionary
        settings.restart["restart_hmc_file_{}".format(self._label)] = "restart_hmc_{}.pickle".format(self._label)

        if restart:
            logging.info("Starting HMC sampler parametrization from a previous restart.")

            # Read HMCSampler pickle
            self.__dict__ = self.read_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label))

            # Read data into system
            system.read_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label)))
        else:
            self._n = 1

            # MM chain
            self._n_total_mm = 0
            self._n_accepted_mm = 0

        while self._n <= n_sweeps:
            if self._n % verbose_freq == 0:
                print("HMC sampler of system {} # Sweep number {}.".format(system.name, self._n))
                print("HMC sampler of system {} # Acceptance rate of MM chain {:.4f}".format(system.name, self._acceptance_rate_mm()))
            if len(system.ref_coordinates) > 0 and self._n != 1:
                # Do not need to compute the QM energy if there are structures in the top ensemble or if we are not in the first ever iteration.
                system.engine.set_positions(system.ref_coordinates[-1])
                potential_initial_mm = mm_ase_engine.run_calculation(coords=system.ref_coordinates[-1] * 10, label=int(self._label))
                coord_to_run = system.ref_coordinates[-1] * 10
            else:
                # Compute MM initial kinetic and potential energy
                potential_initial_mm = mm_ase_engine.run_calculation(coords=system.engine.get_positions().in_units_of(unit.angstrom)._value, label=int(self._label))
                coord_to_run = system.engine.get_positions().in_units_of(unit.angstrom)._value

            # Run short MD using ASE
            coords, potential_initial_mm, kinetic_initial, forces_initial, potential_final_mm, kinetic_final, forces_final = mm_ase_engine.run_md(coords=coord_to_run,
                                                                                                                                                  label=int(self._label),
                                                                                                                                                  steps=n_steps_per_sweep,
                                                                                                                                                  dt=0.5*ase_unit.fs,
                                                                                                                                                  initial_temperature=300.0*ase_unit.kB,)
            # Compute MM final kinetic and potential energy
            kinetic_initial = unit.Quantity(kinetic_initial, unit.kilojoules_per_mole)
            potential_initial_mm = unit.Quantity(potential_initial_mm, unit.kilojoules_per_mole)
            kinetic_final = unit.Quantity(kinetic_final, unit.kilojoules_per_mole)
            potential_final_mm = unit.Quantity(potential_final_mm, unit.kilojoules_per_mole)
            coords = unit.Quantity(coords, unit.nanometers)

            if self._hmc_acceptance_criterion_mm(potential_final_mm, potential_initial_mm, kinetic_final, kinetic_initial, temperature_pot_mm, temperature_kin_mm):
                # Append energies, forces and conformations
                system.ref_energies.append(potential_final_mm._value)
                system.ref_coordinates.append(coords._value)
                system.n_structures += 1

            # TODO: include code related to partial momentum refreshment
            elif len(system.ref_coordinates) > 0:
                    # Append last accepted structure
                system.ref_energies.append(system.ref_energies[-1])
                system.ref_coordinates.append(system.ref_coordinates[-1])
                system.n_structures += 1
            else:
                # No structures have been accepted yet.
                pass

            # Write restart files
            if self._n % checkpoint_freq == 0:
                self.write_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label), self.__dict__)
                system.write_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label)))
                system.write_coordinates_xyz("{}_hmc_{}.xyz".format(system.name, self._label))

                self._n += 1

        return systems
Exemple #4
0
def distributeLipids(
        boxsize,
        resnames,
        sigmas,
        cutoff,
        mass=39.9 * unit.amu,  # argon
        epsilon=0.238 * unit.kilocalories_per_mole,  # argon,
        switch_width=3.4 * unit.angstrom,  # argon
):
    nparticles = len(resnames)

    # Determine Lennard-Jones cutoff.
    cutoff = cutoff * unit.angstrom

    cutoff_type = openmm.NonbondedForce.CutoffPeriodic

    # Create an empty system object.
    system = openmm.System()

    # Periodic box vectors.
    a = unit.Quantity(
        (boxsize[0] * unit.angstrom, 0 * unit.angstrom, 0 * unit.angstrom))
    b = unit.Quantity(
        (0 * unit.angstrom, boxsize[1] * unit.angstrom, 0 * unit.angstrom))
    c = unit.Quantity(
        (0 * unit.angstrom, 0 * unit.angstrom, boxsize[2] * unit.angstrom))
    system.setDefaultPeriodicBoxVectors(a, b, c)

    # Set up periodic nonbonded interactions with a cutoff.
    nb = openmm.NonbondedForce()
    nb.setNonbondedMethod(cutoff_type)
    nb.setCutoffDistance(cutoff)
    nb.setUseDispersionCorrection(True)

    nb.setUseSwitchingFunction(False)
    if (switch_width is not None):
        nb.setUseSwitchingFunction(True)
        nb.setSwitchingDistance(cutoff - switch_width)

    for s in sigmas:
        system.addParticle(mass)
        nb.addParticle(0.0 * unit.elementary_charge, s * unit.angstrom,
                       epsilon)

    positions = subrandom_particle_positions(
        nparticles, system.getDefaultPeriodicBoxVectors(), 2)

    # Add the nonbonded force.
    system.addForce(nb)

    # Add a restraining potential to keep atoms in z=0
    energy_expression = 'k * (z^2)'
    force = openmm.CustomExternalForce(energy_expression)
    force.addGlobalParameter('k', 10)
    for particle_index in range(nparticles):
        force.addParticle(particle_index, [])
    system.addForce(force)

    # Create topology.
    topology = app.Topology()
    chain = topology.addChain()
    elems = ['Ar', 'Cl', 'Na']
    _, idx = np.unique(resnames, return_inverse=True)
    for i in idx:
        element = app.Element.getBySymbol(elems[i])
        residue = topology.addResidue(elems[i], chain)
        topology.addAtom(elems[i], element, residue)

    topology.setUnitCellDimensions(unit.Quantity(boxsize, unit.angstrom))

    # Simulate it
    from simtk.openmm import LangevinIntegrator, VerletIntegrator
    from simtk.openmm.app import Simulation, PDBReporter, StateDataReporter, PDBFile
    from simtk.unit import kelvin, picoseconds, picosecond, angstrom
    from sys import stdout
    from mdtraj.reporters import DCDReporter
    nsteps = 10000
    freq = 1

    integrator = VerletIntegrator(0.002 * picoseconds)
    simulation = Simulation(topology, system, integrator)
    simulation.context.setPositions(positions)
    simulation.minimizeEnergy()
    # simulation.reporters.append(DCDReporter('output.dcd', 1))
    # simulation.reporters.append(StateDataReporter(stdout, 1000, potentialEnergy=True, totalEnergy=True, step=True, separator='   '))
    simulation.step(nsteps)

    state = simulation.context.getState(getPositions=True,
                                        enforcePeriodicBox=True)
    allfinalpos = state.getPositions(asNumpy=True).value_in_unit(angstrom)

    # with open('topology.pdb', 'w') as f:
    #     PDBFile.writeFile(topology, positions, f)

    # from htmd.molecule.molecule import Molecule
    # mol = Molecule('topology.pdb')
    # mol.read('output.dcd')

    return allfinalpos
Exemple #5
0
def readAmberSystem(prmtop_filename=None,
                    prmtop_loader=None,
                    shake=None,
                    gbmodel=None,
                    soluteDielectric=1.0,
                    solventDielectric=78.5,
                    nonbondedCutoff=None,
                    nonbondedMethod='NoCutoff',
                    scee=1.2,
                    scnb=2.0,
                    mm=None,
                    verbose=False,
                    EwaldErrorTolerance=None,
                    flexibleConstraints=True,
                    rigidWater=True):
    """
    Create an OpenMM System from an Amber prmtop file.

    ARGUMENTS (specify  one or the other, but not both)
      prmtop_filename (String) - name of Amber prmtop file (new-style only)
      prmtop_loader (PrmtopLoader) - the loaded prmtop file

    OPTIONAL ARGUMENTS
      shake (String) - if 'h-bonds', will SHAKE all bonds to hydrogen and water; if 'all-bonds', will SHAKE all bonds and water (default: None)
      gbmodel (String) - if 'OBC', OBC GBSA will be used; if 'GBVI', GB/VI will be used (default: None)
      soluteDielectric (float) - The solute dielectric constant to use in the implicit solvent model (default: 1.0)
      solventDielectric (float) - The solvent dielectric constant to use in the implicit solvent model (default: 78.5)
      nonbondedCutoff (float) - if specified, will set nonbondedCutoff (default: None)
      scnb (float) - 1-4 Lennard-Jones scaling factor (default: 1.2)
      scee (float) - 1-4 electrostatics scaling factor (default: 2.0)
      mm - if specified, this module will be used in place of pyopenmm (default: None)
      verbose (boolean) - if True, print out information on progress (default: False)
      flexibleConstraints (boolean) - if True, flexible bonds will be added in addition ot constrained bonds
      rigidWater (boolean=True) If true, water molecules will be fully rigid regardless of the value passed for the shake argument

    NOTES

    Even if bonds are SHAKEn, their harmonic stretch terms are still included in the potential.

    TODO

    Should these option names be changed to reflect their 'sander' counterparts?

    EXAMPLES

    Create a system of alanine dipeptide in implicit solvent.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa')
    >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
    >>> system = readAmberSystem(prmtop_filename)

    Parse a prmtop file of alanine dipeptide in explicit solvent.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
    >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop')
    >>> system = readAmberSystem(prmtop_filename)

    """

    if prmtop_filename is None and prmtop_loader is None:
        raise Exception("Must specify a filename or loader")
    if prmtop_filename is not None and prmtop_loader is not None:
        raise Exception("Cannot specify both a filename and a loader")
    if prmtop_filename is not None:
        # Load prmtop file.
        if verbose: print "Reading prmtop file '%s'..." % prmtop_filename
        prmtop = PrmtopLoader(prmtop_filename)
    else:
        prmtop = prmtop_loader

    if prmtop.getIfCap() > 0:
        raise Exception("CAP option not currently supported")

    if prmtop.getIfPert() > 0:
        raise Exception("perturbation not currently supported")

    if prmtop.getIfBox() > 1:
        raise Exception("only standard periodic boxes are currently supported")

    # Use pyopenmm implementation of OpenMM by default.
    if mm is None:
        mm = simtk.openmm

    # Create OpenMM System.
    if verbose: print "Creating OpenMM system..."
    system = mm.System()

    # Populate system with atomic masses.
    if verbose: print "Adding particles..."
    for mass in prmtop.getMasses():
        system.addParticle(mass)

    # Add constraints.
    isWater = [
        prmtop.getResidueLabel(i) == 'WAT' for i in range(prmtop.getNumAtoms())
    ]
    if shake in ('h-bonds', 'all-bonds', 'h-angles'):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            system.addConstraint(iAtom, jAtom, rMin)
    if shake in ('all-bonds', 'h-angles'):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH():
            system.addConstraint(iAtom, jAtom, rMin)
    if rigidWater and shake == None:
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            if isWater[iAtom] and isWater[jAtom]:
                system.addConstraint(iAtom, jAtom, rMin)

    # Add harmonic bonds.
    if verbose: print "Adding bonds..."
    force = mm.HarmonicBondForce()
    if flexibleConstraints or (shake
                               not in ('h-bonds', 'all-bonds', 'h-angles')):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH():
            if flexibleConstraints or not (rigidWater and isWater[iAtom]
                                           and isWater[jAtom]):
                force.addBond(iAtom, jAtom, rMin, 2 * k)
    if flexibleConstraints or (shake not in ('all-bonds', 'h-angles')):
        for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH():
            force.addBond(iAtom, jAtom, rMin, 2 * k)
    system.addForce(force)

    # Add harmonic angles.
    if verbose: print "Adding angles..."
    force = mm.HarmonicAngleForce()
    if shake == 'h-angles':
        numConstrainedBonds = system.getNumConstraints()
        atomConstraints = [[]] * system.getNumParticles()
        for i in range(system.getNumConstraints()):
            c = system.getConstraintParameters(i)
            distance = c[2].value_in_unit(units.nanometer)
            atomConstraints[c[0]].append((c[1], distance))
            atomConstraints[c[1]].append((c[0], distance))
    for (iAtom, jAtom, kAtom, k, aMin) in prmtop.getAngles():
        if shake == 'h-angles':
            type1 = prmtop.getAtomType(iAtom)
            type2 = prmtop.getAtomType(jAtom)
            type3 = prmtop.getAtomType(kAtom)
            numH = len(
                [type for type in (type1, type3) if type.startswith('H')])
            constrained = (numH == 2 or (numH == 1 and type2.startswith('O')))
        else:
            constrained = False
        if constrained:
            # Find the two bonds that make this angle.
            l1 = None
            l2 = None
            for bond in atomConstraints[jAtom]:
                if bond[0] == iAtom:
                    l1 = bond[1]
                elif bond[0] == kAtom:
                    l2 = bond[1]

            # Compute the distance between atoms and add a constraint
            length = math.sqrt(l1 * l1 + l2 * l2 -
                               2 * l1 * l2 * math.cos(aMin))
            system.addConstraint(iAtom, kAtom, length)
        if flexibleConstraints or not constrained:
            force.addAngle(iAtom, jAtom, kAtom, aMin, 2 * k)
    system.addForce(force)

    # Add torsions.
    if verbose: print "Adding torsions..."
    force = mm.PeriodicTorsionForce()
    for (iAtom, jAtom, kAtom, lAtom, forceConstant, phase,
         periodicity) in prmtop.getDihedrals():
        force.addTorsion(iAtom, jAtom, kAtom, lAtom, periodicity, phase,
                         forceConstant)
    system.addForce(force)

    # Add nonbonded interactions.
    if verbose: print "Adding nonbonded interactions..."
    force = mm.NonbondedForce()
    if (prmtop.getIfBox() == 0):
        # System is non-periodic.
        if nonbondedMethod == 'NoCutoff':
            force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            if nonbondedCutoff is None:
                raise Exception("No cutoff value specified")
            force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
            force.setCutoffDistance(nonbondedCutoff)
        else:
            raise Exception(
                "Illegal nonbonded method for a non-periodic system")
    else:
        # System is periodic.
        # Set periodic box vectors for periodic system
        (boxBeta, boxX, boxY, boxZ) = prmtop.getBoxBetaAndDimensions()
        d0 = units.Quantity(0.0, units.angstroms)
        xVec = units.Quantity((boxX, d0, d0))
        yVec = units.Quantity((d0, boxY, d0))
        zVec = units.Quantity((d0, d0, boxZ))
        system.setDefaultPeriodicBoxVectors(xVec, yVec, zVec)

        # Set cutoff.
        if nonbondedCutoff is None:
            # Compute cutoff automatically.
            min_box_width = min([
                boxX / units.nanometers, boxY / units.nanometers,
                boxZ / units.nanometers
            ])
            CLEARANCE_FACTOR = 0.97  # reduce the cutoff to be a bit smaller than 1/2 smallest box length
            nonbondedCutoff = units.Quantity(
                (min_box_width * CLEARANCE_FACTOR) / 2.0, units.nanometers)
        if nonbondedMethod != 'NoCutoff':
            force.setCutoffDistance(nonbondedCutoff)

        # Set nonbonded method.
        if nonbondedMethod == 'NoCutoff':
            force.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
        elif nonbondedMethod == 'CutoffPeriodic':
            force.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
        elif nonbondedMethod == 'Ewald':
            force.setNonbondedMethod(mm.NonbondedForce.Ewald)
        elif nonbondedMethod == 'PME':
            force.setNonbondedMethod(mm.NonbondedForce.PME)
        else:
            raise Exception("Cutoff method not understood.")

        if EwaldErrorTolerance is not None:
            force.setEwaldErrorTolerance(EwaldErrorTolerance)

    # Add per-particle nonbonded parameters.
    sigmaScale = 2**(-1. / 6.) * 2.0
    for (charge, (rVdw, epsilon)) in zip(prmtop.getCharges(),
                                         prmtop.getNonbondTerms()):
        sigma = rVdw * sigmaScale
        force.addParticle(charge, sigma, epsilon)

    # Add 1-4 Interactions
    excludedAtomPairs = set()
    sigmaScale = 2**(-1. / 6.)
    for (iAtom, lAtom, chargeProd, rMin,
         epsilon) in prmtop.get14Interactions():
        chargeProd /= scee
        epsilon /= scnb
        sigma = rMin * sigmaScale
        force.addException(iAtom, lAtom, chargeProd, sigma, epsilon)
        excludedAtomPairs.add(min((iAtom, lAtom), (lAtom, iAtom)))

    # Add Excluded Atoms
    excludedAtoms = prmtop.getExcludedAtoms()
    excludeParams = (0.0, 0.1, 0.0)
    for iAtom in range(prmtop.getNumAtoms()):
        for jAtom in excludedAtoms[iAtom]:
            if min((iAtom, jAtom), (jAtom, iAtom)) in excludedAtomPairs:
                continue
            force.addException(iAtom, jAtom, excludeParams[0],
                               excludeParams[1], excludeParams[2])

    system.addForce(force)

    # Add virtual sites for water.
    epNames = ['EP', 'LP']
    ep = [
        i for i in range(prmtop.getNumAtoms())
        if isWater[i] and prmtop.getAtomName(i)[:2] in epNames
    ]
    if len(ep) > 0:
        epRes = set((prmtop.getResidueNumber(i) for i in ep))
        numRes = max(epRes) + 1
        # For each residue that contains an "extra point", find the oxygen, hydrogens, and points.
        waterO = []
        waterH = []
        waterEP = []
        for i in range(numRes):
            waterO.append([])
            waterH.append([])
            waterEP.append([])
        for i in range(prmtop.getNumAtoms()):
            res = prmtop.getResidueNumber(i)
            if res in epRes:
                name = prmtop.getAtomName(i)
                if name[0] == 'O':
                    waterO[res].append(i)
                if name[0] == 'H':
                    waterH[res].append(i)
                if name[:2] in epNames:
                    waterEP[res].append(i)
        # Record bond lengths for faster access.
        distOH = [None] * numRes
        distHH = [None] * numRes
        distOE = [None] * numRes
        for (atom1, atom2, k,
             dist) in prmtop.getBondsWithH() + prmtop.getBondsNoH():
            res = prmtop.getResidueNumber(atom1)
            if res in epRes:
                name1 = prmtop.getAtomName(atom1)
                name2 = prmtop.getAtomName(atom2)
                if name1[0] == 'H' or name2[0] == 'H':
                    if name1[0] == 'H' and name2[0] == 'H':
                        distHH[res] = dist
                    if name1[0] == 'O' or name2[0] == 'O':
                        distOH[res] = dist
                elif (name1[0] == 'O' or name2[0] == 'O') and (
                    (name1[:2] in epNames or name2[:2] in epNames)):
                    distOE[res] = dist
        # Loop over residues and add the virtual sites.
        outOfPlaneAngle = 54.735 * units.degree
        cosOOP = units.cos(outOfPlaneAngle)
        sinOOP = units.sin(outOfPlaneAngle)
        for res in range(numRes):
            if len(waterO[res]) == 1 and len(waterH[res]) == 2:
                if len(waterEP[res]) == 1:
                    # Four point water
                    weightH = distOE[res] / math.sqrt(distOH[res]**2 -
                                                      (0.5 * distHH[res])**2)
                    system.setVirtualSite(
                        waterEP[res][0],
                        mm.ThreeParticleAverageSite(waterO[res][0],
                                                    waterH[res][0],
                                                    waterH[res][1],
                                                    1 - weightH, weightH / 2,
                                                    weightH / 2))
                elif len(waterEP[res]) == 2:
                    # Five point water
                    weightH = cosOOP * distOE[res] / math.sqrt(
                        distOH[res]**2 - (0.5 * distHH[res])**2)
                    angleHOH = 2 * math.asin(0.5 * distHH[res] / distOH[res])
                    lenCross = (distOH[res]**2) * math.sin(angleHOH)
                    weightCross = sinOOP * distOE[res] / lenCross
                    system.setVirtualSite(
                        waterEP[res][0],
                        mm.OutOfPlaneSite(waterO[res][0], waterH[res][0],
                                          waterH[res][1], weightH / 2,
                                          weightH / 2, weightCross))
                    system.setVirtualSite(
                        waterEP[res][1],
                        mm.OutOfPlaneSite(waterO[res][0], waterH[res][0],
                                          waterH[res][1], weightH / 2,
                                          weightH / 2, -weightCross))

    # Add GBSA model.
    if gbmodel is not None:
        if verbose: print "Adding GB parameters..."
        charges = prmtop.getCharges()
        symbls = None
        if gbmodel == 'GBn':
            symbls = prmtop.getAtomTypes()
        gb_parms = prmtop.getGBParms(symbls)
        if gbmodel == 'HCT':
            gb = customgb.GBSAHCTForce(solventDielectric, soluteDielectric,
                                       'ACE')
        elif gbmodel == 'OBC1':
            gb = customgb.GBSAOBC1Force(solventDielectric, soluteDielectric,
                                        'ACE')
        elif gbmodel == 'OBC2':
            gb = mm.GBSAOBCForce()
            gb.setSoluteDielectric(soluteDielectric)
            gb.setSolventDielectric(solventDielectric)
        elif gbmodel == 'GBn':
            gb = customgb.GBSAGBnForce(solventDielectric, soluteDielectric,
                                       'ACE')
        else:
            raise Exception(
                "Illegal value specified for implicit solvent model")
        for iAtom in range(prmtop.getNumAtoms()):
            if gbmodel == 'OBC2':
                gb.addParticle(charges[iAtom], gb_parms[iAtom][0],
                               gb_parms[iAtom][1])
            else:
                gb.addParticle(
                    [charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]])
        system.addForce(gb)
        if nonbondedMethod == 'NoCutoff':
            gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff)
        elif nonbondedMethod == 'CutoffNonPeriodic':
            gb.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic)
            gb.setCutoffDistance(nonbondedCutoff)
        elif nonbondedMethod == 'CutoffPeriodic':
            gb.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic)
            gb.setCutoffDistance(nonbondedCutoff)
        else:
            raise Exception("Illegal nonbonded method for use with GBSA")
        force.setReactionFieldDielectric(1.0)

    # TODO: Add GBVI terms?

    return system
Exemple #6
0
    def _compute_energies(self):
        """
        Compute energies of all replicas at all states.

        """

        from scipy import weave

        start_time = time.time()

        # Temporary storage for computed phi and psi angles.
        phi = units.Quantity(numpy.zeros([self.nstates], numpy.float64),
                             units.radians)
        psi = units.Quantity(numpy.zeros([self.nstates], numpy.float64),
                             units.radians)

        # Compute reference energies.
        for replica_index in range(self.nstates):
            # Compute reference energy once.
            reference_energy = self.reference_state.reduced_potential(
                self.replica_coordinates[replica_index],
                platform=self.energy_platform)
            self.u_kl[replica_index, :] = reference_energy

        # Compute torsion angles.
        for replica_index in range(self.nstates):
            # Compute torsion angles.
            phi[replica_index] = self._compute_torsion(
                self.replica_coordinates[replica_index], 4, 6, 8, 14)
            psi[replica_index] = self._compute_torsion(
                self.replica_coordinates[replica_index], 6, 8, 14, 16)

        # Compute torsion energies.
        code = """
        for(int replica_index = 0; replica_index < nstates; replica_index++) {
           double phi = PHI1(replica_index);
           double psi = PSI1(replica_index);
           long state_index = 1;
           for(int phi_index = 0; phi_index < nbins; phi_index++) {
              for(int psi_index = 0; psi_index < nbins; psi_index++) {            
                 // Compute torsion angles
                 double phi0 = phi_index * delta;
                 double psi0 = psi_index * delta;

                 // Compute torsion energies.
                 U_KL2(replica_index,state_index) += kappa*cos(phi-phi0) + kappa*cos(psi-psi0);
                 state_index += 1;
               }
            }
        }
        """

        # Stage input temporarily.
        nstates = self.nstates
        nbins = self.nbins
        delta = self.delta / units.radians
        kappa = self.kappa
        phi = phi / units.radians
        psi = psi / units.radians
        u_kl = self.u_kl
        try:
            # Execute inline C code with weave.
            info = weave.inline(
                code,
                ['nstates', 'nbins', 'delta', 'kappa', 'phi', 'psi', 'u_kl'],
                headers=['<math.h>', '<stdlib.h>'],
                verbose=2)
            self.u_kl = u_kl
        except:
            for replica_index in range(self.nstates):
                # Compute torsion restraint energies for all states.
                state_index = 1
                for phi_index in range(self.nbins):
                    phi0 = float(phi_index) * self.delta / units.radians
                    for psi_index in range(self.nbins):
                        psi0 = float(psi_index) * self.delta / units.radians
                        # Compute torsion energies.
                        self.u_kl[replica_index, state_index] += (
                            self.kappa
                        ) * math.cos(phi[replica_index] - phi0) + (
                            self.kappa) * math.cos(psi[replica_index] - psi0)
                        #print "(%6d,%6d) : %16s %16s : %16.1f %16.1f" % (phi_index, psi_index, str(phi), str(psi), self.u_kl[replica_index,state_index], self.states[state_index].reduced_potential(self.replica_coordinates[replica_index]))
                        # Increment state index.
                        state_index += 1

        end_time = time.time()
        elapsed_time = end_time - start_time
        time_per_energy = elapsed_time / float(self.nstates)**2
        if self.verbose:
            print "Time to compute all energies %.3f s (%.3f per energy calculation).\n" % (
                elapsed_time, time_per_energy)

        return
Exemple #7
0
    def get_standard_state_correction(self):
        """
        Return the standard state correction.

        Returns
        -------
        correction : float
           The standard-state correction, in kT

        """
        initial_time = time.time()

        r_min = 0 * unit.nanometers
        r_max = 100 * unit.nanometers # TODO: Use maximum distance between atoms?

        # Create a System object containing two particles connected by the reference force
        system = openmm.System()
        system.addParticle(1.0 * unit.amu)
        system.addParticle(1.0 * unit.amu)
        force = self._create_restraint_force(0, 1)
        # Disable the PBC if on for this approximation of the analytical solution
        try:  # This was added in OpenMM 7.1
            force.setUsesPeriodicBoundaryConditions(False)
        except AttributeError:
            pass
        system.addForce(force)

        # Create a Reference context to evaluate energies on the CPU.
        integrator = openmm.VerletIntegrator(1.0 * unit.femtoseconds)
        platform = openmm.Platform.getPlatformByName('Reference')
        context = openmm.Context(system, integrator, platform)

        # Set default positions.
        positions = unit.Quantity(np.zeros([2,3]), unit.nanometers)
        context.setPositions(positions)

        # Create a function to compute integrand as a function of interparticle separation.
        beta = self.beta

        def integrand(r):
            """
            Parameters
            ----------
            r : float
                Inter-particle separation in nanometers

            Returns
            -------
            dI : float
               Contribution to integrand (in nm^2).

            """
            positions[1, 0] = r * unit.nanometers
            context.setPositions(positions)
            state = context.getState(getEnergy=True)
            potential = state.getPotentialEnergy()
            dI = 4.0 * math.pi * r**2 * math.exp(-beta * potential)
            return dI

        (shell_volume, shell_volume_error) = scipy.integrate.quad(lambda r : integrand(r), r_min / unit.nanometers, r_max / unit.nanometers) * unit.nanometers**3 # integrate shell volume
        logger.debug("shell_volume = %f nm^3" % (shell_volume / unit.nanometers**3))

        # Compute standard-state volume for a single molecule in a box of size (1 L) / (avogadros number)
        # Should also generate constant V0
        liter = 1000.0 * unit.centimeters**3  # one liter
        box_volume = liter / (unit.AVOGADRO_CONSTANT_NA*unit.mole) # standard state volume
        logger.debug("box_volume = %f nm^3" % (box_volume / unit.nanometers**3))

        # Compute standard state correction for releasing shell restraints into standard-state box (in units of kT).
        DeltaG = - math.log(box_volume / shell_volume)
        logger.debug("Standard state correction: %.3f kT" % DeltaG)

        final_time = time.time()
        elapsed_time = final_time - initial_time
        logger.debug("restraints: _computeStandardStateCorrection: %.3f s elapsed" % elapsed_time)

        # Return standard state correction (in kT).
        return DeltaG
Exemple #8
0
def compute_hydration_energies(molecules, parameters):
    """
    Compute solvation energies of a set of molecules given a GBSA parameter set.

    ARGUMENTS

    molecules (list of OEMol) - molecules with GBSA assigned atom types in type field
    parameters (dict) - dictionary of GBSA parameters keyed on GBSA atom types

    RETURNS

    energies (dict) - energies[molecule] is the computed solvation energy of given molecule

    """

    energies = dict(
    )  # energies[index] is the computed solvation energy of molecules[index]

    platform = openmm.Platform.getPlatformByName("Reference")

    for molecule in molecules:
        # Create OpenMM System.
        system = openmm.System()
        for atom in molecule.GetAtoms():
            mass = OEGetDefaultMass(atom.GetAtomicNum())
            system.addParticle(mass * units.amu)

        # Add nonbonded term.
        #   nonbonded_force = openmm.NonbondedSoftcoreForce()
        #   nonbonded_force.setNonbondedMethod(openmm.NonbondedForce.NoCutoff)
        #   for atom in molecule.GetAtoms():
        #      charge = 0.0 * units.elementary_charge
        #      sigma = 1.0 * units.angstrom
        #      epsilon = 0.0 * units.kilocalories_per_mole
        #      nonbonded_force.addParticle(charge, sigma, epsilon)
        #   system.addForce(nonbonded_force)

        # Add GBSA term
        gbsa_force = openmm.GBSAOBCForce()
        gbsa_force.setNonbondedMethod(
            openmm.GBSAOBCForce.NoCutoff)  # set no cutoff
        gbsa_force.setSoluteDielectric(1)
        gbsa_force.setSolventDielectric(78)

        # Build indexable list of atoms.
        atoms = [atom for atom in molecule.GetAtoms()]

        # Assign GBSA parameters.
        for atom in molecule.GetAtoms():
            atomtype = atom.GetStringData("gbsa_type")  # GBSA atomtype
            charge = atom.GetPartialCharge() * units.elementary_charge
            radius = parameters['%s_%s' %
                                (atomtype, 'radius')] * units.angstroms
            scalingFactor = parameters[
                '%s_%s' %
                (atomtype, 'scalingFactor')] * units.kilocalories_per_mole
            gbsa_force.addParticle(charge, radius, scalingFactor)

        # Add the force to the system.
        system.addForce(gbsa_force)

        # Build coordinate array.
        natoms = len(atoms)
        coordinates = units.Quantity(numpy.zeros([natoms, 3]), units.angstroms)
        for (index, atom) in enumerate(atoms):
            (x, y, z) = molecule.GetCoords(atom)
            coordinates[index, :] = units.Quantity(numpy.array([x, y, z]),
                                                   units.angstroms)

        # Create OpenMM Context.
        timestep = 1.0 * units.femtosecond  # arbitrary
        integrator = openmm.VerletIntegrator(timestep)
        context = openmm.Context(system, integrator, platform)

        # Set the coordinates.
        context.setPositions(coordinates)

        # Get the energy
        state = context.getState(getEnergy=True)
        energies[molecule] = state.getPotentialEnergy()

    return energies
Exemple #9
0
def WCADimer(N=natoms,
             density=density,
             mm=None,
             mass=mass,
             epsilon=epsilon,
             sigma=sigma,
             h=h,
             r0=r0,
             w=w):
    """
    Create a bistable bonded pair of particles (indices 0 and 1) optionally surrounded by a Weeks-Chandler-Andersen fluid.

    The bistable potential has form

    U(r) = h*(1-((r-r0-w)/w)^2)^2

    where r0 is the compact state separation, r0+2w is the extended state separation, and h is the barrier height.

    The WCA potential has form

    U(r) = 4 epsilon [ (sigma/r)^12 - (sigma/r)^6 ] + epsilon      (r < r*)
         = 0                                                       (r >= r*)

    where r* = 2^(1/6) sigma.

    OPTIONAL ARGUMENTS

    N (int) - total number of atoms (default: 2)
    density (float) - number density of particles (default: 0.96 / sigma**3)
    mass (simtk.unit.Quantity of mass) - particle mass (default: 39.948 amu)
    sigma (simtk.unit.Quantity of length) - Lennard-Jones sigma parameter (default: 0.3405 nm)
    epsilon (simtk.unit.Quantity of energy) - Lennard-Jones well depth (default: (119.8 Kelvin)*kB)
    h (simtk.unit.Quantity of energy) - bistable potential barrier height (default: ???)
    r0 (simtk.unit.Quantity of length) - bistable potential compact state separation (default: ???)
    w (simtk.unit.Quantity of length) - bistable potential extended state separation is r0+2*w (default: ???)

    """

    # Choose OpenMM package.
    if mm is None:
        mm = openmm

    # Compute cutoff for WCA fluid.
    r_WCA = 2.**(1. / 6.) * sigma  # cutoff at minimum of potential

    # Create system
    system = mm.System()

    # Compute total system volume.
    volume = N / density

    # Make system cubic in dimension.
    length = volume**(1.0 / 3.0)
    a = units.Quantity(numpy.array([1.0, 0.0, 0.0], numpy.float32),
                       units.nanometer) * length / units.nanometer
    b = units.Quantity(numpy.array([0.0, 1.0, 0.0], numpy.float32),
                       units.nanometer) * length / units.nanometer
    c = units.Quantity(numpy.array([0.0, 0.0, 1.0], numpy.float32),
                       units.nanometer) * length / units.nanometer
    print "box edge length = %s" % str(length)
    system.setDefaultPeriodicBoxVectors(a, b, c)

    # Add particles to system.
    for n in range(N):
        system.addParticle(mass)

    # WCA: Lennard-Jones truncated at minim and shifted so potential is zero at cutoff.
    energy_expression = '4.0*epsilon*((sigma/r)^12 - (sigma/r)^6) + epsilon'

    # Create force.
    force = mm.CustomNonbondedForce(energy_expression)

    # Set epsilon and sigma global parameters.
    force.addGlobalParameter('epsilon', epsilon)
    force.addGlobalParameter('sigma', sigma)

    # Add particles
    for n in range(N):
        force.addParticle([])

    # Add exclusion between bonded particles.
    force.addExclusion(0, 1)

    # Set periodic boundary conditions with cutoff.
    if (N > 2):
        force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic)
    else:
        force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffNonPeriodic)
    print "setting cutoff distance to %s" % str(r_WCA)
    force.setCutoffDistance(r_WCA)

    # Add nonbonded force term to the system.
    system.addForce(force)

    # Add dimer potential to first two particles.
    dimer_force = openmm.CustomBondForce('h*(1-((r-r0-w)/w)^2)^2;')
    dimer_force.addGlobalParameter('h', h)  # barrier height
    dimer_force.addGlobalParameter('r0', r0)  # compact state separation
    dimer_force.addGlobalParameter('w', w)  # second minimum is at r0 + 2*w
    dimer_force.addBond(0, 1, [])
    system.addForce(dimer_force)

    # Create initial coordinates using random positions.
    coordinates = units.Quantity(numpy.random.rand(N, 3),
                                 units.nanometer) * (length / units.nanometer)

    # Reposition dimer particles at compact minimum.
    coordinates[0, :] *= 0.0
    coordinates[1, :] *= 0.0
    coordinates[1, 0] = r0

    # Return system and coordinates.
    return [system, coordinates]
 def __rmul__(self, other):
     """Multiply a Vec3 by a constant."""
     if unit.is_unit(other):
         return unit.Quantity(self, other)
     return Vec3(other * self[0], other * self[1], other * self[2])
Exemple #11
0
                '%s_%s' %
                (atomtype, 'scalingFactor')] * units.kilocalories_per_mole
        except Exception, exception:
            print "Cannot find parameters for atomtype '%s' in molecule '%s'" % (
                atomtype, molecule.GetTitle())
            print parameters.keys()
            raise exception

        gbsa_force.addParticle(charge, radius, scalingFactor)  #

    # Add the force to the system.
    system.addForce(gbsa_force)

    # Build coordinate array.
    natoms = len(atoms)
    coordinates = units.Quantity(numpy.zeros([natoms, 3]), units.angstroms)
    for (index, atom) in enumerate(atoms):
        (x, y, z) = molecule.GetCoords(atom)
        coordinates[index, :] = units.Quantity(numpy.array([x, y, z]),
                                               units.angstroms)

    # Create OpenMM Context.
    timestep = 1.0 * units.femtosecond  # arbitrary
    integrator = openmm.VerletIntegrator(timestep)
    context = openmm.Context(system, integrator, platform)

    # Set the coordinates.
    context.setPositions(coordinates)

    # Get the energy
    state = context.getState(getEnergy=True)
Exemple #12
0
    def _create_phase(self, phase, reference_system, positions, atom_indices, thermodynamic_state, protocols=None):
        """
        Create a repex object for a specified phase.

        Parameters
        ----------
        phase : str
           The phase being initialized (one of ['complex', 'solvent', 'vacuum'])
        reference_system : simtk.openmm.System
           The reference system object from which alchemical intermediates are to be construcfted.
        positions : list of simtk.unit.Qunatity objects containing (natoms x 3) positions (as np or lists)
           The list of positions to be used to seed replicas in a round-robin way.
        atom_indices : dict
           atom_indices[phase][component] is the set of atom indices associated with component, where component
           is ['ligand', 'receptor', 'complex', 'solvent', 'ligand_counterions']
        thermodynamic_state : ThermodynamicState
           Thermodynamic state from which reference temperature and pressure are to be taken.
        protocols : dict of list of AlchemicalState, optional, default=None
           If specified, the alchemical protocol protocols[phase] will be used for phase 'phase' instead of the default.

        """

        # We add default repex options only on creation, on resume repex will pick them from the store file
        repex_parameters = {
            'number_of_equilibration_iterations': 0,
            'number_of_iterations': 100,
            'timestep': 2.0 * unit.femtoseconds,
            'collision_rate': 5.0 / unit.picoseconds,
            'minimize': False,
            'show_mixing_statistics': True,  # this causes slowdown with iteration and should not be used for production
            'displacement_sigma': 1.0 * unit.nanometers  # attempt to displace ligand by this stddev will be made each iteration
        }
        repex_parameters.update(self._repex_parameters)

        # Make sure positions argument is a list of coordinate snapshots.
        if hasattr(positions, 'unit'):
            # Wrap in list.
            positions = [positions]

        # Check the dimensions of positions.
        for index in range(len(positions)):
            # Make sure it is recast as a np array.
            positions[index] = unit.Quantity(np.array(positions[index] / positions[index].unit), positions[index].unit)

            [natoms, ndim] = (positions[index] / positions[index].unit).shape
            if natoms != reference_system.getNumParticles():
                raise Exception("positions argument must be a list of simtk.unit.Quantity of (natoms,3) lists or np array with units compatible with nanometers.")

        # Create metadata storage.
        metadata = dict()

        # Make a deep copy of the reference system so we don't accidentally modify it.
        reference_system = copy.deepcopy(reference_system)

        # TODO: Use more general approach to determine whether system is periodic.
        is_periodic = self._is_periodic(reference_system)

        # Make sure pressure is None if not periodic.
        if not is_periodic: thermodynamic_state.pressure = None

        # Compute standard state corrections for complex phase.
        metadata['standard_state_correction'] = 0.0
        # TODO: Do we need to include a standard state correction for other phases in periodic boxes?
        if phase == 'complex-implicit':
            # Impose restraints for complex system in implicit solvent to keep ligand from drifting too far away from receptor.
            logger.debug("Creating receptor-ligand restraints...")
            reference_positions = positions[0]
            if self._restraint_type == 'harmonic':
                restraints = HarmonicReceptorLigandRestraint(thermodynamic_state, reference_system, reference_positions, atom_indices['receptor'], atom_indices['ligand'])
            elif self._restraint_type == 'flat-bottom':
                restraints = FlatBottomReceptorLigandRestraint(thermodynamic_state, reference_system, reference_positions, atom_indices['receptor'], atom_indices['ligand'])
            else:
                raise Exception("restraint_type of '%s' is not supported." % self._restraint_type)

            force = restraints.getRestraintForce() # Get Force object incorporating restraints
            reference_system.addForce(force)
            metadata['standard_state_correction'] = restraints.getStandardStateCorrection() # standard state correction in kT
        elif phase == 'complex-explicit':
            # For periodic systems, we do not use a restraint, but must add a standard state correction for the box volume.
            # TODO: What if the box volume fluctuates during the simulation?
            box_vectors = reference_system.getDefaultPeriodicBoxVectors()
            box_volume = thermodynamic_state._volume(box_vectors)
            STANDARD_STATE_VOLUME = 1660.53928 * unit.angstrom**3
            metadata['standard_state_correction'] = - np.log(STANDARD_STATE_VOLUME / box_volume)

        # Use default alchemical protocols if not specified.
        if not protocols:
            protocols = self.default_protocols

        # Create alchemically-modified states using alchemical factory.
        logger.debug("Creating alchemically-modified states...")
        try:
            alchemical_indices = atom_indices['ligand_counterions'] + atom_indices['ligand']
        except KeyError:
            alchemical_indices = atom_indices['ligand']
        factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=alchemical_indices,
                                            **self._alchemy_parameters)
        alchemical_states = protocols[phase]
        alchemical_system = factory.alchemically_modified_system
        thermodynamic_state.system = alchemical_system

        # Check systems for finite energies.
        # TODO: Refactor this into another function.
        finite_energy_check = False
        if finite_energy_check:
            logger.debug("Checking energies are finite for all alchemical systems.")
            integrator = openmm.VerletIntegrator(1.0 * unit.femtosecond)
            context = openmm.Context(alchemical_system, integrator)
            context.setPositions(positions[0])
            for alchemical_state in alchemical_states:
                AbsoluteAlchemicalFactory.perturbContext(context, alchemical_state)
                potential = context.getState(getEnergy=True).getPotentialEnergy()
                if np.isnan(potential / unit.kilocalories_per_mole):
                    raise Exception("Energy for system %d is NaN." % index)
            del context, integrator
            logger.debug("All energies are finite.")

        # Randomize ligand position if requested, but only for implicit solvent systems.
        if self._randomize_ligand and (phase == 'complex-implicit'):
            logger.debug("Randomizing ligand positions and excluding overlapping configurations...")
            randomized_positions = list()
            nstates = len(alchemical_states)
            for state_index in range(nstates):
                positions_index = np.random.randint(0, len(positions))
                current_positions = positions[positions_index]
                new_positions = ModifiedHamiltonianExchange.randomize_ligand_position(current_positions,
                                                                                      atom_indices['receptor'], atom_indices['ligand'],
                                                                                      self._randomize_ligand_sigma_multiplier * restraints.getReceptorRadiusOfGyration(),
                                                                                      self._randomize_ligand_close_cutoff)
                randomized_positions.append(new_positions)
            positions = randomized_positions
        if self._randomize_ligand and (phase == 'complex-explicit'):
            logger.warning("Ligand randomization requested, but will not be performed for explicit solvent simulations.")

        # Identify whether any atoms will be displaced via MC, unless option is turned off.
        mc_atoms = None
        if self._mc_displacement_sigma:
            mc_atoms = list()
            if 'ligand' in atom_indices:
                mc_atoms = atom_indices['ligand']

        # Set up simulation.
        # TODO: Support MPI initialization?
        logger.debug("Creating replica exchange object...")
        store_filename = os.path.join(self._store_directory, phase + '.nc')
        self._store_filenames[phase] = store_filename
        simulation = ModifiedHamiltonianExchange(store_filename)
        simulation.create(thermodynamic_state, alchemical_states, positions,
                          displacement_sigma=self._mc_displacement_sigma, mc_atoms=mc_atoms,
                          options=repex_parameters, metadata=metadata)

        # Initialize simulation.
        # TODO: Use the right scheme for initializing the simulation without running.
        #logger.debug("Initializing simulation...")
        #simulation.run(0)

        # Clean up simulation.
        del simulation

        # Add to list of phases that have been set up.
        self._phases.append(phase)

        return
Exemple #13
0
import os
import numpy as np
import matplotlib.pyplot as pyplot
from simtk import unit
from simtk.openmm.app.pdbfile import PDBFile
import mdtraj as md
from cg_openmm.cg_model.cgmodel import CGModel
from cg_openmm.parameters.reweight import *
from cg_openmm.ensembles.ens_build import *
from cg_openmm.parameters.secondary_structure import *
from cg_openmm.simulation.tools import *

# Define Boltzmann's constant
kB = unit.Quantity(0.0019872041, unit.kilocalorie_per_mole)

total_simulation_time = 100.0 * unit.picosecond
simulation_time_step = 5.0 * unit.femtosecond
print_frequency = 5
temperature = 300.0
kT = kB * temperature
output_directory = "scan_T_output"
if not os.path.exists(output_directory):
    os.mkdir(output_directory)

# Model settings
polymer_length = 12
backbone_lengths = [1]
sidechain_lengths = [1]
sidechain_positions = [0]
include_bond_forces = False
include_bond_angle_forces = False
def trajectory_from_mdtraj(mdtrajectory, simple_topology=False,
                           velocities=None):
    """
    Construct a Trajectory object from an mdtraj.Trajectory object

    Parameters
    ----------
    mdtrajectory : mdtraj.Trajectory
        Input mdtraj.Trajectory
    simple_topology : bool
        if `True` only a simple topology with n_atoms will be created.
        This cannot be used with complex CVs but loads and stores very fast
    velocities : np.array
        velocities in units of nm/ps

    Returns
    -------
    openpathsampling.engines.Trajectory
        the constructed Trajectory instance
    """
    trajectory = Trajectory()
    vel_unit = u.nanometer / u.picosecond

    if simple_topology:
        topology = Topology(*mdtrajectory.xyz[0].shape)
    else:
        topology = MDTrajTopology(mdtrajectory.topology)

    if velocities is None:
        empty_vel = u.Quantity(np.zeros(mdtrajectory.xyz[0].shape),
                               vel_unit)


    engine = TopologyEngine(topology)

    for frame_num in range(len(mdtrajectory)):
        # mdtraj trajectories only have coordinates and box_vectors
        coord = u.Quantity(mdtrajectory.xyz[frame_num], u.nanometers)
        if velocities is not None:
            vel = u.Quantity(velocities[frame_num], vel_unit)
        else:
            vel = empty_vel

        if mdtrajectory.unitcell_vectors is not None:
            box_v = u.Quantity(mdtrajectory.unitcell_vectors[frame_num],
                               u.nanometers)
        else:
            box_v = None

        statics = Snapshot.StaticContainer(
            coordinates=coord,
            box_vectors=box_v
        )
        kinetics = Snapshot.KineticContainer(velocities=vel)

        snap = Snapshot(
            statics=statics,
            kinetics=kinetics,
            engine=engine
        )
        trajectory.append(snap)

    return trajectory
Exemple #15
0
    def _run(self, state: interfaces.IState, minimize: bool) -> interfaces.IState:
        # update the transformers to account for sampled parameters
        # stored in the state
        self._transformers_update(state)

        assert abs(state.alpha - self._alpha) < 1e-6

        # Run Monte Carlo position updates
        if minimize:
            state = self._run_min_mc(state)
        else:
            state = self._run_mc(state)

        # Run MonteCarlo parameter updates
        state = self._run_param_mc(state)

        coordinates = u.Quantity(state.positions, u.nanometer)
        velocities = u.Quantity(state.velocities, u.nanometer / u.picosecond)
        box_vectors = u.Quantity(state.box_vector, u.nanometer)

        # set the positions
        self._simulation.context.setPositions(coordinates)

        # if explicit solvent, then set the box vectors
        if self._options.solvation == "explicit":
            self._simulation.context.setPeriodicBoxVectors(
                [box_vectors[0].value_in_unit(u.nanometer), 0.0, 0.0],
                [0.0, box_vectors[1].value_in_unit(u.nanometer), 0.0],
                [0.0, 0.0, box_vectors[2].value_in_unit(u.nanometer)],
            )

        # run energy minimization
        if minimize:
            self._simulation.minimizeEnergy(maxIterations=self._options.minimize_steps)

        # set the velocities
        self._simulation.context.setVelocities(velocities)

        # run timesteps
        self._simulation.step(self._options.timesteps)

        # extract coords, vels, energy and strip units
        if self._options.solvation == "implicit":
            snapshot = self._simulation.context.getState(
                getPositions=True, getVelocities=True, getEnergy=True
            )
        elif self._options.solvation == "explicit":
            snapshot = self._simulation.context.getState(
                getPositions=True,
                getVelocities=True,
                getEnergy=True,
                enforcePeriodicBox=True,
            )
        coordinates = snapshot.getPositions(asNumpy=True).value_in_unit(u.nanometer)
        velocities = snapshot.getVelocities(asNumpy=True).value_in_unit(
            u.nanometer / u.picosecond
        )
        _check_for_nan(coordinates, velocities, self._rank)

        # if explicit solvent, the recover the box vectors
        if self._options.solvation == "explicit":
            box_vector = snapshot.getPeriodicBoxVectors().value_in_unit(u.nanometer)
            box_vector = np.array(
                (box_vector[0][0], box_vector[1][1], box_vector[2][2])
            )
        # just store zeros for implicit solvent
        else:
            box_vector = np.zeros(3)

        # get the energy
        e_potential = (
            snapshot.getPotentialEnergy().value_in_unit(u.kilojoule / u.mole)
            / GAS_CONSTANT
            / self._temperature
        )

        # store in state
        state.positions = coordinates
        state.velocities = velocities
        state.energy = e_potential
        state.box_vector = box_vector

        return state
Exemple #16
0
def WCAFluid(N=natoms,
             density=density,
             mm=None,
             mass=mass,
             epsilon=epsilon,
             sigma=sigma):
    """
    Create a Weeks-Chandler-Andersen system.

    OPTIONAL ARGUMENTS

    N (int) - total number of atoms (default: 150)
    density (float) - N sigma^3 / V (default: 0.96)
    sigma
    epsilon

    """

    # Choose OpenMM package.
    if mm is None:
        mm = openmm

    # Create system
    system = mm.System()

    # Compute total system volume.
    volume = N / density

    # Make system cubic in dimension.
    length = volume**(1.0 / 3.0)
    # TODO: Can we change this to use tuples or 3x3 array?
    a = units.Quantity(numpy.array([1.0, 0.0, 0.0], numpy.float32),
                       units.nanometer) * length / units.nanometer
    b = units.Quantity(numpy.array([0.0, 1.0, 0.0], numpy.float32),
                       units.nanometer) * length / units.nanometer
    c = units.Quantity(numpy.array([0.0, 0.0, 1.0], numpy.float32),
                       units.nanometer) * length / units.nanometer
    print "box edge length = %s" % str(length)
    system.setDefaultPeriodicBoxVectors(a, b, c)

    # Add particles to system.
    for n in range(N):
        system.addParticle(mass)

    # Create nonbonded force term implementing Kob-Andersen two-component Lennard-Jones interaction.
    energy_expression = '4.0*epsilon*((sigma/r)^12 - (sigma/r)^6) + epsilon'

    # Create force.
    force = mm.CustomNonbondedForce(energy_expression)

    # Set epsilon and sigma global parameters.
    force.addGlobalParameter('epsilon', epsilon)
    force.addGlobalParameter('sigma', sigma)

    # Add particles
    for n in range(N):
        force.addParticle([])

    # Set periodic boundary conditions with cutoff.
    force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffNonPeriodic)
    print "setting cutoff distance to %s" % str(r_WCA)
    force.setCutoffDistance(r_WCA)

    # Add nonbonded force term to the system.
    system.addForce(force)

    # Create initial coordinates using random positions.
    coordinates = units.Quantity(numpy.random.rand(N, 3),
                                 units.nanometer) * (length / units.nanometer)

    # Return system and coordinates.
    return [system, coordinates]
Exemple #17
0
def run_endpoint_perturbation(lambda_thermodynamic_state,
                              nonalchemical_thermodynamic_state,
                              initial_hybrid_sampler_state,
                              mc_move,
                              n_iterations,
                              factory,
                              lambda_index=0,
                              print_work=False,
                              write_system=False,
                              write_state=False,
                              write_trajectories=False):
    """

    Parameters
    ----------
    lambda_thermodynamic_state : ThermodynamicState
        The thermodynamic state corresponding to the hybrid system at a lambda endpoint
    nonalchemical_thermodynamic_state : ThermodynamicState
        The nonalchemical thermodynamic state for the relevant endpoint
    initial_hybrid_sampler_state : SamplerState
        Starting positions for the sampler. Must be compatible with lambda_thermodynamic_state
    mc_move : MCMCMove
        The MCMove that will be used for sampling at the lambda endpoint
    n_iterations : int
        The number of iterations
    factory : HybridTopologyFactory
        The hybrid topology factory
    lambda_index : int, optional, default=0
        The index, 0 or 1, at which to retrieve nonalchemical positions
    print_work : bool, optional, default=False
        If True, will print work values
    write_system : bool, optional, default=False
        If True, will write alchemical and nonalchemical System XML files
    write_state : bool, optional, default=False
        If True, write alchemical (hybrid) State XML files each iteration
    write_trajectories : bool, optional, default=False
        If True, will write trajectories

    Returns
    -------
    df : float
        Free energy difference between alchemical and nonalchemical systems, estimated with EXP
    ddf : float
        Standard deviation of estimate, corrected for correlation, from EXP estimator.
    """
    import mdtraj as md

    #run an initial minimization:
    mcmc_sampler = mcmc.MCMCSampler(lambda_thermodynamic_state,
                                    initial_hybrid_sampler_state, mc_move)
    mcmc_sampler.minimize(max_iterations=20)
    new_sampler_state = mcmc_sampler.sampler_state

    if write_system:
        with open(f'hybrid{lambda_index}-system.xml', 'w') as outfile:
            outfile.write(
                openmm.XmlSerializer.serialize(
                    lambda_thermodynamic_state.system))
        with open(f'nonalchemical{lambda_index}-system.xml', 'w') as outfile:
            outfile.write(
                openmm.XmlSerializer.serialize(
                    nonalchemical_thermodynamic_state.system))

    #initialize work array
    w = np.zeros([n_iterations])
    non_potential = np.zeros([n_iterations])
    hybrid_potential = np.zeros([n_iterations])

    #run n_iterations of the endpoint perturbation:
    hybrid_trajectory = unit.Quantity(
        np.zeros([
            n_iterations,
            lambda_thermodynamic_state.system.getNumParticles(), 3
        ]), unit.nanometers)  # DEBUG
    nonalchemical_trajectory = unit.Quantity(
        np.zeros([
            n_iterations,
            nonalchemical_thermodynamic_state.system.getNumParticles(), 3
        ]), unit.nanometers)  # DEBUG
    for iteration in range(n_iterations):
        # Generate a new sampler state for the hybrid system
        mc_move.apply(lambda_thermodynamic_state, new_sampler_state)

        # Compute the hybrid reduced potential at the new sampler state
        hybrid_context, integrator = cache.global_context_cache.get_context(
            lambda_thermodynamic_state)
        new_sampler_state.apply_to_context(hybrid_context,
                                           ignore_velocities=True)
        hybrid_reduced_potential = lambda_thermodynamic_state.reduced_potential(
            hybrid_context)

        if write_state:
            state = hybrid_context.getState(getPositions=True,
                                            getParameters=True)
            state_xml = openmm.XmlSerializer.serialize(state)
            with open(f'state{iteration}_l{lambda_index}.xml', 'w') as outfile:
                outfile.write(state_xml)

        # Construct a sampler state for the nonalchemical system
        if lambda_index == 0:
            nonalchemical_positions = factory.old_positions(
                new_sampler_state.positions)
        elif lambda_index == 1:
            nonalchemical_positions = factory.new_positions(
                new_sampler_state.positions)
        else:
            raise ValueError(
                "The lambda index needs to be either one or zero for this to be meaningful"
            )
        nonalchemical_sampler_state = SamplerState(
            nonalchemical_positions, box_vectors=new_sampler_state.box_vectors)

        if write_trajectories:
            state = hybrid_context.getState(getPositions=True)
            hybrid_trajectory[iteration, :, :] = state.getPositions(
                asNumpy=True)
            nonalchemical_trajectory[iteration, :, :] = nonalchemical_positions

        # Compute the nonalchemical reduced potential
        nonalchemical_context, integrator = cache.global_context_cache.get_context(
            nonalchemical_thermodynamic_state)
        nonalchemical_sampler_state.apply_to_context(nonalchemical_context,
                                                     ignore_velocities=True)
        nonalchemical_reduced_potential = nonalchemical_thermodynamic_state.reduced_potential(
            nonalchemical_context)

        # Compute and store the work
        w[iteration] = nonalchemical_reduced_potential - hybrid_reduced_potential
        non_potential[iteration] = nonalchemical_reduced_potential
        hybrid_potential[iteration] = hybrid_reduced_potential

        if print_work:
            print(
                f'{iteration:8d} {hybrid_reduced_potential:8.3f} {nonalchemical_reduced_potential:8.3f} => {w[iteration]:8.3f}'
            )

    if write_trajectories:
        if lambda_index == 0:
            nonalchemical_mdtraj_topology = md.Topology.from_openmm(
                factory._topology_proposal.old_topology)
        elif lambda_index == 1:
            nonalchemical_mdtraj_topology = md.Topology.from_openmm(
                factory._topology_proposal.new_topology)
        md.Trajectory(
            hybrid_trajectory / unit.nanometers,
            factory.hybrid_topology).save(f'hybrid{lambda_index}.pdb')
        md.Trajectory(nonalchemical_trajectory / unit.nanometers,
                      nonalchemical_mdtraj_topology).save(
                          f'nonalchemical{lambda_index}.pdb')

    # Analyze data and return results
    [t0, g, Neff_max] = timeseries.detectEquilibration(w)
    w_burned_in = w[t0:]
    [df, ddf] = pymbar.EXP(w_burned_in)
    ddf_corrected = ddf * np.sqrt(g)
    results = [df, ddf_corrected, t0, Neff_max]

    return results, non_potential, hybrid_potential
Exemple #18
0
def strip_units(coords):
    return unit.Quantity(np.array(coords / coords.unit), coords.unit)
Exemple #19
0
from __future__ import print_function, absolute_import
import os
import shutil
import numpy as np
import xml.etree.ElementTree as ET

import simtk.unit as unit
import simtk.openmm as omm
import simtk.openmm.app as app

import simulation.openmm.util as util
import simulation.openmm.additional_reporters as additional_reporters

global energy_minimization_tol
energy_minimization_tol = unit.Quantity(value=10., unit=unit.kilojoule_per_mole)

def adaptively_find_best_pressure(target_volume, ff_files, name, n_beads,
        cutoff, r_switch, refT, save_forces=False, cuda=False, p0=4000.):
    """Adaptively change pressure to reach target volume (density)"""

    temperature = refT*unit.kelvin
    collision_rate = 1.0/unit.picosecond
    timestep = 0.002*unit.picosecond
    n_steps = 5000
    nsteps_out = 100
    pressure = p0*unit.atmosphere    # starting pressure

    dynamics = "Langevin"
    ensemble = "NPT"

    traj_idx = 1
Exemple #20
0
def createAmberInputFiles(topology, system, state, nproteinatoms, openmm_forcefields_to_use, amber_forcefield_to_use, prmtop_filename, inpcrd_filename, verbose=False, shell='/bin/tcsh'):
    '''
    Create AMBER input files from an OpenMM system.

    ARGUMENTS

    topology (simtk.openmm.app.Topology) - the topology of the system to be simulated
    system (simtk.openmm.System) - the System object for the system to be simulated
    state (simtk.openmm.State) - the State of the system to be simulated
    nproteinatoms (int) - number of protein atoms (required for centering)
    openmm_forcefields_to_use (list of string) - list of forcefield XML files to use in generating AMBER translation names
    amber_forcefield_to_use (string) - protein force field to be used in LEaP
    prmtop_filename (string) - the filename of the AMBER prmtop file to be created
    inpcrd_filename (string) - the filename of the AMBER inpcrd file to be created

    OPTIONAL ARGUMENTS

    verbose (boolean) - if True, will print verbose output

    NOTES

    Note that AmberTools must be installed.  LEaP is used to create the input files.

    TODO

    * ?Use a single forcefields_to_use argument to automatically select the appropriate AMBER and OpenMM forcefield files.
    * Use temporary directory and copy out to desired filenames.
    * Decide on whether to keep ambernames.pdb or amber.pdb in each directory

    RETURNS

    None

    '''

    from simtk import openmm
    from simtk import unit

    # Create box and center protein
    if verbose: print("Creating box and centering protein in unit cell...")
    integrator = openmm.VerletIntegrator(1.0 * unit.femtoseconds)
    platform = openmm.Platform.getPlatformByName('Reference')
    context = openmm.Context(system, integrator, platform)
    context.setState(state)
    state = context.getState(getPositions=True, enforcePeriodicBox=True)
    positions = state.getPositions(asNumpy=True)
    box_vectors = state.getPeriodicBoxVectors(asNumpy=True)
    mean = unit.Quantity((positions[0:nproteinatoms,:] / unit.angstrom).mean(0), unit.angstroms)
    for i in range(system.getNumParticles()):
        positions[i,:] -= mean[:] + box_vectors[0,0]/2.0
    context.setPositions(positions)
    state = context.getState(getPositions=True, enforcePeriodicBox=True)
    positions = state.getPositions(asNumpy=True)
    del context

    # Configure text for LEaP input file
    leap_template = '''
# Set up solvated protein system for explicit solvent simulation.

# Load AMBER ff99sb-ildn forcefield for protein.
source %(amber_forcefield_to_use)s

# Load modified ion parameters.
loadAmberParams frcmod.ionsjc_tip3p

# Load in protein (with all residues modeled in).
system = loadPdb ambernames.pdb

# Generate box.
solvatebox system TIP3PBOX 0.0001 10000 iso

# Check protein.
check system

# Report on net charge.
charge system

# Write parameters.
saveAmberParm system amber.prmtop amber.crd

# Exit
quit
''' % vars()

    # Write LEaP input file.
    if verbose: print("Writing LEaP input file...")
    leap_filename = 'setup.leap.in'
    outfile = open(leap_filename, 'w')
    outfile.write(leap_template)
    outfile.close()

    # Clear leap.log.
    import os, os.path
    leap_log_filename = 'leap.log'
    if os.path.exists(leap_log_filename):
        os.remove(leap_log_filename)

    # Determine atom names.
    (atoms, sorted_atom_indices) = _assignNamesFromForceFieldTemplates(topology, system, openmm_forcefields_to_use)

    # Re-sort atoms.
    resort_atoms = True
    if resort_atoms:
        import copy
        atoms2 = copy.deepcopy(atoms)
        positions2 = copy.deepcopy(positions)
        for (new_index, old_index) in enumerate(sorted_atom_indices):
            atoms2[new_index] = atoms[old_index]
            atoms2[new_index].index = new_index+1
            positions2[new_index,:] = positions[old_index,:]

        atoms = atoms2
        positions = positions2

    # Write PDB file with AMBER names for atoms and residues.
    amberpdb_filename = 'ambernames.pdb'
    outfile = open(amberpdb_filename, 'w')
    box_vectors = state.getPeriodicBoxVectors(asNumpy=True)
    index = 0
    wrap_coordinates = False
    for atom in atoms:
        residue = atom.residue
        atomIndex = atom.index
        atomName = atom.name
        resName = atom.resname
        chainName = chr(ord('A') + residue.chain.index)
        resIndex = residue.index

        if len(resName) > 3:
            resName = resName[1:]
        outfile.write("ATOM  %5d %-4s %3s %s%4d    %8.3f%8.3f%8.3f  1.00  0.00\n" % (atomIndex%100000, atomName, resName, chainName, (resIndex+1)%10000, positions[index,0]/unit.angstrom, positions[index,1]/unit.angstrom, positions[index,2]/unit.angstrom))
        index += 1
    outfile.close()

    # Run tleap.
    import subprocess
    command = 'setenv AMBERHOME $AMBERHOME_CPU; tleap -f setup.leap.in >& setup.leap.out'
    try:
        output = subprocess.check_output(command, shell=True, executable=shell, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        print(e, 'with output:\n' + e.output)
        raise e
    if verbose: print(output)

    # Write periodic AMBER .crd file.
    overwrite_crd = True
    if overwrite_crd:
        inpcrd_filename = 'amber.crd'
        outfile = open(inpcrd_filename, 'w')
        outfile.write('Automatically converted from OpenMM\n')
        natoms = system.getNumParticles()
        outfile.write('%6d\n' % natoms)
        nwritten = 0
        for i in range(natoms):
            if nwritten == 6:
                outfile.write('\n')
                nwritten = 0
            for k in range(3):
                outfile.write('%12.7f' % (positions[i][k] / unit.angstroms))
                nwritten += 1
        outfile.write('\n')
        for k in range(3):
            outfile.write('%12.7f' % (box_vectors[k][k]/unit.angstroms))
        for k in range(3):
            outfile.write('%12.7f' % 90.0)
        outfile.write('\n')
        outfile.close()

        # Generate PDB file.
        import subprocess
        command = 'cat amber.crd | ambpdb -p amber.prmtop -aatm > amber.pdb'
        try:
            output = subprocess.check_output(command, shell=True, executable=shell, stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as e:
            print(e, 'with output:\n' + e.output)
            raise e
        print(output)

    return
    def test_to_from_rdkit_core_props_filled(self):
        """Test RDKitToolkitWrapper to_rdkit() and from_rdkit() when given populated core property fields"""
        toolkit_wrapper = RDKitToolkitWrapper()

        # Replacing with a simple molecule with stereochemistry
        input_smiles = r'C\C(F)=C(/F)C[C@@](C)(Cl)Br'
        expected_output_smiles = r'[H][C]([H])([H])/[C]([F])=[C](\[F])[C]([H])([H])[C@@]([Cl])([Br])[C]([H])([H])[H]'
        molecule = Molecule.from_smiles(input_smiles,
                                        toolkit_registry=toolkit_wrapper)
        assert molecule.to_smiles(
            toolkit_registry=toolkit_wrapper) == expected_output_smiles

        # Populate core molecule property fields
        molecule.name = 'Alice'
        partial_charges = unit.Quantity(
            np.array([
                -.9, -.8, -.7, -.6, -.5, -.4, -.3, -.2, -.1, 0., .1, .2, .3,
                .4, .5, .6, .7, .8
            ]), unit.elementary_charge)
        molecule.partial_charges = partial_charges
        coords = unit.Quantity(
            np.array([['0.0', '1.0', '2.0'], ['3.0', '4.0', '5.0'],
                      ['6.0', '7.0', '8.0'], ['9.0', '10.0', '11.0'],
                      ['12.0', '13.0', '14.0'], ['15.0', '16.0', '17.0'],
                      ['18.0', '19.0', '20.0'], ['21.0', '22.0', '23.0'],
                      ['24.0', '25.0', '26.0'], ['27.0', '28.0', '29.0'],
                      ['30.0', '31.0', '32.0'], ['33.0', '34.0', '35.0'],
                      ['36.0', '37.0', '38.0'], ['39.0', '40.0', '41.0'],
                      ['42.0', '43.0', '44.0'], ['45.0', '46.0', '47.0'],
                      ['48.0', '49.0', '50.0'], ['51.0', '52.0', '53.0']]),
            unit.angstrom)
        molecule.add_conformer(coords)
        # Populate core atom property fields
        molecule.atoms[2].name = 'Bob'
        # Ensure one atom has its stereochemistry specified
        central_carbon_stereo_specified = False
        for atom in molecule.atoms:
            if (atom.atomic_number == 6) and atom.stereochemistry == "S":
                central_carbon_stereo_specified = True
        assert central_carbon_stereo_specified

        # Populate bond core property fields
        fractional_bond_orders = [float(val) for val in range(18)]
        for fbo, bond in zip(fractional_bond_orders, molecule.bonds):
            bond.fractional_bond_order = fbo

        # Do a first conversion to/from oemol
        rdmol = molecule.to_rdkit()
        molecule2 = Molecule.from_rdkit(rdmol)

        # Test that properties survived first conversion
        #assert molecule.to_dict() == molecule2.to_dict()
        assert molecule.name == molecule2.name
        # NOTE: This expects the same indexing scheme in the original and new molecule

        central_carbon_stereo_specified = False
        for atom in molecule2.atoms:
            if (atom.atomic_number == 6) and atom.stereochemistry == "S":
                central_carbon_stereo_specified = True
        assert central_carbon_stereo_specified
        for atom1, atom2 in zip(molecule.atoms, molecule2.atoms):
            assert atom1.to_dict() == atom2.to_dict()
        for bond1, bond2 in zip(molecule.bonds, molecule2.bonds):
            assert bond1.to_dict() == bond2.to_dict()
        assert (molecule._conformers[0] == molecule2._conformers[0]).all()
        for pc1, pc2 in zip(molecule._partial_charges,
                            molecule2._partial_charges):
            pc1_ul = pc1 / unit.elementary_charge
            pc2_ul = pc2 / unit.elementary_charge
            assert_almost_equal(pc1_ul, pc2_ul, decimal=6)
        assert molecule2.to_smiles(
            toolkit_registry=toolkit_wrapper) == expected_output_smiles
Exemple #22
0
def test_overlap():
    """
    BUGS TO REPORT:
    * Even if epsilon = 0, energy of two overlapping atoms is 'nan'.
    * Periodicity in 'nan' if dr = 0.1 even in nonperiodic system
    """

    # Create a reference system.    
    import testsystems

    print "Creating Lennard-Jones cluster system..."
    #[reference_system, coordinates] = testsystems.LennardJonesFluid()
    #receptor_atoms = [0]
    #alchemical_atoms = [1]

    [reference_system, coordinates] = testsystems.LysozymeImplicit()
    receptor_atoms = range(0,2603) # T4 lysozyme L99A
    alchemical_atoms = range(2603,2621) # p-xylene

    import simtk.unit as units
    unit = coordinates.unit
    coordinates = units.Quantity(numpy.array(coordinates / unit), unit)

    factory = AbsoluteAlchemicalFactory(reference_system, alchemical_atoms=alchemical_atoms)
    alchemical_state = AlchemicalState(0.00, 0.00, 0.00, 1.0)

    # Create the perturbed system.
    print "Creating alchemically-modified state..."
    alchemical_system = factory.createPerturbedSystem(alchemical_state)    
    # Compare energies.
    import simtk.unit as units
    import simtk.openmm as openmm
    timestep = 1.0 * units.femtosecond
    print "Computing reference energies..."
    integrator = openmm.VerletIntegrator(timestep)
    context = openmm.Context(reference_system, integrator)
    context.setPositions(coordinates)
    state = context.getState(getEnergy=True)
    reference_potential = state.getPotentialEnergy()    
    del state, context, integrator
    print reference_potential
    print "Computing alchemical energies..."
    integrator = openmm.VerletIntegrator(timestep)
    context = openmm.Context(alchemical_system, integrator)
    dr = 0.1 * units.angstroms # TODO: Why does 0.1 cause periodic 'nan's?
    a = receptor_atoms[-1]
    b = alchemical_atoms[-1]
    delta = coordinates[a,:] - coordinates[b,:]
    for k in range(3):
        coordinates[alchemical_atoms,k] += delta[k]
    for i in range(30):
        r = dr * i
        coordinates[alchemical_atoms,0] += dr
          
        context.setPositions(coordinates)
        state = context.getState(getEnergy=True)
        alchemical_potential = state.getPotentialEnergy()    
        print "%8.3f A : %f " % (r / units.angstroms, alchemical_potential / units.kilocalories_per_mole)
    del state, context, integrator

    return
def SMD(system, prmtop, platform, platformProperties, temperature, positions, velocities, keyInteraction, spring_k, dist_in, dist_fin, SMD_num, save_step, move_force_step):

    # See page 456 of http://ambermd.org/doc12/Amber17.pdf
    pullforce = mm.CustomExternalForce('k_sp*0.5*(dx^2+dy^2+dz^2); \
                                       dx=x-(x0+displace_x); \
                                       dy=y-(y0+displace_y); \
                                       dz=z-(z0+displace_z);')

    pullforce.addPerParticleParameter('k_sp')
    pullforce.addPerParticleParameter('x0')
    pullforce.addPerParticleParameter('y0')
    pullforce.addPerParticleParameter('z0')

    pullforce.addGlobalParameter("displace_x", 0.0 * u.nanometer)
    pullforce.addGlobalParameter("displace_y", 0.0 * u.nanometer)
    pullforce.addGlobalParameter("displace_z", 0.0 * u.nanometer)

    keyInteraction_pos = [positions[keyInteraction[0]], positions[keyInteraction[1]]]
    keyInteraction_dist = np.linalg.norm(keyInteraction_pos[0] - keyInteraction_pos[1])
    keyInteraction_vect = (keyInteraction_pos[1] - keyInteraction_pos[0]) / keyInteraction_dist
    keyInteraction_vect = u.Quantity(value=keyInteraction_vect, unit=u.nanometers)
    pullto = keyInteraction_pos[0] + 0.25 * keyInteraction_vect
    pullto_old = pullto

    pullforce.addParticle(keyInteraction[1], [spring_k, pullto[0], pullto[1], pullto[2] ])
    system.addForce(pullforce)

    integrator = mm.LangevinIntegrator(temperature, 4/u.picosecond, 0.002*u.picosecond)

    simulation = app.Simulation(prmtop.topology, system, integrator, platform,
                        platformProperties)
    simulation.context.setPositions(positions)
    simulation.context.setVelocities(velocities)

    force_val_old = -spring_k*(keyInteraction_dist - dist_in)
    energy_val_old = u.Quantity(value=0, unit=u.kilocalorie/u.mole)

    f=open('duck_'+str(temperature).split()[0].replace('.0','K')+'_'+str(SMD_num)+'.dat','w')
    steps = int((dist_fin.value_in_unit(u.nanometer) / 0.000001 - dist_in.value_in_unit(u.nanometer) / 0.000001)) / move_force_step
    pull_distance = 0.000001 * move_force_step
    
    #write trajectory 
    top = md.load_prmtop('system_solv.prmtop')
    atom_subset = top.select('not water')
    simulation.reporters.append(app.StateDataReporter("smd_"+str(temperature).split()[0].replace('.0','K')+"_"+str(SMD_num)+".csv", move_force_step, step=True, time=True, totalEnergy=True, kineticEnergy=True, potentialEnergy=True,
                                                      temperature=True, density=True, progress=True, totalSteps=move_force_step*steps, speed=True))
        
    simulation.reporters.append(HDF5Reporter("smd_"+str(temperature).split()[0].replace('.0','K')+"_"+str(SMD_num)+".h5", move_force_step*20, atomSubset=atom_subset))

    
    for i in range(steps):    
        state = simulation.context.getState(getPositions=True)
        pos_keyInt = state.getPositions()
        keyInteraction_pos = [pos_keyInt[keyInteraction[0]], pos_keyInt[keyInteraction[1]]]
    
        keyInteraction_dist = np.linalg.norm(keyInteraction_pos[0]-keyInteraction_pos[1])
        keyInteraction_vect = (keyInteraction_pos[1] - keyInteraction_pos[0]) / keyInteraction_dist
        keyInteraction_vect = u.Quantity(value=keyInteraction_vect, unit=u.nanometers)
        pullto = keyInteraction_pos[0] + (0.25 + float(i) * pull_distance) * keyInteraction_vect
    
        displace = pullto - pullto_old
    
        simulation.context.setParameter('displace_x', displace[0])
        simulation.context.setParameter('displace_y', displace[1])
        simulation.context.setParameter('displace_z', displace[2])
        if i == 0:
            distance = 0.0
        else:
            distance = pull_distance
        dist_spring =  (0.25 + float(i) * pull_distance) * u.nanometer
        force_val = -spring_k * (keyInteraction_dist - dist_spring)
        energy_val = energy_val_old + (distance * u.nanometer) * 0.5 * (force_val+force_val_old)
        force_val_old = force_val
        energy_val_old = energy_val
        if (i%int(save_step/move_force_step)) == 0:
            f.write(str(i)+' '+str(dist_spring)+' '+str(keyInteraction_dist)+' '+str(force_val)+' '+str(energy_val)+'\n')
        
        
        simulation.step(move_force_step)
        
    #f.write(str(i)+' '+str(dist_spring)+' '+str(keyInteraction_dist)+' '+str(force_val)+' '+str(energy_val)+'\n')
    f.close()
Exemple #24
0
def lambda_trace(reference_system,
                 positions,
                 receptor_atoms,
                 ligand_atoms,
                 platform_name=None,
                 precision=None,
                 annihilate_electrostatics=True,
                 annihilate_sterics=False,
                 nsteps=100):
    """
    Compute potential energy as a function of lambda.

    """
    # Create a factory to produce alchemical intermediates.
    factory = AbsoluteAlchemicalFactory(
        reference_system,
        ligand_atoms=ligand_atoms,
        annihilate_electrostatics=annihilate_electrostatics,
        annihilate_sterics=annihilate_sterics)

    platform = None
    if platform_name:
        # Get platform.
        platform = openmm.Platform.getPlatformByName(platform_name)

    if precision:
        if platform_name == 'CUDA':
            platform.setDefaultPropertyValue('CudaPrecision', precision)
        elif platform_name == 'OpenCL':
            platform.setDefaultPropertyValue('OpenCLPrecision', precision)

    # Take equally-sized steps.
    delta = 1.0 / nsteps

    def compute_potential(system, positions, platform=None):
        timestep = 1.0 * units.femtoseconds
        integrator = openmm.VerletIntegrator(timestep)
        if platform:
            context = openmm.Context(system, integrator, platform)
        else:
            context = openmm.Context(system, integrator)
        context.setPositions(positions)
        state = context.getState(getEnergy=True)
        potential = state.getPotentialEnergy()
        del integrator, context
        return potential

    # Compute unmodified energy.
    u_original = compute_potential(reference_system, positions, platform)

    # Scan through lambda values.
    lambda_i = np.zeros([nsteps + 1], np.float64)  # lambda values for u_i
    u_i = units.Quantity(np.zeros([nsteps + 1],
                                  np.float64), units.kilocalories_per_mole
                         )  # u_i[i] is the potential energy for lambda_i[i]
    for i in range(nsteps + 1):
        lambda_value = 1.0 - i * delta  # compute lambda value for this step
        alchemical_system = factory.createPerturbedSystem(
            AlchemicalState(0, lambda_value, lambda_value, lambda_value))
        lambda_i[i] = lambda_value
        u_i[i] = compute_potential(alchemical_system, positions, platform)
        print "%12.9f %24.8f kcal/mol" % (lambda_i[i],
                                          u_i[i] / units.kilocalories_per_mole)

    # Write figure as PDF.
    import pylab
    from matplotlib.backends.backend_pdf import PdfPages
    import matplotlib.pyplot as plt
    with PdfPages('lambda-trace.pdf') as pdf:
        fig = plt.figure(figsize=(10, 5))
        ax = fig.add_subplot(111)
        plt.plot(1,
                 u_original / units.kilocalories_per_mole,
                 'ro',
                 label='unmodified')
        plt.plot(lambda_i,
                 u_i / units.kilocalories_per_mole,
                 'k.',
                 label='alchemical')
        plt.title('T4 lysozyme L99A + p-xylene : AMBER96 + OBC GBSA')
        plt.ylabel('potential (kcal/mol)')
        plt.xlabel('lambda')
        ax.legend()
        rstyle(ax)
        pdf.savefig()  # saves the current figure into a pdf page
        plt.close()

    return
Exemple #25
0
def readAmberCoordinates(filename,
                         read_box=False,
                         read_velocities=False,
                         verbose=False,
                         asNumpy=False):
    """
    Read atomic coordinates (and optionally, box vectors) from Amber formatted coordinate file.

    ARGUMENTS

    filename (string) - name of Amber coordinates file to be read in
    system (simtk.openmm.System) - System object for which coordinates are to be read

    OPTIONAL ARGUMENTS

    verbose (boolean) - if True, will print out verbose information about the file being read
    asNumpy (boolean) - if True, results will be returned as Numpy arrays instead of lists of Vec3s

    EXAMPLES

    Read coordinates in vacuum.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa')
    >>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd')
    >>> coordinates = readAmberCoordinates(crd_filename)

    Read coordinates in solvent.

    >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit')
    >>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd')
    >>> [coordinates, box_vectors] = readAmberCoordinates(crd_filename, read_box=True)

    """

    # Open coordinate file for reading.
    infile = open(filename, 'r')

    # Read title
    title = infile.readline().strip()
    if verbose: print "title: '%s'" % title

    # Read number of atoms
    natoms = int(infile.readline().split()[0])
    if verbose: print "%d atoms" % natoms

    # Allocate storage for coordinates
    coordinates = []

    # Read coordinates
    mm = simtk.openmm
    natoms_read = 0
    while (natoms_read < natoms):
        line = infile.readline()
        if len(line) == 0:
            raise ValueError(
                "Unexpected end of file while reading coordinates")
        line = line.strip()
        elements = line.split()
        while (len(elements) > 0):
            coordinates.append(
                mm.Vec3(float(elements.pop(0)), float(elements.pop(0)),
                        float(elements.pop(0))))
            natoms_read += 1
    if asNumpy:
        newcoords = numpy.zeros([natoms, 3], numpy.float32)
        for i in range(len(coordinates)):
            for j in range(3):
                newcoords[i, j] = coordinates[i][j]
        coordinates = newcoords
    # Assign units.
    coordinates = units.Quantity(coordinates, units.angstroms)

    # Read velocities if requested.
    velocities = None
    if (read_velocities):
        # Read velocities
        velocities = []
        natoms_read = 0
        while (natoms_read < natoms):
            line = infile.readline()
            if len(line) == 0:
                raise ValueError(
                    "Unexpected end of file while reading velocities")
            line = line.strip()
            elements = line.split()
            while (len(elements) > 0):
                velocities.append(
                    20.455 *
                    mm.Vec3(float(elements.pop(0)), float(elements.pop(0)),
                            float(elements.pop(0))))
                natoms_read += 1
        if asNumpy:
            newvel = numpy.zeros([natoms, 3], numpy.float32)
            for i in range(len(velocities)):
                for j in range(3):
                    newvel[i, j] = velocities[i][j]
            velocities = newvel
        # Assign units.
        velocities = units.Quantity(velocities,
                                    units.angstroms / units.picoseconds)

    # Read box size if present
    box_vectors = None
    if (read_box):
        line = infile.readline()
        if len(line) == 0:
            raise ValueError(
                "Unexpected end of file while reading box vectors")
        line = line.strip()
        elements = line.split()
        nelements = len(elements)
        box_dimensions = [0.0] * nelements
        for i in range(nelements):
            box_dimensions[i] = float(elements[i])
        # TODO: Deal with non-standard box sizes.
        if nelements == 6:
            if asNumpy:
                a = units.Quantity(numpy.array([box_dimensions[0], 0.0, 0.0]),
                                   units.angstroms)
                b = units.Quantity(numpy.array([0.0, box_dimensions[1], 0.0]),
                                   units.angstroms)
                c = units.Quantity(numpy.array([0.0, 0.0, box_dimensions[2]]),
                                   units.angstroms)
            else:
                a = units.Quantity(mm.Vec3(box_dimensions[0], 0.0, 0.0),
                                   units.angstroms)
                b = units.Quantity(mm.Vec3(0.0, box_dimensions[1], 0.0),
                                   units.angstroms)
                c = units.Quantity(mm.Vec3(0.0, 0.0, box_dimensions[2]),
                                   units.angstroms)
            box_vectors = [a, b, c]
        else:
            raise Exception("Don't know what to do with box vectors: %s" %
                            line)

    # Close file
    infile.close()

    if box_vectors and velocities:
        return (coordinates, box_vectors, velocities)
    if box_vectors:
        return (coordinates, box_vectors)
    if velocities:
        return (coordinates, velocities)
    return coordinates
Exemple #26
0
    def execute(self, directory, available_resources):

        import mdtraj

        logging.info('Extracting dielectrics: ' + self.id)

        base_exception = super(ExtractAverageDielectric,
                               self).execute(directory, available_resources)

        if isinstance(base_exception, ExtractAverageDielectric):
            return base_exception

        charge_list = []

        from simtk.openmm import XmlSerializer

        with open(self._system_path, 'rb') as file:
            self._system = XmlSerializer.deserialize(file.read().decode())

        for force_index in range(self._system.getNumForces()):

            force = self._system.getForce(force_index)

            if not isinstance(force, openmm.NonbondedForce):
                continue

            for atom_index in range(force.getNumParticles()):

                charge = force.getParticleParameters(atom_index)[0]
                charge /= unit.elementary_charge

                charge_list.append(charge)

        dipole_moments = mdtraj.geometry.dipole_moments(
            self.trajectory, charge_list)

        dipole_moments, self._equilibration_index, self._statistical_inefficiency = \
            timeseries.decorrelate_time_series(dipole_moments)

        sample_indices = timeseries.get_uncorrelated_indices(
            len(self.trajectory[self._equilibration_index:]),
            self._statistical_inefficiency)

        sample_indices = [
            index + self._equilibration_index for index in sample_indices
        ]

        volumes = self.trajectory[sample_indices].unitcell_volumes

        self._uncorrelated_values = unit.Quantity(dipole_moments, None)
        self._uncorrelated_volumes = volumes * unit.nanometer**3

        value, uncertainty = bootstrap(self._bootstrap_function,
                                       self._bootstrap_iterations,
                                       self._bootstrap_sample_size,
                                       dipoles=dipole_moments,
                                       volumes=volumes)

        self._value = EstimatedQuantity(unit.Quantity(value, None),
                                        unit.Quantity(uncertainty, None),
                                        self.id)

        logging.info('Extracted dielectrics: ' + self.id)

        return self._get_output_dictionary()
Exemple #27
0
import netCDF4 as netcdf
from simtk import unit
import glob

#lentraj = 10000
#perckeep = 0.8
ncfiles = glob.glob('AlkEthOH_*.nc')

for i in ncfiles:
    #indkeep = int(lentraj*perckeep)
    data = netcdf.Dataset(i)
    xyz = data.variables['coordinates']
    #xyzn = unit.Quantity(xyz[-indkeep:], unit.angstroms)
    xyzn = unit.Quantity(xyz[:], unit.angstroms)
    if len(xyzn) < 10000:
        print i, len(xyzn)
Exemple #28
0
    def execute(self, directory, available_resources):

        logging.info('Reweighting dielectric: {}'.format(self.id))

        if len(self._reference_observables) == 0:
            return PropertyEstimatorException(
                directory=directory,
                message='There were no dipole moments to reweight.')

        if len(self._reference_volumes) == 0:
            return PropertyEstimatorException(
                directory=directory,
                message='There were no volumes to reweight.')

        if (not isinstance(self._reference_observables[0], unit.Quantity)
                or not isinstance(self._reference_volumes[0], unit.Quantity)):

            return PropertyEstimatorException(
                directory=directory,
                message='The reference observables should be '
                'a list of unit.Quantity wrapped ndarray\'s.')

        if len(self._reference_observables) != len(self._reference_volumes):
            return PropertyEstimatorException(
                directory=directory,
                message='The number of reference dipoles does '
                'not match the number of reference volumes.')

        for reference_dipoles, reference_volumes in zip(
                self._reference_observables, self._reference_volumes):

            if len(reference_dipoles) == len(reference_volumes):
                continue

            return PropertyEstimatorException(
                directory=directory,
                message='The number of reference dipoles does '
                'not match the number of reference volumes.')

        dipole_moments = self._prepare_observables_array(
            self._reference_observables)
        dipole_moments_sqr = np.array([[
            np.dot(dipole, dipole) for dipole in np.transpose(dipole_moments)
        ]])

        volumes = self._prepare_observables_array(self._reference_volumes)

        if self._bootstrap_uncertainties:

            reference_potentials = np.transpose(
                np.array(self._reference_reduced_potentials))
            target_potentials = np.transpose(
                np.array(self._target_reduced_potentials))

            frame_counts = np.array([
                len(observable) for observable in self._reference_observables
            ])

            # Construct an mbar object to get out the number of effective samples.
            import pymbar
            mbar = pymbar.MBAR(self._reference_reduced_potentials,
                               frame_counts,
                               verbose=False,
                               relative_tolerance=1e-12)

            effective_samples = mbar.computeEffectiveSampleNumber().max()

            value, uncertainty = bootstrap(
                self._bootstrap_function,
                self._bootstrap_iterations,
                self._bootstrap_sample_size,
                frame_counts,
                reference_reduced_potentials=reference_potentials,
                target_reduced_potentials=target_potentials,
                dipoles=np.transpose(dipole_moments),
                dipoles_sqr=np.transpose(dipole_moments_sqr),
                volumes=np.transpose(volumes))

            if effective_samples < self._required_effective_samples:
                uncertainty = sys.float_info.max

            self._value = EstimatedQuantity(unit.Quantity(value, None),
                                            unit.Quantity(uncertainty, None),
                                            self.id)

        else:

            return PropertyEstimatorException(
                directory=directory,
                message='Dielectric uncertainties may only'
                'be bootstrapped.')

        logging.info('Dielectric reweighted: {}'.format(self.id))

        return self._get_output_dictionary()
Exemple #29
0
    def run_task(self, settings, systems, n_sweeps, n_steps_per_sweep=100, verbose_freq=10,
                 temperature_pot_qm=unit.Quantity(300, unit.kelvin), temperature_pot_mm=unit.Quantity(300, unit.kelvin),
                 temperature_kin_mm=unit.Quantity(300, unit.kelvin), label="0", optimize_mm=False, ase_calculator_low_level=None, calculate_forces=False, checkpoint_freq=100,
                 parametrization=False, sampling_mm=True, sampling_freq_mm=10, sampling_freq_qm=50,
                 parametrization_freq=100, restart=False, seed=np.random.randint(2**32-1), system_mm_solute=None):
        """
        Method that runs a HMC sampler.

        Parameters
        ----------
        settings : dict
            Dictionary containing global ParaMol settings.
        systems : list of :obj:`ParaMol.System.system.ParaMolSystem`
            List containing instances of ParaMol systems.
        n_sweeps : int
            Number of MC sweeps to perform.
        n_steps_per_sweep : int
            Number of MD steps per MC sweep.
        verbose_freq : int
            Verbose frequency.
        temperature_pot_qm : unit.Quantity
            Temperature used for the QM potential part.
        temperature_pot_mm : unit.Quantity
            Temperature used for the MM potential part.
        temperature_kin_mm : unit.Quantity
            Temperature used for the MM kinetic part.
        label : int
            HMC sampler label. It has to be an integer number.
        optimize_mm : bool
            Flag that controls whether an MM optimization is performed before the HMC run. Only used if ase_calculator_low_level is None.
        ase_calculator_low_level : ase.calculator.*.*
            ASE calculator used for the low level chain.
        calculate_forces : bool
            Flag that signals whether or not to calculate forces when a structure is accepted. Only relevant for parametrization purposes (does not affect the HMC in itself).
        checkpoint_freq : int
            Frequency at which checkpoint restart files are written.
        parametrization : bool
            Flag that controls whether parametrization is to be performed.
        sampling_mm : bool
            Flag that controls whether or not to sample rejected MM structures.
        sampling_freq_mm : int
            Frequency at which MM structures rejected in the QM ensemble are sampled.
        sampling_freq_qm : int
            Frequency at which structures accepted in sample
        parametrization_freq : int
            Number of structures that has to be collected before performing parametrization.
        restart : bool
            Flag that controls whether or not to perform a restart.
        seed : int
            Numpy random seed.

        Returns
        -------
        systems : listg
            List with the updated instances of ParaMol System.
        """
        assert len(systems) == 1, "HMC task currently only supports one system at once."

        # Create QM Engines and initiate OpenMM
        for system in systems:
            system.convert_system_ref_arrays_to_list()

            # Reset any previously created OpenMM Context\
            system.engine.context = None

            # HMC has to use a sympletic integrator such VelocityVerletIntegrator
            system.engine.integrator = VelocityVerletIntegratorAdapted(system.engine._integrator_params["stepSize"])
            system.engine.init_openmm(create_system_params=system.engine._create_system_params)

            system.engine.get_masses()

            if system_mm_solute.interface is None:
                system_mm_solute.interface = ParaMolInterface()

            if system.interface is None:
                system.interface = ParaMolInterface()

            system_mm_solute.create_qm_engines(settings.qm_engine["qm_engine"], settings.qm_engine[settings.qm_engine["qm_engine"].lower()])

            if ase_calculator_low_level is not None:
                from ase import units as ase_units

                mm_ase_engine = ASEWrapper(system_name=system.name,
                                           interface=system.interface,
                                           calculator=ase_calculator_low_level,
                                           n_atoms=system.n_atoms,
                                           atom_list=system.engine.atom_list,
                                           n_calculations=1,
                                           cell=None,
                                           work_dir_prefix="HMC_LL_ASEWorkDir_",
                                           opt_log_file="ase_md.log")

                temperature_kin_mm_units = unit.Quantity(temperature_kin_mm / ase_units.kB, unit.kelvin)

            else:
                mm_ase_engine = None
                temperature_kin_mm_units = temperature_kin_mm

        system = systems[0]

        self._label = label

        parameter_space, objective_function, optimizer = (None, None, None)
        # TODO: Once this is included in the main ParaMol version, add this line to the default dictionary
        settings.restart["restart_hmc_file_{}".format(self._label)] = "restart_hmc_{}.pickle".format(self._label)

        if restart:
            logging.info("Starting HMC sampler parametrization from a previous restart.")

            # Read HMCSampler pickle
            self.__dict__ = self.read_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label))

            # Read data into system
            system.read_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label)))

            # Convert np.arrays to list
            system.convert_system_ref_arrays_to_list()

            # Calculate last accepted mm energy if not available
            self._last_accepted_mm_energy = None
            if self._last_accepted_mm_energy is None:
                if mm_ase_engine is None:
                    potential_final_mm = unit.Quantity(system.engine.get_potential_energy(system.ref_coordinates[-1]), unit.kilojoules_per_mole)
                else:
                    coord_to_run = np.asarray(system.ref_coordinates[-1]) * 10
                    potential_final_mm = mm_ase_engine.run_calculation(coords=coord_to_run, label=int(self._label))

                self._last_accepted_mm_energy = potential_final_mm
        else:
            self._n = 1
            self._n_total_qm = 0
            self._n_accepted_qm = 0

            # MM chain
            self._n_total_mm = 0
            self._n_accepted_mm = 0

            if parametrization:
                # Data used for the parametrization
                self._param_coordinates = []
                self._param_energies = []
                self._param_forces = []
                self._param_n_structures = 0
                self._param_sampling_freq_qm_id = 0
                self._param_sampling_freq_mm_id = 0
                self._parametrize = True
            else:
                self._parametrize = False

            if optimize_mm:
                logging.info("Performing MM optimization...")
                system.engine.minimize_system(tolerance=0.1, max_iter=0)

        # Set numpy seed
        np.random.seed(seed)
        mask_atoms = 14

        while self._n <= n_sweeps:
            if len(system.ref_coordinates) > 0 and self._n != 1:
                # Do not need to compute the QM energy if there are structures in the top ensemble or if we are not in the first ever iteration.
                system.engine.set_positions(system.ref_coordinates[-1])
                potential_initial_qm = system.ref_energies[-1]
                potential_initial_qm = unit.Quantity(potential_initial_qm, unit.kilojoules_per_mole)
                potential_initial_mm = self._last_accepted_mm_energy

                mm_solute_initial = unit.Quantity(system_mm_solute.engine.get_potential_energy(system.ref_coordinates[-1][:mask_atoms]), unit.kilojoules_per_mole)

                if mm_ase_engine is not None:
                    coord_to_run = np.asarray(system.ref_coordinates[-1]) * 10
            else:
                # Compute MM initial kinetic and potential energy
                coord_to_run = system.engine.get_positions().in_units_of(unit.angstrom)._value

                if mm_ase_engine is None:
                    potential_initial_mm = unit.Quantity(system.engine.get_potential_energy(system.engine.get_positions()), unit.kilojoules_per_mole)
                else:
                    potential_initial_mm = mm_ase_engine.run_calculation(coords=system.engine.get_positions().in_units_of(unit.angstrom)._value, label=int(self._label))

                potential_initial_qm, _ = system_mm_solute.qm_engine.qm_engine.run_calculation(coords=coord_to_run[:mask_atoms], label=int(self._label))
                potential_initial_qm = unit.Quantity(potential_initial_qm, unit.kilojoules_per_mole)

                mm_solute_initial = unit.Quantity(system_mm_solute.engine.get_potential_energy(coord_to_run[:mask_atoms]*0.1), unit.kilojoules_per_mole)

            if mm_ase_engine is None:
                # ---------------------------------------------------------------- #
                #                             OpenMM HMC                           #
                # ---------------------------------------------------------------- #
                # Use OpenMM as the low level
                # Sample new velocities and calculate kinetic energy
                new_velocities = system.engine.generate_maxwell_boltzmann_velocities(temperature_kin_mm)
                system.engine.set_velocities(new_velocities)
                system.engine.context.applyVelocityConstraints(1e-8)

                # Calculate kinetic energy
                kinetic_initial = unit.Quantity(system.engine.get_kinetic_energy(system.engine.get_velocities()), unit.kilojoules_per_mole)

                # Run classical MD simulation
                system.engine.integrator.step(n_steps_per_sweep)

                # Compute MM final kinetic and potential energy
                kinetic_final = unit.Quantity(system.engine.get_kinetic_energy(system.engine.get_velocities()), unit.kilojoules_per_mole)
                potential_final_mm = unit.Quantity(system.engine.get_potential_energy(system.engine.get_positions()), unit.kilojoules_per_mole)
                coords = system.engine.get_positions()
            else:
                # ---------------------------------------------------------------- #
                #                             ASE HMC                              #
                # ---------------------------------------------------------------- #
                # Use ASE as the low level
                # Run short MD using ASE
                coords, potential_initial_mm, kinetic_initial, forces_initial, potential_final_mm, kinetic_final, forces_final = mm_ase_engine.run_md(coords=coord_to_run,
                                                                                                                                                      label=int(self._label),
                                                                                                                                                      steps=n_steps_per_sweep,
                                                                                                                                                      dt=1.0 * ase_units.fs,
                                                                                                                                                      initial_temperature=temperature_kin_mm, )

                # Compute MM final kinetic and potential energy
                kinetic_initial = unit.Quantity(kinetic_initial, unit.kilojoules_per_mole)
                potential_initial_mm = unit.Quantity(potential_initial_mm, unit.kilojoules_per_mole)
                kinetic_final = unit.Quantity(kinetic_final, unit.kilojoules_per_mole)
                potential_final_mm = unit.Quantity(potential_final_mm, unit.kilojoules_per_mole)
                coords = unit.Quantity(coords, unit.nanometers)

            # ---------------------------------------------------------------- #
            #                       Low Level->High Level                      #
            # ---------------------------------------------------------------- #
            if self._hmc_acceptance_criterion_mm(potential_final_mm, potential_initial_mm, kinetic_final, kinetic_initial, temperature_pot_mm, temperature_kin_mm_units):
                potential_final_qm, forces_final_qm = system_mm_solute.qm_engine.qm_engine.run_calculation(coords=coords[:mask_atoms].in_units_of(unit.angstrom)._value, label=int(self._label))
                potential_final_qm = unit.Quantity(potential_final_qm, unit.kilojoules_per_mole)

                mm_solute_final = unit.Quantity(system_mm_solute.engine.get_potential_energy(coords[:mask_atoms]), unit.kilojoules_per_mole)


                potential_final_qm_mm = potential_final_mm - mm_solute_final + potential_final_qm
                potential_initial_qm_mm = potential_initial_mm - mm_solute_initial + potential_initial_qm

                #potential_initial_mm = mm_solute_initial
                #potential_final_mm = mm_solute_final

                # Nested Markov chain acceptance criterion
                qm_accepted = self._hmc_acceptance_criterion_qm(potential_final_qm_mm, potential_initial_qm_mm, potential_final_mm, potential_initial_mm, temperature_pot_qm, temperature_pot_mm)

                if qm_accepted:
                    self._last_accepted_mm_energy = potential_final_mm

                    # Update position of context if using ASE engine
                    if mm_ase_engine is not None:
                        system.engine.set_positions(coords)

                    # Append energies, forces and conformations
                    system.ref_energies.append(potential_final_qm._value)
                    system.ref_coordinates.append(system.engine.get_positions()._value)

                    if calculate_forces:
                        system.ref_forces.append(forces_final_qm)

                    system.n_structures += 1
                elif len(system.ref_coordinates) > 0:
                    # Append last accepted structure
                    system.ref_energies.append(system.ref_energies[-1])
                    system.ref_coordinates.append(system.ref_coordinates[-1])

                    if calculate_forces:
                        system.ref_forces.append(system.ref_forces[-1])

                    system.n_structures += 1
                else:
                    # No structures have been accepted yet.
                    pass

                if self._parametrize:
                    # TODO: include code related to partial momentum refreshment
                    if sampling_freq_qm == self._param_sampling_freq_qm_id and len(system.ref_coordinates) > 0:
                        self._param_coordinates.append(system.ref_coordinates[-1])
                        self._param_energies.append(system.ref_energies[-1])
                        if calculate_forces:
                            self._param_forces.append(system.ref_forces[-1])
                        self._param_n_structures += 1

                        # Reset sampling counter
                        self._param_sampling_freq_qm_id = 1
                    else:
                        self._param_sampling_freq_qm_id += 1

            # Write restart files
            if self._n % checkpoint_freq == 0:
                self.write_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label), self.__dict__)
                system.write_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label)))

                system_mm_solute.ref_coordinates = np.asarray(system.ref_coordinates)[:, :mask_atoms, :]
                system_mm_solute.write_coordinates_xyz("{}_hmc_{}.xyz".format(system_mm_solute.name, self._label))

            if self._parametrize and (self._param_n_structures % parametrization_freq == 0 and self._param_n_structures > 0):
                system, parameter_space, objective_function, optimizer = self._run_parametrization(settings, system, parameter_space, objective_function, optimizer, calculate_forces)

            if self._n % verbose_freq == 0:
                self._print_output(system.name)

            self._n += 1

        return systems
Exemple #30
0
    def _parse_parameters(self, path_to_parameters):
        """
        It parses the parameters from ffld_server's output file.

        Parameters
        ----------
        path_to_parameters : str
            The path to the ffld_server's output file

        Returns
        -------
        params : an OPLSParameters object
            The set of lists of parameters grouped by parameter type.
            Thus, the dictionary has the following keys: atom_names,
            atom_types, charges, sigmas, and epsilons.
        """
        params = defaultdict(list)

        with open(path_to_parameters) as f:
            section = 'out'
            name_to_index = dict()  # To pair atom names and indexes
            for line in f:
                if line.startswith('OPLSAA FORCE FIELD TYPE ASSIGNED'):
                    section = 'atoms'

                    # Skip the next 3 lines
                    f.readline()
                    f.readline()
                    f.readline()

                elif line.startswith(' Stretch'):
                    section = 'bonds'

                elif line.startswith(' Bending'):
                    section = 'angles'

                elif line.startswith(' proper Torsion'):
                    section = 'propers'

                elif line.startswith(' improper Torsion'):
                    section = 'impropers'

                elif line == '\n':
                    continue

                elif section == 'atoms':
                    if line.startswith('-'):
                        continue

                    fields = line.split()
                    assert len(fields) > 7, 'Unexpected number of fields ' \
                        + 'found at line {}'.format(line)

                    name_to_index[line[0:4]] = len(params['atom_names'])

                    params['atom_names'].append(line[0:4])
                    params['atom_types'].append(fields[3])
                    params['charges'].append(
                        unit.Quantity(float(fields[4]),
                                      unit.elementary_charge))
                    params['sigmas'].append(
                        unit.Quantity(float(fields[5]), unit.angstrom))
                    params['epsilons'].append(
                        unit.Quantity(float(fields[6]),
                                      unit.kilocalorie / unit.mole))

                elif section == 'bonds':
                    fields = line.split()
                    assert len(fields) > 4, 'Unexpected number of fields ' \
                        + 'found at line {}'.format(line)

                    params['bonds'].append({
                        'atom1_idx':
                        name_to_index[line[0:4]],
                        'atom2_idx':
                        name_to_index[line[8:12]],
                        'spring_constant':
                        unit.Quantity(
                            float(fields[2]),
                            unit.kilocalorie / (unit.angstrom**2 * unit.mole)),
                        'eq_dist':
                        unit.Quantity(float(fields[3]), unit.angstrom)
                    })

                elif section == 'angles':
                    fields = line.split()
                    assert len(fields) > 5, 'Unexpected number of fields ' \
                        + 'found at line {}'.format(line)

                    params['angles'].append({
                        'atom1_idx':
                        name_to_index[line[0:4]],
                        'atom2_idx':
                        name_to_index[line[8:12]],
                        'atom3_idx':
                        name_to_index[line[16:20]],
                        'spring_constant':
                        unit.Quantity(
                            float(fields[3]),
                            unit.kilocalorie / (unit.radian**2 * unit.mole)),
                        'eq_angle':
                        unit.Quantity(float(fields[4]), unit.degrees)
                    })

        return self.OPLSParameters(params)