コード例 #1
0
def minimise_energy_all_confs(mol, models = None, epsilon = 4, allow_undefined_stereo = True, **kwargs ):
    from simtk import unit
    from simtk.openmm import LangevinIntegrator
    from simtk.openmm.app import Simulation, HBonds, NoCutoff
    from rdkit import Chem
    from rdkit.Geometry import Point3D
    import mlddec
    import copy
    import tqdm
    mol = Chem.AddHs(mol, addCoords = True)

    if models is None:
        models  = mlddec.load_models(epsilon)
    charges = mlddec.get_charges(mol, models)

    from openforcefield.utils.toolkits import RDKitToolkitWrapper, ToolkitRegistry
    from openforcefield.topology import Molecule, Topology
    from openforcefield.typing.engines.smirnoff import ForceField
    # from openforcefield.typing.engines.smirnoff.forcefield import PME

    import parmed
    import numpy as np

    forcefield = ForceField(get_data_filename("modified_smirnoff99Frosst.offxml")) #FIXME better way of identifying file location

    tmp = copy.deepcopy(mol)
    tmp.RemoveAllConformers() #XXX workround for speed beacuse seemingly openforcefield records all conformer informations, which takes a long time. but I think this is a ill-practice

    molecule = Molecule.from_rdkit(tmp, allow_undefined_stereo = allow_undefined_stereo)
    molecule.partial_charges = unit.Quantity(np.array(charges), unit.elementary_charge)
    topology = Topology.from_molecules(molecule)
    openmm_system = forcefield.create_openmm_system(topology, charge_from_molecules= [molecule])

    structure = parmed.openmm.topsystem.load_topology(topology.to_openmm(), openmm_system)


    system = structure.createSystem(nonbondedMethod=NoCutoff, nonbondedCutoff=1*unit.nanometer, constraints=HBonds)

    integrator = LangevinIntegrator(273*unit.kelvin, 1/unit.picosecond, 0.002*unit.picoseconds)
    simulation = Simulation(structure.topology, system, integrator)

    out_mol = copy.deepcopy(mol)
    for i in tqdm.tqdm(range(out_mol.GetNumConformers())):
        conf = mol.GetConformer(i)
        structure.coordinates =  unit.Quantity(np.array([np.array(conf.GetAtomPosition(i)) for i in range(mol.GetNumAtoms())]), unit.angstroms)

        simulation.context.setPositions(structure.positions)

        simulation.minimizeEnergy()
        # simulation.step(1)

        coords = simulation.context.getState(getPositions = True).getPositions(asNumpy = True).value_in_unit(unit.angstrom)
        conf = out_mol.GetConformer(i)
        for j in range(out_mol.GetNumAtoms()):
            conf.SetAtomPosition(j, Point3D(*coords[j]))

    return out_mol
コード例 #2
0
def minimize_energy(pdb: PDBFile, simulation: Simulation, args: ListOfArgs):
    if args.MINIMIZE:
        print('Energy minimizing...')
        simulation.minimizeEnergy(tolerance=0.01 * simtk.unit.kilojoules_per_mole)
        if not args.MINIMIZED_FILE:
            base, _ = os.path.splitext(args.INITIAL_STRUCTURE_PATH)
            minimized_file_name = f'{base}_min.pdb'
        else:
            minimized_file_name = args.MINIMIZED_FILE  # TODO: Nasty fix
        print(f'  Saving minimized structure in {minimized_file_name}')
        state = simulation.context.getState(getPositions=True)
        PDBFile.writeFile(pdb.topology, state.getPositions(), open(minimized_file_name, 'w'))
コード例 #3
0
    def simulation_from_graph(self, g):
        """ Create simulation from moleucle """
        # assign partial charge
        g.mol.assign_partial_charges("gasteiger")  # faster

        # parameterize topology
        topology = g.mol.to_topology()

        # create openmm system
        system = self.forcefield.create_openmm_system(
            topology,
            # TODO:
            # figure out whether `sqm` should be so slow
            charge_from_molecules=[g.mol],
        )

        # use langevin integrator
        integrator = openmm.LangevinIntegrator(self.temperature,
                                               self.collision_rate,
                                               self.step_size)

        # initialize simulation
        simulation = Simulation(topology=topology,
                                system=system,
                                integrator=integrator)

        import openforcefield

        # get conformer
        g.mol.generate_conformers(
            toolkit_registry=openforcefield.utils.RDKitToolkitWrapper(), )

        # put conformer in simulation
        simulation.context.setPositions(g.mol.conformers[0])

        # minimize energy
        simulation.minimizeEnergy()

        # set velocities
        simulation.context.setVelocitiesToTemperature(self.temperature)

        return simulation
コード例 #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
コード例 #5
0
# Loop extrusion force
le_force = mm.HarmonicBondForce()
le_force.addBond(
    48, 50, 1 * u.angstrom,
    LE_FORCE_MATRIX[2][0] * u.kilocalories_per_mole / u.angstroms**2)
for i in range(2, 35):
    p1, p2 = 49 - i, 49 + i
    le_force.addBond(
        p1, p2, 1 * u.angstrom,
        LE_FORCE_MATRIX[1][0] * u.kilocalories_per_mole / u.angstroms**2)
system.addForce(le_force)

simulation = Simulation(pdb.topology, system, integrator)
simulation.context.setPositions(pdb.positions)
simulation.minimizeEnergy()
simulation.reporters.append(DCDReporter('trj.dcd', 1))
simulation.reporters.append(
    StateDataReporter(stdout,
                      1000,
                      step=True,
                      potentialEnergy=True,
                      temperature=True))
simulation.reporters.append(
    StateDataReporter(STATE_FNAME, 10, step=True, potentialEnergy=True))

simulation.step(STEPS_PER_CYCLE)

for i in range(2, 35):
    p1, p2 = 49 - i, 49 + i
    for j in range(0, STEPS_PER_CYCLE):
コード例 #6
0
def test_improper_recover():
    from simtk import openmm, unit
    from simtk.openmm.app import Simulation
    from simtk.unit import Quantity

    TEMPERATURE = 500 * unit.kelvin
    STEP_SIZE = 1 * unit.femtosecond
    COLLISION_RATE = 1 / unit.picosecond

    system, topology, g = _create_impropers_only_system()

    # use langevin integrator, although it's not super useful here
    integrator = openmm.LangevinIntegrator(
        TEMPERATURE, COLLISION_RATE, STEP_SIZE
    )

    # initialize simulation
    simulation = Simulation(
        topology=topology, system=system, integrator=integrator
    )

    import openff.toolkit

    # get conformer
    g.mol.generate_conformers(
        toolkit_registry=openff.toolkit.utils.RDKitToolkitWrapper(),
    )

    # put conformer in simulation
    simulation.context.setPositions(g.mol.conformers[0])

    # minimize energy
    simulation.minimizeEnergy()

    # set velocities
    simulation.context.setVelocitiesToTemperature(TEMPERATURE)

    samples = []
    us = []

    # loop through number of samples
    for _ in range(10):

        # run MD for `self.n_steps_per_sample` steps
        simulation.step(10)

        # append samples to `samples`
        samples.append(
            simulation.context.getState(getPositions=True)
            .getPositions(asNumpy=True)
            .value_in_unit(esp.units.DISTANCE_UNIT)
        )

        us.append(
            simulation.context.getState(getEnergy=True)
            .getPotentialEnergy()
            .value_in_unit(esp.units.ENERGY_UNIT)
        )

    # put samples into an array
    samples = np.array(samples)
    us = np.array(us)

    # put samples into tensor
    samples = torch.tensor(samples, dtype=torch.float32)
    us = torch.tensor(us, dtype=torch.float32)[None, :, None]

    g.heterograph.nodes["n1"].data["xyz"] = samples.permute(1, 0, 2)

    # require gradient for force matching
    g.heterograph.nodes["n1"].data["xyz"].requires_grad = True

    g.heterograph.nodes["g"].data["u_ref"] = us

    # parametrize
    layer = esp.nn.dgl_legacy.gn()
    net = torch.nn.Sequential(
        esp.nn.Sequential(layer, [32, "tanh", 32, "tanh", 32, "tanh"]),
        esp.nn.readout.janossy.JanossyPoolingImproper(
            in_features=32,
            config=[32, "tanh"],
            out_features={
                "k": 6,
            },
        ),
        esp.mm.geometry.GeometryInGraph(),
        esp.mm.energy.EnergyInGraph(terms=["n4_improper"]),
    )

    optimizer = torch.optim.Adam(net.parameters(), 1e-3)

    for _ in range(1500):
        optimizer.zero_grad()

        net(g.heterograph)
        u_ref = g.nodes["g"].data["u"]
        u = g.nodes["g"].data["u_ref"]
        loss = torch.nn.MSELoss()(u_ref, u)
        loss.backward()
        print(loss)
        optimizer.step()

    assert loss.detach().numpy().item() < 0.1
コード例 #7
0
    print("Done specifying integrator.")
    platform = mm.Platform.getPlatformByName('CUDA')
    print("Done specifying platform.")
    platform.setPropertyDefaultValue('Precision', 'mixed')
    print("Done setting the precision to mixed.")
    minimize = Simulation(molecule.topology, system, integrator, platform)
    print("Done specifying simulation.")
    minimize.context.setPositions(molecule.positions)
    print("Done recording a context for positions.")
    minimize.context.setVelocitiesToTemperature(310.15 * unit.kelvin)
    print("Done assigning velocities.")

    # start minimization
    tolerance = 0.1 * unit.kilojoules_per_mole / unit.angstroms
    print("Done setting tolerance.")
    minimize.minimizeEnergy(tolerance=tolerance, maxIterations=1000)
    print("Done setting energy minimization.")
    minimize.reporters.append(
        StateDataReporter('relax-hydrogens.log',
                          1000,
                          step=True,
                          temperature=True,
                          potentialEnergy=True,
                          totalEnergy=True,
                          speed=True))
    minimize.step(min_steps)
    print("Done 100000 steps of minimization.")
    print("Potential energy after minimization:")
    #print(minimize.context.getState(getEnergy=True).getPotentialEnergy())
    positions = minimize.context.getState(getPositions=True).getPositions()
    print("Done updating positions.")
コード例 #8
0
    def simulate(self, header, content):
        """Main method that is "executed" by the receipt of the
        msg_type == 'simulate' message from the server.

        We run some OpenMM dynamics, and then send back the results.
        """
        self.log.info('Setting up simulation...')
        state, topology = self.deserialize_input(content)

        # set the GPU platform
        platform = Platform.getPlatformByName(str(self.platform))
        if self.platform == 'CUDA':
            properties = {'CudaPrecision': 'mixed',
                          'CudaDeviceIndex': str(self.device_index)
                         }
        elif self.platform == 'OpenCL':
            properties = {'OpenCLPrecision': 'mixed',
                          'OpenCLDeviceIndex': str(self.device_index)
                         }
        else:
            properties = None


        simulation = Simulation(topology, self.system, self.integrator,
                                platform, properties)
        # do the setup
        self.set_state(state, simulation)
        self.sanity_check(simulation)
        if self.minimize:
            self.log.info('minimizing...')
            simulation.minimizeEnergy()

        if self.random_initial_velocities:
            try:
                temp = simulation.integrator.getTemperature()
                simulation.context.setVelocitiesToTemperature(temp)
            except AttributeError:
                print "I don't know what temperature to use!!"
                # TODO: look through the system's forces to find an andersen
                # thermostate?
                raise
            pass

        assert content.output.protocol == 'localfs', "I'm currently only equiped for localfs output"
        self.log.info('adding reporters...')
        self.add_reporters(simulation, content.output.path)

        # run dynamics!
        self.log.info('Starting dynamics')
        simulation.step(self.number_of_steps)

        for reporter in simulation.reporters:
            # explicitly delete the reporters so that any open file handles
            # are closed.
            del reporter

        # tell the master that I'm done
        self.send_recv(msg_type='simulation_done', content={
            'status': 'success',
            'output': {
                'protocol': 'localfs',
                'path': content.output.path
            }
        })
コード例 #9
0
def loop_extrusion(STEPS, LE_FORCE_SCALE, MATRIX_LENGTH, STEPS_PER_CYCLE, STEPS_PER_IT):
    STATE_FNAME = '2sided-state.csv'
    #STEPS = 10000
    #LE_FORCE_SCALE = 3
    #MATRIX_LENGTH = 200
    #STEPS_PER_CYCLE = 10
    #STEPS_PER_IT = 1

#Macierz z parametrami sił wiązań
#Dodano funkcje generacji macierzy o wartościach sinusoidalnych. Funkcja ta przyjmuje dwa argumenty. Pierwszy oznacza liczbę kroków które ma posiadać macierz a drugi
#stanowi regulacje maksymalnej siły (tzn jeśli wstawimy 3 to maksymalna siła bedzie tyle wynosić)
    LE_FORCE_MATRIX = gen_sin_array(MATRIX_LENGTH,LE_FORCE_SCALE)
    LE_FORCE_MATRIX[1][0] = 0
    LE_FORCE_MATRIX[2][-1] = 0
#print(LE_FORCE_MATRIX)

    pdb = PDBFile('initial_structure.pdb')
    forcefield = ForceField('polymer_ff.xml')
    system = forcefield.createSystem(pdb.topology, nonbondedCutoff=1 * u.nanometer)
    integrator = mm.LangevinIntegrator(100 * u.kelvin, 0.2, 1 * u.femtoseconds)

# Distance constraint
    for i in range(system.getNumParticles() - 1):
        system.addConstraint(i, i + 1, 0.1 * u.nanometer)

# Pinning ends with rubber
    pin_force = mm.CustomExternalForce("k*((x-x0)^2+(y-y0)^2+(z-z0)^2)")
    pin_force.addGlobalParameter("k", 50 * u.kilocalories_per_mole / u.angstroms ** 2)
    pin_force.addPerParticleParameter("x0")
    pin_force.addPerParticleParameter("y0")
    pin_force.addPerParticleParameter("z0")
    pin_force.addParticle(0, [15 * u.angstrom, 0 * u.angstrom, 0 * u.angstrom])
    pin_force.addParticle(system.getNumParticles() - 1, [-15 * u.angstrom, 0 * u.angstrom, 0 * u.angstrom])
    system.addForce(pin_force)


# Loop extrusion force
    le_force = mm.HarmonicBondForce()
    le_force.addBond(48, 50, 1 * u.angstrom, LE_FORCE_SCALE * u.kilocalories_per_mole / u.angstroms ** 2)
    for i in range(2, 35):
        p1, p2 = 49 - i, 49 + i
        le_force.addBond(p1, p2, 1 * u.angstrom, 0.000001 * u.kilocalories_per_mole / u.angstroms ** 2)
    system.addForce(le_force)
    

    simulation = Simulation(pdb.topology, system, integrator)
    simulation.context.setPositions(pdb.positions)
    simulation.minimizeEnergy()
    simulation.reporters.append(DCDReporter('wyniki/2sided-trj.dcd', 1))
    simulation.reporters.append(StateDataReporter(stdout, 1000, step=True, potentialEnergy=True, temperature=True))
    simulation.reporters.append(StateDataReporter(STATE_FNAME, 10, step=True, potentialEnergy=True))

    simulation.step(1)

    for i in range(2, 35):
        p1, p2 = 49 - i, 49 + i
        for j in range(MATRIX_LENGTH):
            le_force_one = LE_FORCE_MATRIX[1][j] * u.kilocalories_per_mole / u.angstroms ** 2 #ROSNĄCA
            le_force_two = LE_FORCE_MATRIX[2][j] * u.kilocalories_per_mole / u.angstroms ** 2 #MALEJĄCA
            le_force.setBondParameters(i - 2, p1 + 1, p2 - 1, 1 * u.angstrom,
                                    le_force_two)
            le_force.setBondParameters(i - 1, p1, p2, 1 * u.angstrom, le_force_one)
            le_force.updateParametersInContext(simulation.context)
        #print(le_force_one)
        #print(le_force_two)
        #simulation.minimizeEnergy()
            simulation.step(STEPS_PER_IT)
#    for i in range(STEPS_PER_CYCLE):
#        simulation.step(1)
        simulation.step(200)
        plot_data(STATE_FNAME, '2sided-energy.png')

    print('#1: repr stick; color white; color red :1,100; repr sphere :1,100; vdwdefine 0.5')
    print('#1: color green :49,51; repr sphere :49,51; color #ffffa2e8a2e8 :50;')
    for i in range(1, 35):
        p1, p2 = 50 - i - 1, 50 + i + 1
        print(
            f'#{i*STEPS_PER_CYCLE+1}: color green :{p1},{p2}; repr sphere :{p1},{p2}; repr stick :{p1+1},{p2-1}; color #ffffa2e8a2e8 :{p1+1}-{p2-1};')

    print("Done")
コード例 #10
0
class MoleculeUtil(object):
    """
    A class for managing a molecule defined by a PDB file
    """
    np.random.seed(20)

    def __init__(self, pdb_path, offset_size=2):
        # OpenMM init
        self.pdb_path = pdb_path
        self.pdb = PDBFile(self.pdb_path)
        self.forcefield = ForceField('amber14-all.xml', 'amber14/tip3pfb.xml')
        self.modeller = Modeller(self.pdb.topology, self.pdb.positions)

        # Remove any water that might be present in the PDB file
        self.modeller.deleteWater()

        # Add any hydrogens not present
        self.modeller.addHydrogens(self.forcefield)
        self.system = self.forcefield.createSystem(self.modeller.topology,
                                                   nonbondedMethod=PME,
                                                   nonbondedCutoff=1 *
                                                   u.nanometer,
                                                   constraints=HBonds)
        self.integrator = LangevinIntegrator(300 * u.kelvin, 1 / u.picosecond,
                                             0.002 * u.picoseconds)
        self.simulation = Simulation(self.modeller.topology, self.system,
                                     self.integrator)
        self.pdb_positions = self.modeller.getPositions()

        # Initialize bond dictionary and positions for chemcoord
        self.cc_bonds = {}
        self.offset_size = offset_size
        self._init_pdb_bonds()
        self.set_cc_positions(self.pdb_positions)

        # Perform initial minimization, which updates self.pdb_positions
        min_energy, min_positions = self.run_simulation()

        # Reset the positions after the minimization
        self.set_cc_positions(self.pdb_positions)
        self.torsion_indices = self._get_torsion_indices()
        self.starting_positions = min_positions
        self.starting_torsions = np.array([
            self.zmat.loc[self.torsion_indices[:, 0], 'dihedral'],
            self.zmat.loc[self.torsion_indices[:, 1], 'dihedral']
        ]).T
        self.seed_offsets()

    def _add_backbone_restraint(self):
        # https://github.com/ParmEd/ParmEd/wiki/OpenMM-Tricks-and-Recipes#positional-restraints
        positions = self.modeller.getPositions()
        force = CustomExternalForce('k*((x-x0)^2+(y-y0)^2+(z-z0)^2)')
        force.addGlobalParameter(
            'k', 5.0 * u.kilocalories_per_mole / u.angstroms**2)
        force.addPerParticleParameter('x0')
        force.addPerParticleParameter('y0')
        force.addPerParticleParameter('z0')

        for index, atom in enumerate(self.modeller.topology.atoms()):
            if atom.name in ('CA', 'C', 'N'):
                coord = positions[index]
                force.addParticle(index, coord.value_in_unit(u.nanometers))

        self.restraint_force_id = self.system.addForce(force)

    def _remove_backbone_restraint(self):
        self.system.removeForce(self.restraint_force_id)

    def _fix_backbone(self):
        for index, atom in enumerate(self.modeller.topology.atoms()):
            if atom.name in ('CA', 'C', 'N'):
                self.system.setParticleMass(index, 0)

    def seed_offsets(self):
        self.offsets = np.random.choice([0, 0, -1, 1],
                                        self.starting_torsions.shape)

    def get_torsions(self):
        return np.array([
            self.zmat.loc[self.torsion_indices[:, 0], 'dihedral'],
            self.zmat.loc[self.torsion_indices[:, 1], 'dihedral']
        ]).T

    def set_torsions(self, new_torsions):
        self.zmat.safe_loc[self.torsion_indices[:, 0],
                           'dihedral'] = new_torsions[:, 0]
        self.zmat.safe_loc[self.torsion_indices[:, 1],
                           'dihedral'] = new_torsions[:, 1]

    def get_offset_torsions(self, scale_factor):
        """
        Calculates and returns new torsion angles based on randomly generated
        offsets.

        Args:
            scale_factor: the relative scale of the offset relative to
                          self.offset_size
        Returns:
            The new torsion angles
        """
        total_offset = self.offset_size * scale_factor
        new_torsions = np.zeros(shape=self.starting_torsions.shape)
        new_torsions[:, 0] = self.starting_torsions[:, 0] + \
            (self.offsets[:, 0] * total_offset)
        new_torsions[:, 1] = self.starting_torsions[:, 1] + \
            (self.offsets[:, 1] * total_offset)
        return new_torsions

    def run_simulation(self):
        """
        Run a simulation to calculate the current configuration's energy level.
        Note that the atoms will likely move somewhat during the calculation,
        since energy minimization is used.

        Returns:
            A tuple of the form (potential_energy, updated_positions)
        """
        # Delete solvent that's based on previous positions
        cartesian = self.zmat.get_cartesian().sort_index()
        self.simulation.context.setPositions([
            Vec3(x, y, z)
            for x, y, z in zip(cartesian['x'], cartesian['y'], cartesian['z'])
        ])

        # self._add_backbone_restraint()
        # self._fix_backbone()

        self.modeller.addSolvent(self.forcefield, padding=1.0 * u.nanometer)
        self.simulation.minimizeEnergy(maxIterations=200)
        state = self.simulation.context.getState(getEnergy=True,
                                                 getPositions=True)
        p_energy = state.getPotentialEnergy()
        positions = state.getPositions(asNumpy=True)

        # Clean up - remove solvent and backbone restraint (for next iteration)
        self.modeller.deleteWater()

        # self._remove_backbone_restraint()

        return p_energy, positions

    def _init_pdb_bonds(self):
        """Construct a dictionary describing the PDB's bonds for chemcoord use"""
        for index in range(self.modeller.topology.getNumAtoms()):
            self.cc_bonds[index] = set()

        for bond in self.modeller.topology.bonds():
            self.cc_bonds[bond[0].index].add(bond[1].index)
            self.cc_bonds[bond[1].index].add(bond[0].index)

    def set_cc_positions(self, positions):
        """
        Calculates the zmat from an OpenMM modeller

        Args:
            positions (list): A list 
        """
        cc_df = self._get_cartesian_df(positions)
        self.cartesian = cc.Cartesian(cc_df)
        self.cartesian.set_bonds(self.cc_bonds)
        self.cartesian._give_val_sorted_bond_dict(use_lookup=True)
        self.zmat = self.cartesian.get_zmat(use_lookup=True)

    def _get_cartesian_df(self, positions):
        cc_positions = np.zeros((3, self.modeller.topology.getNumAtoms()))
        atom_names = []
        for index, atom in enumerate(self.modeller.topology.atoms()):
            pos = positions[index] / u.nanometer
            atom_names.append(atom.name)
            cc_positions[:, index] = pos

        cc_df = pd.DataFrame({
            'atom': atom_names,
            'x': cc_positions[0, :],
            'y': cc_positions[1, :],
            'z': cc_positions[2, :]
        })
        return cc_df

    def _get_torsion_indices(self):
        """
        Calculates indices into the zmatrix which correspond to phi
        and psi angles.

        Args:
            zmat: the zmatrix specifying the molecule
        Returns:
            a numpy.array, with first column as phi_indices, second column
            as psi_indices
        """
        phi_indices = []
        psi_indices = []

        for i in range(len(self.zmat.index)):
            b_index = self.zmat.loc[i, 'b']
            a_index = self.zmat.loc[i, 'a']
            d_index = self.zmat.loc[i, 'd']

            # If this molecule references a magic string (origin, e_x, e_y, e_z, etc)
            if isinstance(b_index, str) or isinstance(
                    a_index, str) or isinstance(d_index, str):
                continue

            # Psi angles
            if (self.zmat.loc[i, 'atom'] == 'N') & \
                    (self.zmat.loc[b_index, 'atom'] == 'CA') & \
                    (self.zmat.loc[a_index, 'atom'] == 'C') & \
                    (self.zmat.loc[d_index, 'atom'] == 'N'):
                psi_indices.append(i)

            elif (self.zmat.loc[i, 'atom'] == 'N') & \
                    (self.zmat.loc[b_index, 'atom'] == 'C') & \
                    (self.zmat.loc[a_index, 'atom'] == 'CA') & \
                    (self.zmat.loc[d_index, 'atom'] == 'N'):
                psi_indices.append(i)

            elif (self.zmat.loc[i, 'atom'] == 'C') & \
                    (self.zmat.loc[b_index, 'atom'] == 'N') & \
                    (self.zmat.loc[a_index, 'atom'] == 'CA') & \
                    (self.zmat.loc[d_index, 'atom'] == 'C'):
                phi_indices.append(i)

            elif (self.zmat.loc[i, 'atom'] == 'C') & \
                    (self.zmat.loc[b_index, 'atom'] == 'CA') & \
                    (self.zmat.loc[a_index, 'atom'] == 'N') & \
                    (self.zmat.loc[d_index, 'atom'] == 'C'):
                phi_indices.append(i)

        return np.array([phi_indices, psi_indices]).T
コード例 #11
0
print("Done specifying integrator.")
platform = mm.Platform.getPlatformByName('CUDA')
print("Done specifying platform.")
platform.setPropertyDefaultValue('Precision', 'single')
print("Done setting the precision to single.")
simulation = Simulation(molecule.topology, system, integrator, platform)
print("Done specifying simulation.")
simulation.context.setPositions(molecule.positions)
print("Done recording a context for positions.")
simulation.context.setVelocitiesToTemperature(310.15 * unit.kelvin)
print("Done assigning velocities.")

# start minimization
tolerance = 0.1 * unit.kilojoules_per_mole / unit.angstroms
print("Done setting tolerance.")
simulation.minimizeEnergy(tolerance=tolerance, maxIterations=1000)
print("Done setting energy minimization.")
simulation.reporters.append(
    StateDataReporter('relax-hydrogens.log',
                      1000,
                      step=True,
                      temperature=True,
                      potentialEnergy=True,
                      totalEnergy=True,
                      speed=True))
simulation.step(10000)
print("Done 10000 steps of simulation.")
positions = simulation.context.getState(getPositions=True).getPositions()
print("Done updating positions.")
simulation.saveCheckpoint('state.chk')
print("Done saving checkpoints.")
コード例 #12
0
def main():
    parser = argparse.ArgumentParser(description='Runs local minimization on \
                                     the protein-ligand complex using OpenMM')
    parser.add_argument('-l',
                        '--ligand',
                        action='store',
                        nargs=1,
                        dest='ligand',
                        help='The ligand .pdb file to generate \
                        parameters for')
    parser.add_argument('-x',
                        '--xml_ligand',
                        action='store',
                        nargs=1,
                        dest='xml',
                        help='The xml file containing ligand \
                        parameters - use full path if not in directory where \
                        this script is being executed from')
    parser.add_argument('-p',
                        '--protein',
                        action='store',
                        nargs=1,
                        dest='protein',
                        help='The protein complex .pdb file')
    parser.add_argument('-i',
                        '--input_directory',
                        action='store',
                        nargs=1,
                        dest='input',
                        default=['./'],
                        help='Directory where \
                        input pdb files are stored')
    parser.add_argument('-o',
                        '--output_directory',
                        action='store',
                        nargs=1,
                        dest='output',
                        default=['./'],
                        help='Directory where \
                        output log should be written')
    args = vars(parser.parse_args())

    #Load in ligand file and parameters
    ligand_pdbfile = PDBFile(args['input'][0] + '/' + args['ligand'][0])
    ligand_system = parmed.load_file(args['xml'][0])
    ligand_structure = load_topology(ligand_pdbfile.topology,
                                     ligand_system,
                                     xyz=ligand_pdbfile.positions)

    #Load in protein complex file and  force field
    receptor_pdbfile = PDBFile(args['input'][0] + '/' + args['protein'][0])
    receptor_pdbfile_name = args['protein'][0].split('.pdb')[-2]
    omm_forcefield = ForceField('amber14-all.xml')
    receptor_system = omm_forcefield.createSystem(receptor_pdbfile.topology)
    receptor_structure = load_topology(receptor_pdbfile.topology,
                                       receptor_system,
                                       xyz=receptor_pdbfile.positions)

    #Generate ligand-protein complex
    complex_structure = receptor_structure + ligand_structure
    complex_system = (complex_structure.createSystem(nonbondedMethod=NoCutoff,
                                                     nonbondedCutoff=9.0 *
                                                     angstrom,
                                                     constraints=HBonds,
                                                     removeCMMotion=False))

    #Set up simulation parameters
    integrator = LangevinIntegrator(300 * kelvin, 1 / picosecond,
                                    0.002 * picoseconds)
    simulation = Simulation(complex_structure.topology, complex_system,
                            integrator)
    simulation.context.setPositions(complex_structure.positions)

    #Run local minimization
    simulation.minimizeEnergy()
    positions = simulation.context.getState(getPositions=True).getPositions()

    #Save minimized complex structure
    PDBFile.writeFile(
        simulation.topology, positions,
        open(args['output'][0] + '/' + receptor_pdbfile_name + '_min.pdb',
             'w'))
コード例 #13
0
    def simulate(self, header, content):
        """Main method that is "executed" by the receipt of the
        msg_type == 'simulate' message from the server.

        We run some OpenMM dynamics, and then send back the results.
        """
        self.log.info('Setting up simulation...')
        state, topology = self.deserialize_input(content)
        starting_state_path = content.starting_state.path

        # set the GPU platform
        platform = Platform.getPlatformByName(str(self.platform))
        if self.platform == 'CUDA':
            properties = {
                'CudaPrecision': 'mixed',
                'CudaDeviceIndex': str(self.device_index)
            }
        elif self.platform == 'OpenCL':
            properties = {
                'OpenCLPrecision': 'mixed',
                'OpenCLDeviceIndex': str(self.device_index)
            }
        else:
            properties = None

        simulation = Simulation(topology, self.system, self.integrator,
                                platform, properties)
        # do the setup
        self.set_state(state, simulation)
        self.sanity_check(simulation)
        if self.minimize:
            self.log.info('minimizing...')
            simulation.minimizeEnergy()

        if self.random_initial_velocities:
            try:
                temp = simulation.integrator.getTemperature()
                simulation.context.setVelocitiesToTemperature(temp)
            except AttributeError:
                print "I don't know what temperature to use!!"
                # TODO: look through the system's forces to find an andersen
                # thermostate?
                raise
            pass

        assert content.output.protocol == 'localfs', "I'm currently only equiped for localfs output"
        self.log.info('adding reporters...')
        self.add_reporters(simulation, content.output.path)

        # run dynamics!
        self.log.info('Starting dynamics')
        simulation.step(self.number_of_steps)

        for reporter in simulation.reporters:
            # explicitly delete the reporters so that any open file handles
            # are closed.
            del reporter

        # tell the master that I'm done
        self.send_recv(msg_type='simulation_done',
                       content={
                           'status': 'success',
                           'starting_state': {
                               'protocol': 'localfs',
                               'path': starting_state_path
                           },
                           'output': {
                               'protocol': 'localfs',
                               'path': content.output.path
                           }
                       })
コード例 #14
0
def main(argdict):
    """ Main function for entry point checking.

    Expects a dictionary of command line arguments.
    """

    # are we continuing from logfile or starting from fresh PDB?
    if argdict["log"] is None:

        # keep track of restart number:
        argdict["restart_number"] = int(0)

        # write arguments to a file to keep a record:
        with open(argdict["outname"] + "_parameters.log", 'w') as f:
            print(json.dumps(argdict, sort_keys=True, indent=4), file=f)
    else:

        # load configuration from logfile:
        logfile = argdict["log"]
        with open(argdict["log"], 'r') as f:
            argdict = json.load(f)

        # make sure log file has appropriate entry:
        argdict["log"] = logfile

        # increment restart number:
        argdict["restart_number"] += 1

        # write unnumbered parameters file (for easy restarts):
        with open(argdict["outname"] + "_parameters.log", 'w') as f:
            print(json.dumps(argdict, sort_keys=True, indent=4), file=f)

    # write numbered parameters file (for record keeping):
    with open(
            argdict["outname"] + "_" + str(argdict["restart_number"]) +
            "_parameters.log", 'w') as f:
        print(json.dumps(argdict, sort_keys=True, indent=4), file=f)

    # load system initial configuration:
    pdb = pdb_file_nonstandard_bonds(argdict["pdb"])
    print("--> input topology: ", end="")
    print(pdb.topology)

    # get XML file from absolute path:
    xmlfile = os.path.abspath(argdict["xml"])

    # set box size in topology to values from XML file:
    box_vectors = periodic_box_vectors_from_xml(xmlfile)
    pdb.topology.setPeriodicBoxVectors(box_vectors)

    # physical parameters of simulation:
    sim_temperature = argdict["temperature"] * kelvin
    sim_andersen_coupling = 1 / picosecond
    sim_pressure = (
        (argdict["pressure"], argdict["pressure"], argdict["pressure"]) * bar)
    sim_scale_x = True
    sim_scale_y = True
    sim_scale_z = True

    # simulation control parameters:
    sim_timestep = argdict["timestep"] * femtoseconds
    sim_num_steps = argdict["num_steps"]
    sim_traj_rep_steps = argdict["report_freq"]
    sim_state_rep_steps = argdict["report_freq"]
    sim_checkpoint_steps = argdict["report_freq"]

    # restraints parameters:
    sim_restr_fc = argdict["restr_fc"] * kilojoule_per_mole / nanometer**2

    # create force field object:
    ff = ForceField(*argdict["ff"])

    # build a simulation system from topology and force field:
    # (note that AMOEBA is intended to be run without constraints)
    # (note that mutualInducedtargetEpsilon defaults to 0.01 unlike what is
    # specified in the documentation which claims 0.00001)
    sys = ff.createSystem(
        pdb.topology,
        nonbondedMethod=PME,
        nonbondedCutoff=argdict["nonbonded_cutoff"] * nanometer,
        vdwCutoff=argdict["vdw_cutoff"] * nanometer,
        ewaldErrorTolerance=argdict["ewald_error_tolerance"],
        polarisation=argdict["polarisation"],
        mutualInducedTargetEpsilon=argdict["mutual_induced_target_epsilon"],
        constraints=None,
        rigidWater=False,
        removeCMMotion=True  # removes centre of mass motion
    )

    # overwrite the polarisation method set at system creation; this is
    # necessary as openMM always sets polarisation method to "mutual" of the
    # target epsilon is specified at system creation; this way, target epsilon
    # is ignored for all but the mutual method
    multipole_force = sys.getForce(9)
    print("--> using polarisation method " + str(argdict["polarisation"]))
    if argdict["polarisation"] == "mutual":
        multipole_force.setPolarizationType(multipole_force.Mutual)
    elif argdict["polarisation"] == "extrapolated":
        multipole_force.setPolarizationType(multipole_force.Extrapolated)
    elif argdict["polarisation"] == "direct":
        multipole_force.setPolarizationType(multipole_force.Direct)
    else:
        raise Exception("Polarisation method " + str(argdict["polarisation"]) +
                        " not supported!")

    # will use Andersen thermostat here:
    # (Inhibits particle dynamics somewhat, but little or no ergodicity
    # issues (from Gromacs documenation). However, only alternative is full
    # Langevin dynamics, which is even worse wrt dynamics. Bussi/v-rescale is
    # not available at the moment, it seems (it is available in tinker, but
    # without GPU acceleration))
    sys.addForce(AndersenThermostat(sim_temperature, sim_andersen_coupling))

    # use anisotropic barostat:
    # (note that this corresponds to semiisotropic pressure coupling in Gromacs
    # if the pressure is identical for the x- and y/axes)
    # (note that by default this attempts an update every 25 steps)
    sys.addForce(
        MonteCarloAnisotropicBarostat(sim_pressure, sim_temperature,
                                      sim_scale_x, sim_scale_y, sim_scale_z))

    # prepare harmonic restraining potential:
    # (note that periodic distance is absolutely necessary here to prevent
    # system from blowing up, as otherwise periodic image position may be used
    # resulting in arbitrarily large forces)
    force = CustomExternalForce("k*periodicdistance(x, y, z, x0, y0, z0)^2")
    force.addGlobalParameter("k", sim_restr_fc)
    force.addPerParticleParameter("x0")
    force.addPerParticleParameter("y0")
    force.addPerParticleParameter("z0")

    # apply harmonic restraints to C-alphas:
    if argdict["restr"] == "capr":
        print("--> applying harmonic positional restraints to CA atoms")
        for atm in pdb.topology.atoms():
            if atm.name == "CA":
                force.addParticle(atm.index, pdb.positions[atm.index])
    elif argdict["restr"] == "hapr":
        sys.exit("Restraints mode " + str(argdict["restr"]) +
                 "is not implemented.")
    elif argdict["restr"] == "none":
        print("--> applying no harmonic positional restraints to any atom")
    else:
        sys.exit("Restraints mode " + str(argdict["restr"]) +
                 "is not implemented.")

    # add restraining force to system:
    sys.addForce(force)

    # make special group for nonbonded forces:
    for f in sys.getForces():
        if (isinstance(f, AmoebaMultipoleForce)
                or isinstance(f, AmoebaVdwForce)
                or isinstance(f, AmoebaGeneralizedKirkwoodForce)
                or isinstance(f, AmoebaWcaDispersionForce)):
            f.setForceGroup(1)

    # read umbrella parameters from file:

    with open(argdict["umbrella_target"], "r") as f:
        umbrella_target = json.load(f)

    # obtain index from atom to be restrained:
    umbrella_index = umbrella_target["target_particle"]["index"]
    umbrella_fc = (umbrella_target["umbrella_params"]["fc"] *
                   kilojoule_per_mole / nanometer**2)
    umbrella_cv = umbrella_target["umbrella_params"]["cv"] * nanometer

    # inform user:
    print("--> applying umbrella potential to " +
          str(list(pdb.topology.atoms())[umbrella_index]) + " at position " +
          str(pdb.positions[umbrella_index]))

    # additional restraining force applied to ion under umbrella restraints:
    umbrella_force = CustomExternalForce(
        "k*periodicdistance(0.0, 0.0, z, 0.0, 0.0, z0)^2")
    umbrella_force.addGlobalParameter("k", umbrella_fc)
    # z0 is set to value in JSON file rather than initial particle coordinate to
    # allow for a few steps of energy minimisation to avoid clashes between the
    # restrained umbrella target and surrounding atoms:
    umbrella_force.addGlobalParameter("z0", umbrella_cv)

    # select integrator:
    if argdict["integrator"] == "mts":

        # use multiple timestep RESPA integrator:
        print("--> using RESPA/MTS integrator")
        integrator = MTSIntegrator(sim_timestep,
                                   [(0, argdict["inner_ts_frac"]), (1, 1)])

    elif argdict["integrator"] == "verlet":

        # use Leapfrog Verlet integrator here:
        print("--> using Verlet integrator")
        integrator = VerletIntegrator(sim_timestep)

    else:

        # no other integrators supported:
        raise Exception("Integrator " + str(argdict["integrator"]) +
                        " is not supported.")

    # select a platform:
    platform = Platform.getPlatformByName(argdict["platform"])
    properties = {
        "CudaPrecision": argdict["precision"],
        "CudaDeviceIndex": "0"
    }

    # prepare a simulation from topology, system, and integrator and set initial
    # positions as in PDB file:
    sim = Simulation(pdb.topology, sys, integrator, platform, properties)

    # is this initial simulation or restart from checkpoint?
    if argdict["log"] is None:

        # load positions and velocities from XML file:
        print("--> loading simulation state from XML file...")
        sim.loadState(xmlfile)

        # find all particles bonded to ion (i.e. Drude particles):
        idx_bonded = atom_idx_from_bonds(sim.topology, umbrella_index)
        idx_shift = idx_bonded + [umbrella_index]

        # shift target particle into position:
        pos = (sim.context.getState(getPositions=True).getPositions(
            asNumpy=True))
        pos[idx_shift, :] = (umbrella_target["target_particle"]["init_pos"] *
                             nanometer)
        print("--> target particle now placed at " + str(pos[idx_shift, :]))

        # set new particle positions in context:
        sim.context.setPositions(pos)
        e_pot = sim.context.getState(getEnergy=True).getPotentialEnergy()
        print("--> potential energy after target placement is: " + str(e_pot))

        # minimise energy to remove clashes:
        # (too many steps might ruin everythin!)
        print("--> running energy minimisation...")
        sim.minimizeEnergy(maxIterations=argdict["minimisation_steps"])
        e_pot = sim.context.getState(getEnergy=True).getPotentialEnergy()
        print(
            "--> " + str(argdict["minimisation_steps"]) +
            " steps of energy minimisation reduced the potential energy to " +
            str(e_pot))

        # reduce time step for equilibration period:
        print("--> running equilibration at reduced time step...")
        sim.integrator.setStepSize(0.1 * sim.integrator.getStepSize())
        sim.context.setTime(0.0 * picosecond)

        # will write report about equilibration phase:
        sim.reporters.append(
            StateDataReporter(stdout,
                              int(argdict["equilibration_steps"] / 10),
                              step=True,
                              time=True,
                              speed=True,
                              progress=True,
                              remainingTime=True,
                              totalSteps=argdict["equilibration_steps"],
                              separator="\t"))

        # run equilibration steps:
        sim.step(argdict["equilibration_steps"])

        # reset step size to proper value:
        sim.integrator.setStepSize(10.0 * sim.integrator.getStepSize())
        sim.context.setTime(0.0 * picosecond)
        sim.reporters.clear()

    else:

        # load checkpoint file:
        checkpoint_file = (str(argdict["outname"]) + "_" +
                           str(argdict["restart_number"] - 1) + ".chk")
        print("--> loading checkpoint file " + checkpoint_file)
        sim.loadCheckpoint(checkpoint_file)

    # write collective variable value to file:
    sample_outname = (argdict["outname"] + "_" +
                      str(argdict["restart_number"]) + str(".dat"))
    sim.reporters.append(
        CollectiveVariableReporter(sample_outname, argdict["umbrella_freq"],
                                   umbrella_index))

    # write simulation trajectory to DCD file:
    dcd_outname = (argdict["outname"] + "_" + str(argdict["restart_number"]) +
                   str(".dcd"))
    sim.reporters.append(DCDReporter(dcd_outname, sim_traj_rep_steps))

    # write state data to tab-separated CSV file:
    state_outname = (argdict["outname"] + "_" +
                     str(argdict["restart_number"]) + str(".csv"))
    sim.reporters.append(
        StateDataReporter(state_outname,
                          sim_state_rep_steps,
                          step=True,
                          time=True,
                          progress=False,
                          remainingTime=True,
                          potentialEnergy=True,
                          kineticEnergy=True,
                          totalEnergy=True,
                          temperature=True,
                          volume=True,
                          density=False,
                          speed=True,
                          totalSteps=sim_num_steps,
                          separator="\t"))

    # write limited state information to standard out:
    sim.reporters.append(
        StateDataReporter(stdout,
                          sim_state_rep_steps,
                          step=True,
                          time=True,
                          speed=True,
                          progress=True,
                          remainingTime=True,
                          totalSteps=sim_num_steps,
                          separator="\t"))

    # write checkpoint files regularly:
    checkpoint_outname = (argdict["outname"] + "_" +
                          str(argdict["restart_number"]) + ".chk")
    sim.reporters.append(
        CheckpointReporter(checkpoint_outname, sim_checkpoint_steps))

    # run simulation:
    sim.step(argdict["num_steps"])

    # save final simulation state:
    sim.saveState(argdict["outname"] + "_" + str(argdict["restart_number"]) +
                  ".xml")
    positions = sim.context.getState(getPositions=True).getPositions()
    PDBFile.writeFile(
        sim.topology, positions,
        open(
            argdict["outname"] + "_" + str(argdict["restart_number"]) + ".pdb",
            "w"))
コード例 #15
0
def distributeAtoms(boxsize=[34, 34, 34],
                     nparticles=1000,
                     reduced_density=0.05,
                     mass=39.9 * unit.amu,  # argon
                     sigma=3.4 * unit.angstrom,  # argon,
                     epsilon=0.238 * unit.kilocalories_per_mole,  # argon,
                     cutoff=None,
                     switch_width=3.4 * unit.angstrom,  # argon
                     dispersion_correction=True,
                     lattice=False,
                     charge=None,
                     **kwargs):
        # Determine Lennard-Jones cutoff.
        if cutoff is None:
            cutoff = 3.0 * sigma

        charge = 0.0 * unit.elementary_charge
        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(dispersion_correction)

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

        for particle_index in range(nparticles):
            system.addParticle(mass)
            nb.addParticle(charge, sigma, epsilon)

        positions = subrandom_particle_positions(nparticles, system.getDefaultPeriodicBoxVectors(), 2)
        # Add the nonbonded force.
        system.addForce(nb)

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

        # Create topology.
        topology = app.Topology()
        element = app.Element.getBySymbol('Ar')
        chain = topology.addChain()
        for particle in range(system.getNumParticles()):
            residue = topology.addResidue('Ar', chain)
            topology.addAtom('Ar', 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
        #from dcdreporter import DCDReporter
        nsteps = 10000
        freq = 1
        #integrator = LangevinIntegrator(300 * kelvin, 1 / picosecond, 0.002 * picoseconds)
        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)
        finalpos = 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 finalpos, mol, system, simulation