Exemple #1
0
def test_inner_exceptions():
    model = ufedmm.AlanineDipeptideModel()
    nbforce = next(
        filter(lambda f: isinstance(f, openmm.NonbondedForce),
               model.system.getForces()))
    rs = 0.2
    rc = 0.4
    ufedmm.add_inner_nonbonded_force(model.system, rs, rc, 1)
    model.system.getForce(model.system.getNumForces() - 1).setForceGroup(3)
    platform = openmm.Platform.getPlatformByName('Reference')
    context = openmm.Context(model.system, openmm.CustomIntegrator(0),
                             platform)
    context.setPositions(model.positions)
    forces1 = _standardized(
        context.getState(getForces=True, groups={3}).getForces())
    forces2 = [0 * f for f in forces1]
    ONE_4PI_EPS0 = 138.93545764438198
    for index in range(nbforce.getNumExceptions()):
        i, j, chargeprod, sigma, epsilon = map(
            _standardized, nbforce.getExceptionParameters(index))
        rij = _standardized(model.positions[i] - model.positions[j])
        r = np.linalg.norm(rij)
        z = (r - rs) / (rc - rs)
        F = S(z) * (24 * epsilon * (2 * (sigma / r)**12 - (sigma / r)**6) / r +
                    ONE_4PI_EPS0 * chargeprod / r**2) * rij / r
        forces2[i] += F
        forces2[j] -= F
    for f1, f2 in zip(forces1, forces2):
        for i in range(3):
            assert f1[i] == pytest.approx(f2[i])
Exemple #2
0
 def update_temperatures(self, system_temperature, extended_space_temperatures):
     super().update_temperatures(system_temperature, extended_space_temperatures)
     kT_vectors = self.getPerDofVariableByName('kT')
     tauSq = _standardized(self._tau)**2
     Q = [tauSq*kT for kT in kT_vectors]
     invQ = [openmm.Vec3(*map(lambda x: 1/x if x > 0.0 else 0.0, q)) for q in Q]
     self.setPerDofVariableByName('invQ', invQ)
Exemple #3
0
    def free_energy_functions(self, sigma=None, factor=8):
        """
        Returns Python functions for evaluating the potential of mean force and their originating
        mean forces as a function of the collective variables.

        Keyword Args
        ------------
            sigma : float or unit.Quantity, default=None
                The standard deviation of kernels. If this is `None`, then values will be
                determined from the distances between nodes.
            factor : float, default=8
                If ``sigma`` is not explicitly provided, then it will be computed as
                ``sigma = factor*range/bins`` for each direction.

        Returns
        -------
            potential : function
                A Python function whose arguments are collective variable values and whose result
                is the potential of mean force at that values.
            mean_force : function
                A Python function whose arguments are collective variable values and whose result
                is the mean force at that values regarding a given direction. Such direction must
                be defined through a keyword argument `dir`, whose default value is `0` (meaning
                the direction of the first collective variable).

        """
        self.centers, self.mean_forces = self.centers_and_mean_forces(
            self._bins,
            self._min_count,
            self._adjust_centers,
        )
        variables = self._ufed.variables
        if sigma is None:
            sigmas = [
                factor * v._range / bin
                for v, bin in zip(variables, self._bins)
            ]
        else:
            try:
                sigmas = [_standardized(value) for value in sigma]
            except TypeError:
                sigmas = [_standardized(sigma)] * len(variables)

        return self.mean_force_free_energy(self.centers, self.mean_forces,
                                           sigmas)
Exemple #4
0
 def __init__(self, temperature, time_constant, step_size, **kwargs):
     if 'num_rattles' in kwargs.keys() and kwargs['num_rattles'] != 0:
         raise ValueError(f'{self.__class__.__name__} cannot handle constraints')
     self._tau = _standardized(time_constant)
     super().__init__(temperature, step_size, **kwargs)
     self.addPerDofVariable('Q1', 0)
     self.addPerDofVariable('Q2', 0)
     self.addPerDofVariable('v1', 0)
     self.addPerDofVariable('v2', 0)
Exemple #5
0
 def __init__(self, temperature, time_constant, step_size, nchain=2, **kwargs):
     if 'num_rattles' in kwargs.keys() and kwargs['num_rattles'] != 0:
         raise ValueError(f'{self.__class__.__name__} cannot handle constraints')
     self._tau = _standardized(time_constant)
     self._nchain = nchain
     super().__init__(temperature, step_size, **kwargs)
     self.addPerDofVariable('Q', 0)
     for i in range(nchain):
         self.addPerDofVariable(f'v{i+1}', 0)
Exemple #6
0
    def free_energy_functions(self, sigma=None, factor=8):
        """
        Returns Python functions for evaluating the potential of mean force and their originating
        mean forces as a function of the collective variables.

        Keyword Args
        ------------
            sigma : float or unit.Quantity, default=None
                The standard deviation of kernels. If this is `None`, then values will be
                determined from the distances between nodes.
            factor : float, default=8
                If ``sigma`` is not explicitly provided, then it will be computed as
                ``sigma = factor*range/bins`` for each direction.

        Returns
        -------
            potential : function
                A Python function whose arguments are collective variable values and whose result
                is the potential of mean force at that values.
            mean_force : function
                A Python function whose arguments are collective variable values and whose result
                is the mean force at that values regarding a given direction. Such direction must
                be defined through a keyword argument `dir`, whose default value is `0` (meaning
                the direction of the first collective variable).

        """
        if sigma is None:
            variances = [(factor * v._range / self._bins[i])**2
                         for i, v in enumerate(self._ufed.variables)]
        else:
            try:
                variances = [_standardized(value)**2 for value in sigma]
            except TypeError:
                variances = [_standardized(sigma)**2] * len(
                    self._ufed.variables)

        exponent = []
        derivative = []
        for v, variance in zip(self._ufed.variables, variances):
            if v.periodic:  # von Mises
                factor = 2 * np.pi / v._range
                exponent.append(lambda x: (np.cos(factor * x) - 1.0) /
                                (factor * factor * variance))
                derivative.append(lambda x: -np.sin(factor * x) /
                                  (factor * variance))
            else:  # Gauss
                exponent.append(lambda x: -0.5 * x**2 / variance)
                derivative.append(lambda x: -x / variance)

        n = len(self._ufed.variables)

        def kernel(x):
            return np.exp(np.sum(exponent[i](x[i]) for i in range(n)))

        def gradient(x, i):
            return kernel(x) * derivative[i](x[i])

        centers = [np.array(xc) for xc in zip(*self.centers)]
        coefficients = []
        for i in range(n):
            for x in centers:
                coefficients.append(
                    np.array([gradient(x - xc, i) for xc in centers]))
        M = np.vstack(coefficients)
        F = -np.hstack(self.mean_forces)
        A, _, _, _ = np.linalg.lstsq(M, F, rcond=None)

        kernels = np.empty((len(centers), len(centers)))
        for i, x in enumerate(centers):
            kernels[i, :] = np.array([kernel(x - xc) for xc in centers])
        potentials = kernels.dot(A)
        minimum = potentials.min()

        def potential(*x):
            xa = np.array(x)
            kernels = np.array([kernel(xa - xc) for xc in centers])
            return np.sum(A * kernels) - minimum

        def mean_force(*x, dir=0):
            xa = np.array(x)
            gradients = np.array([gradient(xa - xc, dir) for xc in centers])
            return -np.sum(A * gradients)

        return np.vectorize(potential), np.vectorize(mean_force)
Exemple #7
0
    def mean_force_free_energy(self,
                               centers,
                               mean_forces,
                               sigma,
                               platform_name='Reference',
                               properties={}):
        """
        Returns Python functions for evaluating the potential of mean force and their originating
        mean forces as a function of the collective variables.

        Parameters
        ----------
            centers : list(numpy.array)
                The bin centers.
            mean_forces : list(numpy.array)
                The mean forces.
            sigmas : float or unit.Quantity or list
                The standard deviation of kernels.

        Keyword Args
        ------------
            platform_name : string, default='Reference'
                The name of the OpenMM Platform to be used for potential and mean-force evaluations.
            properties : dict, default={}
                A set of values for platform-specific properties. Keys are the property names.

        Returns
        -------
            potential : function
                A Python function whose arguments are collective variable values and whose result
                is the potential of mean force at that values.
            mean_force : function
                A Python function whose arguments are collective variable values and whose result
                is the mean force at those values.

        """
        variables = self._ufed.variables
        n = len(variables)
        try:
            variances = [_standardized(value)**2 for value in sigma]
        except TypeError:
            variances = [_standardized(sigma)**2] * n

        exponent = []
        derivative = []
        for v, variance in zip(variables, variances):
            if v.periodic:  # von Mises
                factor = 2 * np.pi / v._range
                exponent.append(lambda x: (np.cos(factor * x) - 1.0) /
                                (factor * factor * variance))
                derivative.append(lambda x: -np.sin(factor * x) /
                                  (factor * variance))
            else:  # Gauss
                exponent.append(lambda x: -0.5 * x**2 / variance)
                derivative.append(lambda x: -x / variance)

        def kernel(x):
            return np.exp(np.sum(exponent[i](x[i]) for i in range(n)))

        def gradient(x, i):
            return kernel(x) * derivative[i](x[i])

        grid_points = [np.array(xc) for xc in zip(*centers)]
        coefficients = []
        for i in range(n):
            for x in grid_points:
                coefficients.append(
                    np.array([gradient(x - xc, i) for xc in grid_points]))

        M = np.vstack(coefficients)
        F = -np.hstack(mean_forces)
        A, _, _, _ = np.linalg.lstsq(M, F, rcond=None)

        platform = openmm.Platform.getPlatformByName(platform_name)
        context = _RBFContext(variables, variances, grid_points, A, platform,
                              properties)
        minimum = 0.0

        def potential(*x):
            for parameter, value in zip(context.parameters, x):
                context.setParameter(parameter, value)
            state = context.getState(getEnergy=True)
            return state.getPotentialEnergy()._value - minimum

        def mean_force(*x):
            for parameter, value in zip(context.parameters, x):
                context.setParameter(parameter, value)
            state = context.getState(getParameterDerivatives=True)
            return -state.getEnergyParameterDerivatives()._value

        minimum = np.min([potential(*x) for x in grid_points])
        return np.vectorize(potential), np.vectorize(mean_force)
Exemple #8
0
def add_inner_nonbonded_force(system, inner_switch, inner_cutoff, force_group_index):
    """
    To a given OpenMM System_ containing a NonbondedForce_ object, this function adds a new force
    group with the purpose of performing multiple time-scale integration according to the RESPA2
    splitting scheme of Morrone, Zhou, and Berne :cite:`Morrone_2010`. Besides, it assigns the
    provided `force_group_index` to this new group and `force_group_index+1` to the original
    NonbondedForce_. When used in any instance of :class:`AbstractMiddleRespaIntegrator`, the new
    force group must be identified as being embodied by the NonbondedForce_ as opposed to being
    complimentary to it.

    .. warning:
        The new force group is not intended to contribute to the system energy. Its sole purpose
        is to provide a smooth, short-range force calculator for some intermediary time scale in
        a RESPA-type integration.

    Parameters
    ----------
        system : openmm.System
            The system the inner force will be added to, which must contain a NonbondedForce_.
        inner_switch : float or unit.Quantity
            The inner switching distance, where the interaction of an atom pair begins to switch
            off to zero.
        inner_cutoff : float or unit.Quantity
            The inner cutoff distance, where the interaction of an atom pairs completely switches
            off.
        force_group_index : int
            The force group the new interactions will belong to. The old NonbondedForce_ will be
            automatically assigned to `force_group_index+1`.

    Example
    -------
        >>> import ufedmm
        >>> from simtk import unit
        >>> dt = 2*unit.femtoseconds
        >>> temp = 300*unit.kelvin
        >>> tau = 10*unit.femtoseconds
        >>> gamma = 10/unit.picoseconds
        >>> model = ufedmm.AlanineDipeptideModel()
        >>> ufedmm.add_inner_nonbonded_force(model.system, 5*unit.angstroms, 8*unit.angstroms, 1)
        >>> for force in model.system.getForces():
        ...     print(force.__class__.__name__, force.getForceGroup())
        HarmonicBondForce 0
        HarmonicAngleForce 0
        PeriodicTorsionForce 0
        NonbondedForce 2
        CustomNonbondedForce 1
        CustomBondForce 1

    """
    if openmm.__version__ < '7.5':
        raise Exception("add_inner_nonbonded_force requires OpenMM version >= 7.5")
    try:
        nonbonded_force = next(filter(lambda f: isinstance(f, openmm.NonbondedForce), system.getForces()))
    except StopIteration:
        raise Exception("add_inner_nonbonded_force requires system with NonbondedForce")
    if nonbonded_force.getNumParticleParameterOffsets() > 0 or nonbonded_force.getNumExceptionParameterOffsets() > 0:
        raise Exception("add_inner_nonbonded_force does not support parameter offsets")
    periodic = nonbonded_force.usesPeriodicBoundaryConditions()
    rs = _standardized(inner_switch)
    rc = _standardized(inner_cutoff)
    a = rc+rs
    b = rc*rs
    c = (30/(rc-rs)**5)*np.array([b**2, -2*a*b, a**2 + 2*b, -2*a, 1])
    f0s = sum([c[n]*rs**(n+1)/(n+1) for n in range(5)])
    def coeff(n, m): return c[m-1] if m == n else c[m-1]/(m-n)
    def func(n, m): return '*log(r)' if m == n else (f'*r^{m-n}' if m > n else f'/r^{n-m}')
    def val(n, m): return f0s if m == 0 else (coeff(n, m) - coeff(0, m) if n != m else coeff(n, m))
    def sgn(n, m): return '+' if m > 0 and val(n, m) >= 0 else ''
    def S(n): return ''.join(f'{sgn(n, m)}{val(n, m)}{func(n, m)}' for m in range(6))
    potential = 'eps4*((sigma/r)^12-(sigma/r)^6)+Qprod/r'
    potential += f'+step(r-{rs})*(eps4*(sigma^12*({S(12)})-sigma^6*({S(6)}))+Qprod*({S(1)}))'
    mixing_rules = '; Qprod=Q1*Q2'
    mixing_rules += '; sigma=halfsig1+halfsig2'
    mixing_rules += '; eps4=sqrt4eps1*sqrt4eps2'
    force = openmm.CustomNonbondedForce(potential + mixing_rules)
    for parameter in ['Q', 'halfsig', 'sqrt4eps']:
        force.addPerParticleParameter(parameter)
    force.setNonbondedMethod(force.CutoffPeriodic if periodic else force.CutoffNonPeriodic)
    force.setCutoffDistance(inner_cutoff)
    force.setUseLongRangeCorrection(False)
    ONE_4PI_EPS0 = 138.93545764438198
    for index in range(nonbonded_force.getNumParticles()):
        charge, sigma, epsilon = map(_standardized, nonbonded_force.getParticleParameters(index))
        force.addParticle([charge*np.sqrt(ONE_4PI_EPS0), sigma/2, np.sqrt(4*epsilon)])
    non_exclusion_exceptions = []
    for index in range(nonbonded_force.getNumExceptions()):
        i, j, q1q2, sigma, epsilon = nonbonded_force.getExceptionParameters(index)
        q1q2, sigma, epsilon = map(_standardized, [q1q2, sigma, epsilon])
        force.addExclusion(i, j)
        if q1q2 != 0.0 or epsilon != 0.0:
            non_exclusion_exceptions.append((i, j, q1q2*ONE_4PI_EPS0, sigma, 4*epsilon))
    force.setForceGroup(force_group_index)
    system.addForce(force)
    if non_exclusion_exceptions:
        exceptions = openmm.CustomBondForce(f'step({rc}-r)*({potential})')
        for parameter in ['Qprod', 'sigma', 'eps4']:
            exceptions.addPerBondParameter(parameter)
        for i, j, Qprod, sigma, eps4 in non_exclusion_exceptions:
            exceptions.addBond(i, j, [Qprod, sigma, eps4])
        exceptions.setForceGroup(force_group_index)
        system.addForce(exceptions)
    nonbonded_force.setForceGroup(force_group_index+1)
    def mean_force_free_energy(self, centers, mean_forces, sigma):
        """
        Returns Python functions for evaluating the potential of mean force and their originating
        mean forces as a function of the collective variables.

        Parameters
        ----------
            centers : list(numpy.array)
                The bin centers.
            mean_forces : list(numpy.array)
                The mean forces.
            sigmas : float or unit.Quantity or list
                The standard deviation of kernels.

        Returns
        -------
            potential : function
                A Python function whose arguments are collective variable values and whose result
                is the potential of mean force at that values.
            mean_force : function
                A Python function whose arguments are collective variable values and whose result
                is the mean force at that values regarding a given direction. Such direction must
                be defined through a keyword argument `dir`, whose default value is `0` (meaning
                the direction of the first collective variable).

        """

        variables = self._ufed.variables
        n = len(variables)
        try:
            variances = [_standardized(value)**2 for value in sigma]
        except TypeError:
            variances = [_standardized(sigma)**2] * n

        exponent = []
        derivative = []
        for v, variance in zip(variables, variances):
            if v.periodic:  # von Mises
                factor = 2 * np.pi / v._range
                exponent.append(lambda x: (math.cos(factor * x) - 1.0) /
                                (factor * factor * variance))
                derivative.append(lambda x: -math.sin(factor * x) /
                                  (factor * variance))
            else:  # Gauss
                exponent.append(lambda x: -0.5 * x**2 / variance)
                derivative.append(lambda x: -x / variance)

        @jit
        def kernel(x):
            trace = 0.0
            for i in range(n):
                trace = trace + exponent[i](x[i])
            return math.exp(trace)
#            return math.exp(np.sum(exponent[i](x[i]) for i in range(n)))
#            return math.exp(np.sum(exponent[i](x[i]) for i in range(n)))

        def gradient(x, i):
            return kernel(x) * derivative[i](x[i])

        center_points = [np.array(xc) for xc in zip(*centers)]
        coefficients = []
        start = time.time()
        for i in range(n):
            for x in center_points:
                coefficients.append(
                    np.array([gradient(x - xc, i) for xc in center_points]))
        end = time.time()
        print("coefficient-appending", end - start)

        start3 = time.time()
        M = np.vstack(coefficients)
        #        M_C=cp.asarray(M)
        #        print(M_C.device)
        F = -np.hstack(mean_forces)
        #        F_C=cp.asarray(F)
        #        B=cp.linalg.lstsq(M_C,F_C,rcond=None)
        #        end3=time.time()
        #        print("linalg-time",end3-start3)

        #        print("B",B)
        A, _, _, _ = np.linalg.lstsq(M, F, rcond=None)
        end3 = time.time()
        print("linalg-time", end3 - start3)
        #        A=cp.asnumpy(B[0])
        start = time.time()
        kernels = np.empty((len(center_points), len(center_points)))
        for i, x in enumerate(center_points):
            kernels[i, :] = np.array([kernel(x - xc) for xc in center_points])
        potentials = kernels.dot(A)
        minimum = potentials.min()
        end = time.time()
        print("minimum-find", end - start)

        def potential(*x):
            xa = np.array(x)
            kernels = np.array([kernel(xa - xc) for xc in center_points])
            #            ker=cp.asarray(kernels)
            #            reslt=cp.sum(A*kernels)-minimum
            return np.sum(A * kernels) - minimum


#            return reslt - minimum

        def mean_force(*x, dir=0):
            xa = np.array(x)
            gradients = np.array(
                [gradient(xa - xc, dir) for xc in center_points])
            return -np.sum(A * gradients)

        return np.vectorize(potential), np.vectorize(mean_force)
Exemple #10
0
    def __init__(self,
                 group,
                 nbforce,
                 style='conductor-reaction-field',
                 damping_coefficient=0.2 / unit.angstroms,
                 scaling_parameter_name='inOutCoulombScaling',
                 pbc_for_exceptions=False):

        rc = _standardized(nbforce.getCutoffDistance())
        alpha_c = _standardized(damping_coefficient) * rc
        prefix = f'{138.935485/rc}*charge1*charge2'
        if style == 'shifted':
            u_C = '1/x - 1'
        elif style == 'shifted-force':
            u_C = '1/x + x - 2'
        elif style == 'conductor-reaction-field':
            u_C = '1/x + x^2/2 - 3/2'
        elif style == 'reaction-field':
            epsilon = nbforce.getReactionFieldDielectric()
            krf = (epsilon - 1) / (2 * epsilon + 1)
            crf = 3 * epsilon / (2 * epsilon + 1)
            u_C = f'1/x + {krf}*x^2 - {crf}'
        elif style == 'damped':
            u_C = f'erfc({alpha_c}*x)/x'
        elif style == 'damped-shifted-force':
            A = math.erfc(alpha_c)
            B = A + 2 * alpha_c * math.exp(-alpha_c**2) / math.sqrt(math.pi)
            u_C = f'erfc({alpha_c}*x)/x + {A+B}*x - {2*A+B}'
        else:
            raise ValueError("Invalid cutoff electrostatic style")

        super().__init__(f'{prefix}*({u_C}); x=r/{rc}')
        parameters = self._get_parameters(nbforce)
        offset_index = {}
        for index in range(nbforce.getNumParticleParameterOffsets()):
            variable, i, charge, _, _ = nbforce.getParticleParameterOffset(
                index)
            if variable == scaling_parameter_name:
                offset_index[i] = index
                parameters[i] = _ParamTuple(charge, parameters[i].sigma,
                                            parameters[i].epsilon)
        self.addPerParticleParameter('charge')
        for parameter in parameters:
            self.addParticle([parameter.charge])
        self._update_nonbonded_force(group, nbforce, parameters,
                                     pbc_for_exceptions)
        self._import_properties(nbforce)
        self.addInteractionGroup(
            set(group),
            set(range(nbforce.getNumParticles())) - set(group))
        self.setUseLongRangeCorrection(False)
        global_vars = map(nbforce.getGlobalParameterName,
                          range(nbforce.getNumGlobalParameters()))
        if scaling_parameter_name not in global_vars:
            nbforce.addGlobalParameter(scaling_parameter_name, 0.0)
        for i in group:
            charge, sigma, epsilon = parameters[i]
            nbforce.setParticleParameters(i, 0.0, sigma, epsilon)
            input = [scaling_parameter_name, i, charge, 0.0, 0.0]
            if i in offset_index:
                nbforce.setParticleParameterOffset(offset_index[i], *input)
            else:
                nbforce.addParticleParameterOffset(*input)