Ejemplo n.º 1
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            if self.use_pbc:
                cartesian_force = CustomExternalForce(
                    "0.5 * cart_force_const * r_eff^2;"
                    "r_eff = max(0.0, r - cart_delta);"
                    "r = periodicdistance(0, y, z, 0, cart_y, cart_z);"
                )
            else:
                cartesian_force = CustomExternalForce(
                    "0.5 * cart_force_const * r_eff^2;"
                    "r_eff = max(0.0, r - cart_delta);"
                    "r = sqrt(r2);"
                    "r2 = dy*dy + dz*dz;"
                    "dy = y - cart_y;"
                    "dz = z - cart_z;"
                )
            cartesian_force.addPerParticleParameter("cart_y")
            cartesian_force.addPerParticleParameter("cart_z")
            cartesian_force.addPerParticleParameter("cart_delta")
            cartesian_force.addPerParticleParameter("cart_force_const")

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                cartesian_force.addParticle(
                    r.atom_index - 1, [r.y, r.z, r.delta, weight]
                )
            system.addForce(cartesian_force)
            self.force = cartesian_force
        return system
Ejemplo n.º 2
0
def _add_yzcartesian_restraints(system, restraint_list, alpha, timestep, force_dict):
    # split restraints into confinement and others
    cartesian_restraints = [r for r in restraint_list if isinstance(r, YZCartesianRestraint)]
    noncartesian_restraints = [r for r in restraint_list if not isinstance(r, YZCartesianRestraint)]

    if cartesian_restraints:
        # create the confinement force
        cartesian_force = CustomExternalForce(
            '0.5 * cart_force_const * r_eff^2; r_eff = max(0.0, r - cart_delta);'
            'r = sqrt(dy*dy + dz*dz);'
            'dy = y - cart_y;'
            'dz = z - cart_z;')
        cartesian_force.addPerParticleParameter('cart_y')
        cartesian_force.addPerParticleParameter('cart_z')
        cartesian_force.addPerParticleParameter('cart_delta')
        cartesian_force.addPerParticleParameter('cart_force_const')

        # add the atoms
        for r in cartesian_restraints:
            weight = r.force_const * r.scaler(alpha) * r.ramp(timestep)
            cartesian_force.addParticle(r.atom_index - 1, [r.y, r.z, r.delta, weight])
        system.addForce(cartesian_force)
        force_dict['yzcartesian'] = cartesian_force
    else:
        force_dict['yzcartesian'] = None

    return noncartesian_restraints
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            cartesian_force = CustomExternalForce(
                '0.5 * cart_force_const * r_eff2;'
                'r_eff2 = max(0.0, r2 - cart_delta^2);'
                'r2 = dy*dy + dz*dz;'
                'dy = y - cart_y;'
                'dz = z - cart_z;')
            cartesian_force.addPerParticleParameter('cart_y')
            cartesian_force.addPerParticleParameter('cart_z')
            cartesian_force.addPerParticleParameter('cart_delta')
            cartesian_force.addPerParticleParameter('cart_force_const')

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                cartesian_force.addParticle(r.atom_index - 1,
                                            [r.y, r.z, r.delta, weight])
            system.addForce(cartesian_force)
            self.force = cartesian_force
        return system
Ejemplo n.º 5
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            if self.use_pbc:
                confinement_force = CustomExternalForce(
                    "step(r - radius) * force_const * (radius - r)^2;"
                    "r = periodicdistance(x, y, z, 0, 0 ,0)")
            else:
                confinement_force = CustomExternalForce(
                    "step(r - radius) * force_const * (radius - r)^2;"
                    "r=sqrt(x*x + y*y + z*z)")
            confinement_force.addPerParticleParameter("radius")
            confinement_force.addPerParticleParameter("force_const")

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                confinement_force.addParticle(r.atom_index - 1,
                                              [r.radius, weight])
            system.addForce(confinement_force)
            self.force = confinement_force

        return system
Ejemplo n.º 6
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            confinement_force = CustomExternalForce(
                'step(r - radius) * force_const * (radius - r)^2;'
                'r=sqrt(x*x + y*y + z*z)')
            confinement_force.addPerParticleParameter('radius')
            confinement_force.addPerParticleParameter('force_const')

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                confinement_force.addParticle(r.atom_index - 1,
                                              [r.radius, weight])
            system.addForce(confinement_force)
            self.force = confinement_force

        return system
Ejemplo n.º 7
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            cartesian_force = CustomExternalForce(
                '0.5 * cart_force_const * r_eff2;'
                'r_eff2 = max(0.0, r2 - cart_delta^2);'
                'r2 = dy*dy + dz*dz;'
                'dy = y - cart_y;'
                'dz = z - cart_z;')
            cartesian_force.addPerParticleParameter('cart_y')
            cartesian_force.addPerParticleParameter('cart_z')
            cartesian_force.addPerParticleParameter('cart_delta')
            cartesian_force.addPerParticleParameter('cart_force_const')

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                cartesian_force.addParticle(r.atom_index - 1,
                                            [r.y, r.z, r.delta, weight])
            system.addForce(cartesian_force)
            self.force = cartesian_force
        return system
Ejemplo n.º 8
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            confinement_force = CustomExternalForce(
                'step(r - radius) * force_const * (radius - r)^2;'
                'r=sqrt(x*x + y*y + z*z)')
            confinement_force.addPerParticleParameter('radius')
            confinement_force.addPerParticleParameter('force_const')

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                confinement_force.addParticle(r.atom_index - 1,
                                              [r.radius, weight])
            system.addForce(confinement_force)
            self.force = confinement_force

        return system
Ejemplo n.º 9
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            if self.use_pbc:
                confinement_force = CustomExternalForce(
                    "step(r - radius) * force_const * (radius - r)^2;"
                    "r = periodicdistance(x, y, z, 0, 0 ,0)"
                )
            else:
                confinement_force = CustomExternalForce(
                    "step(r - radius) * force_const * (radius - r)^2;"
                    "r=sqrt(x*x + y*y + z*z)"
                )
            confinement_force.addPerParticleParameter("radius")
            confinement_force.addPerParticleParameter("force_const")

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                confinement_force.addParticle(r.atom_index - 1, [r.radius, weight])
            system.addForce(confinement_force)
            self.force = confinement_force

        return system
Ejemplo n.º 10
0
def _add_confinement_restraints(system, restraint_list, alpha, timestep, force_dict):
    # split restraints into confinement and others
    confinement_restraints = [r for r in restraint_list if isinstance(r, ConfinementRestraint)]
    nonconfinement_restraints = [r for r in restraint_list if not isinstance(r, ConfinementRestraint)]

    if confinement_restraints:
        # create the confinement force
        confinement_force = CustomExternalForce(
            'step(r - radius) * force_const * (radius - r)^2; r=sqrt(x*x + y*y + z*z)')
        confinement_force.addPerParticleParameter('radius')
        confinement_force.addPerParticleParameter('force_const')

        # add the atoms
        for r in confinement_restraints:
            weight = r.force_const * r.scaler(alpha) * r.ramp(timestep)
            confinement_force.addParticle(r.atom_index - 1, [r.radius, weight])
        system.addForce(confinement_force)
        force_dict['confine'] = confinement_force
    else:
        force_dict['confine'] = None

    return nonconfinement_restraints
Ejemplo n.º 11
0
    def _targeted_sim(self,
                      coords0,
                      coords1,
                      tmdk=15.,
                      d_steps=100,
                      n_max_steps=10000,
                      ddtol=1e-3,
                      n_conv=5):

        try:
            from simtk.openmm import CustomExternalForce
            from simtk.openmm.app import StateDataReporter
            from simtk.unit import nanometer, kelvin, angstrom, kilojoule_per_mole, MOLAR_GAS_CONSTANT_R
        except ImportError:
            raise ImportError(
                'Please install PDBFixer and OpenMM in order to use ClustENM.')

        tmdk *= kilojoule_per_mole / angstrom**2
        tmdk = tmdk.value_in_unit(kilojoule_per_mole / nanometer**2)

        # coords1_ca = coords1[self._idx_cg, :]
        pos1 = coords1 * angstrom
        # pos1_ca = pos1[self._idx_cg, :]

        force = CustomExternalForce('tmdk*((x-x0)^2+(y-y0)^2+(z-z0)^2)')
        force.addGlobalParameter('tmdk', 0.)
        force.addPerParticleParameter('x0')
        force.addPerParticleParameter('y0')
        force.addPerParticleParameter('z0')
        force.setForceGroup(1)
        # for i, atm_idx in enumerate(self._idx_cg):
        #     pars = pos1_ca[i, :].value_in_unit(nanometer)
        #     force.addParticle(int(atm_idx), pars)

        n_atoms = coords0.shape[0]
        atom_indices = np.arange(n_atoms)
        for i, atm_idx in enumerate(atom_indices):
            pars = pos1[i, :].value_in_unit(nanometer)
            force.addParticle(int(atm_idx), pars)

        simulation = self._prep_sim([force])

        # automatic conversion into nanometer will be carried out.
        simulation.context.setPositions(coords0 * angstrom)

        dist = dist0 = calcRMSD(coords0, coords1)
        m_conv = 0
        n_steps = 0
        try:
            simulation.minimizeEnergy(tolerance=self._tolerance *
                                      kilojoule_per_mole,
                                      maxIterations=self._maxIterations)

            # update parameters
            while n_steps < n_max_steps:
                simulation.context.setParameter('tmdk', tmdk)
                force.updateParametersInContext(simulation.context)

                simulation.step(d_steps)
                n_steps += d_steps

                # evaluate distance to destination
                pos = simulation.context.getState(
                    getPositions=True).getPositions(
                        asNumpy=True).value_in_unit(angstrom)
                d = calcRMSD(pos, coords1)
                dd = np.abs(dist - d)

                if dd < ddtol:
                    m_conv += 1

                if m_conv >= n_conv:
                    break

                dist = d

            LOGGER.debug('RMSD: %4.2f -> %4.2f' % (dist0, dist))

            simulation.context.setParameter('tmdk', 0.0)
            simulation.minimizeEnergy(tolerance=self._tolerance *
                                      kilojoule_per_mole,
                                      maxIterations=self._maxIterations)

            pos = simulation.context.getState(getPositions=True).getPositions(
                asNumpy=True).value_in_unit(angstrom)
            pot = simulation.context.getState(
                getEnergy=True).getPotentialEnergy().value_in_unit(
                    kilojoule_per_mole)

            return pot, pos

        except BaseException as be:
            LOGGER.warning(
                'OpenMM exception: ' + be.__str__() +
                ' so the corresponding conformer will be discarded!')

            return np.nan, np.full_like(coords0, np.nan)
Ejemplo n.º 12
0
    def add_interactions(self, system, topology):
        if self.active:
            # create the confinement force
            if self.use_pbc:
                cartesian_force = CustomExternalForce(
                    "0.5 * cart_force_const * r_eff^2;"
                    "r_eff = max(0.0, r - cart_delta);"
                    "r = periodicdistance(0, y, z, 0, cart_y, cart_z);"
                )
            else:
                cartesian_force = CustomExternalForce(
                    "0.5 * cart_force_const * r_eff^2;"
                    "r_eff = max(0.0, r - cart_delta);"
                    "r = sqrt(r2);"
                    "r2 = dy*dy + dz*dz;"
                    "dy = y - cart_y;"
                    "dz = z - cart_z;"
                )
            cartesian_force.addPerParticleParameter("cart_y")
            cartesian_force.addPerParticleParameter("cart_z")
            cartesian_force.addPerParticleParameter("cart_delta")
            cartesian_force.addPerParticleParameter("cart_force_const")

            # add the atoms
            for r in self.restraints:
                weight = r.force_const
                cartesian_force.addParticle(
                    r.atom_index - 1, [r.y, r.z, r.delta, weight]
                )
            system.addForce(cartesian_force)
            self.force = cartesian_force
        return system
def main(argdict):
    """ Main function for entry point checking.

    Expects a dictionary of command line arguments.
    """

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

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

    # 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

    # 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)
    system = 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 = [
        f for f in system.getForces() if isinstance(f, AmoebaMultipoleForce)
    ][0]
    print("--> using polarisation method " + str(argdict["polarisation"]))
    if argdict["polarisation"] == "mutual":
        multipole_force.setPolarizationType(multipole_force.Mutual)
    if argdict["polarisation"] == "extrapolated":
        multipole_force.setPolarizationType(multipole_force.Extrapolated)
    if argdict["polarisation"] == "direct":
        multipole_force.setPolarizationType(multipole_force.Direct)

    # 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))
    system.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)
    system.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:
    system.addForce(force)

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

    # 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)])
    if argdict["integrator"] == "verlet":
        # use Leapfrog Verlet integrator here:
        print("--> using Verlet integrator")
        integrator = VerletIntegrator(sim_timestep)

    # select a platform (should be CUDA, otherwise VERY slow):
    platform = Platform.getPlatformByName(argdict["platform"])
    properties = {
        "CudaPrecision": argdict["precision"],
        "CudaDeviceIndex": "0"
    }

    # create simulation system:
    sim = Simulation(pdb.topology, system, integrator, platform, properties)

    # unit conversion factors:
    ang2nm = 0.1

    # create MDA universe:
    u = mda.Universe(args.s, args.f)

    # selection for overall system will be needed to set OpenMM positions
    # accordingt to trajectory:
    allsystem = u.select_atoms("all")

    # get parameters to define cylinder around protein center of geometry:
    # (the cylinder spans the entire box in the z-direction)
    protein = u.select_atoms("protein")
    radius = str(args.r)
    z_margin = args.z_margin
    z_min = str(protein.bbox()[0, 2] - protein.center_of_geometry()[2] -
                z_margin)
    z_max = str(protein.bbox()[1, 2] - protein.center_of_geometry()[2] +
                z_margin)

    # select all solvent atoms, note that AMOEBA residue name is HOH:
    # (these must be updating, as water may move in and out of pore!)
    solvent = u.select_atoms("byres (resname HOH SOL) and cyzone " + radius +
                             " " + z_max + " " + z_min + " protein",
                             updating=True)
    solvent_ow = solvent.select_atoms("name O OW", updating=True)

    # lambda function for converting atomic dipoles to molecular dipoles:
    # (this only works on 1D arrays, hence use apply_along_axis if quantity is
    # vector-valued, e.g. positions and dipoles)
    def atomic2molecular_sum(arr):
        return np.bincount(allsystem.resindices, arr)

    def atomic2molecular_avg(arr):
        return np.bincount(allsystem.resindices, arr) / np.bincount(
            allsystem.resindices)

    # create lambda function for obtaining charges in vectorisable way:
    # (units are elementary_charge)
    get_atomic_charges = np.vectorize(
        lambda index: multipole_force.getMultipoleParameters(int(index))[
            0].value_in_unit(elementary_charge))

    # obtain atomic charges:
    # (charges are static, so need this only once; units are elementary charge)
    atomic_charges = get_atomic_charges(allsystem.ix)

    # obtain start and end time as will as time step:
    dt = float(args.dt)
    t_start = float(args.b)
    t_end = float(args.e)

    # prepare results dictionary:
    res = {
        "t": [],
        "x": [],
        "y": [],
        "z": [],
        "indu_rho": [],
        "indu_costheta": [],
        "indu_cosphi": [],
        "perm_rho": [],
        "perm_costheta": [],
        "perm_cosphi": [],
        "mono_rho": [],
        "mono_costheta": [],
        "mono_cosphi": [],
        "total_rho": [],
        "total_costheta": [],
        "total_cosphi": []
    }

    # loop over trajectory:
    for ts in u.trajectory:

        # skip all frames before starting frame:
        if ts.time < t_start:
            continue

        # only analyse relevant time frames:
        if round(ts.time, 4) % dt == 0:

            # inform user:
            print("analysing frame: " + str(ts.frame) + " at time: " +
                  str(ts.time))
            print("number of selected solvent molecules in this frame: " +
                  str(solvent.n_residues))

            # convert mda positions to OpenMM positions and set context:
            omm_positions = Quantity(
                [tuple(pos) for pos in list(allsystem.positions)],
                unit=angstrom)
            sim.context.setPositions(omm_positions)

            # calculate molecular positions (or molecular centre of geometry) by
            # averaging over all atomic positions within a residue:
            # (units are Angstrom in MDAnalysis!)
            molecular_positions = np.apply_along_axis(
                atomic2molecular_avg, 0, allsystem.positions) * ang2nm

            # calculate charge-weighted positions by multiplying the relative
            # atomic positions with the atomic charges (relative positions are
            # necessary to account for charged residues/molecules, where the
            # dipole moment is calculated relative to the center of geometry of
            # the residue):
            # (units are elementary charge * nanometer)
            atomic_charge_weighted_positions = (
                allsystem.positions -
                molecular_positions[allsystem.resindices])
            atomic_charge_weighted_positions *= (atomic_charges[np.newaxis].T *
                                                 ang2nm)

            # obtain induced and permanent atomic dipoles from OpenMM:
            # (units are elementary charge * nm)
            atomic_dipoles_indu = np.array(
                multipole_force.getInducedDipoles(sim.context))
            atomic_dipoles_perm = np.array(
                multipole_force.getLabFramePermanentDipoles(sim.context))

            # convert atomic to molecular quantities and calculate total dipole:
            molecular_dipoles_indu = np.apply_along_axis(
                atomic2molecular_sum, 0, atomic_dipoles_indu)
            molecular_dipoles_perm = np.apply_along_axis(
                atomic2molecular_sum, 0, atomic_dipoles_perm)
            molecular_dipoles_mono = np.apply_along_axis(
                atomic2molecular_sum, 0, atomic_charge_weighted_positions)
            molecular_dipoles_total = (molecular_dipoles_indu +
                                       molecular_dipoles_perm +
                                       molecular_dipoles_mono)

            # convert to spherical coordinates:
            molecular_dipoles_indu = cartesian2spherical(
                molecular_dipoles_indu)
            molecular_dipoles_perm = cartesian2spherical(
                molecular_dipoles_perm)
            molecular_dipoles_mono = cartesian2spherical(
                molecular_dipoles_mono)
            molecular_dipoles_total = cartesian2spherical(
                molecular_dipoles_total)

            # insert into results dictionary:
            res["t"].append(np.repeat(ts.time, solvent.n_residues))
            res["x"].append(molecular_positions[solvent_ow.resindices, 0])
            res["y"].append(molecular_positions[solvent_ow.resindices, 1])
            res["z"].append(molecular_positions[solvent_ow.resindices, 2])
            res["indu_rho"].append(
                molecular_dipoles_indu[solvent_ow.resindices, 0])
            res["indu_costheta"].append(
                molecular_dipoles_indu[solvent_ow.resindices, 1])
            res["indu_cosphi"].append(
                molecular_dipoles_indu[solvent_ow.resindices, 2])
            res["perm_rho"].append(
                molecular_dipoles_perm[solvent_ow.resindices, 0])
            res["perm_costheta"].append(
                molecular_dipoles_perm[solvent_ow.resindices, 1])
            res["perm_cosphi"].append(
                molecular_dipoles_perm[solvent_ow.resindices, 2])
            res["mono_rho"].append(
                molecular_dipoles_mono[solvent_ow.resindices, 0])
            res["mono_costheta"].append(
                molecular_dipoles_mono[solvent_ow.resindices, 1])
            res["mono_cosphi"].append(
                molecular_dipoles_mono[solvent_ow.resindices, 2])
            res["total_rho"].append(
                molecular_dipoles_total[solvent_ow.resindices, 0])
            res["total_costheta"].append(
                molecular_dipoles_total[solvent_ow.resindices, 1])
            res["total_cosphi"].append(
                molecular_dipoles_total[solvent_ow.resindices, 2])

        # stop iterating through trajectory after end time:
        if ts.time > t_end:
            break

    # convert lists of arrays to arrays:
    for k in res.keys():
        res[k] = np.concatenate(res[k])

    # convert units of dipole magnitude to Debye:
    eNm2debye = 48.03205
    res["indu_rho"] = eNm2debye * res["indu_rho"]
    res["perm_rho"] = eNm2debye * res["perm_rho"]
    res["mono_rho"] = eNm2debye * res["mono_rho"]
    res["total_rho"] = eNm2debye * res["total_rho"]

    # load spline curve data:
    with open(args.j, "r") as f:
        chap_data = json.load(f)

    # create spline curve from CHAP data:
    spline_curve = BSplineCurve(chap_data)

    # calculate s-coordinate from z-coordinate:
    res["s"] = spline_curve.z2s(res["z"])

    # convert results to data frame:
    df_res = pd.DataFrame(res)

    # loop over various numbers of bins:
    df = []
    for nbins in args.nbins:

        # create a temporary data frame:
        tmp = df_res

        # drop positional coordinates:
        tmp = tmp.drop(["x", "y", "z", "t"], axis=1)

        # bin by value of s-coordinate:
        tmp = tmp.groupby(pd.cut(tmp.s, nbins))

        # aggregate variables:
        tmp = tmp.agg([np.mean, np.std, sem, np.size, np.median, qlo,
                       qhi]).reset_index()

        # rename columns (combines variable name with aggregation method):
        tmp.columns = ["_".join(x) for x in tmp.columns.ravel()]

        # remove grouping key:
        tmp = tmp.drop("s_", axis=1)

        # add column wit number of bins:
        tmp["nbins"] = nbins

        # append to list of data frames:
        df.append(tmp)

    # combine list of data frames into single data frame:
    df = pd.concat(df)

    # write to JSON file:
    df.to_json(args.o, orient="records")

    # need to add newline for POSIX compliance:
    with open(args.o, "a") as f:
        f.write("\n")
Ejemplo n.º 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"))