def dihedral(a1, a2=None, a3=None, a4=None):
    """Twist angle of bonds a1-a2 and a4-a3 around around the central bond a2-a3

    Can be called as ``dihedral(a1, a2, a3, a4)``
              OR     ``dihedral(a2, a2)``
              OR     ``dihedral(bond)``

    Args:
        a1 (mdt.Bond): the central bond in the dihedral. OR
        a1,a2 (mdt.Atom): the atoms describing the dihedral
        a3,a4 (mdt.Atom): (optional) if not passed, ``a1`` and ``a2`` will be treated as the
            central atoms in this bond, and a3 and a4 will be inferred.

    Returns:
        (units.Scalar[angle]): angle -  [0, 2 pi) radians
    """
    if a3 is a4 is None:  # infer the first and last atoms
        a1, a2, a3, a4 = _infer_dihedral(a1, a2)

    r21 = (a1.position - a2.position).defunits_value()  # remove units immediately to improve speed
    r34 = (a4.position - a3.position).defunits_value()

    dim = len(r21.shape)
    center_bond = (a2.position - a3.position).defunits_value()
    plane_normal = normalized(center_bond)

    if dim == 1:
        r21_proj = r21 - plane_normal * r21.dot(plane_normal)
        r34_proj = r34 - plane_normal * r34.dot(plane_normal)
    else:
        assert dim == 2
        r21_proj = r21 - plane_normal * (r21*plane_normal).sum(axis=1)[:, None]
        r34_proj = r34 - plane_normal * (r34*plane_normal).sum(axis=1)[:, None]

    va = normalized(r21_proj)
    vb = normalized(r34_proj)

    if dim == 1:
        costheta = np.dot(va, vb)
        if np.allclose(costheta, 1.0):
            return 0.0 * u.radians
        elif np.allclose(costheta, -1.0):
            return u.pi * u.radians
    else:
        costheta = (va*vb).sum(axis=1)

    abstheta = safe_arccos(costheta)
    cross = np.cross(va, vb)

    if dim == 1:
        theta = abstheta * np.sign(np.dot(cross, plane_normal))
    else:
        theta = abstheta * np.sign((cross*plane_normal).sum(axis=1))
    return (theta * u.radians) % (2.0 * u.pi * u.radians)
def test_bond_alignment_on_axis(benzene):
    mol = benzene.copy()
    directions = ['x', 'y', 'z',
                  [1,2,3.0],
                  [0,1,0],
                  [0.1, 0.1, 0.1] * u.angstrom]

    for i, dir in enumerate(directions):
        bond = mdt.Bond(*random.sample(mol.atoms, 2))
        center = (i % 2) == 0.0
        bond.align(dir, centered=center)

        if center:
            np.testing.assert_allclose(bond.midpoint, np.zeros(3),
                                       atol=1.e-12)
            np.testing.assert_allclose(bond.a1.position.defunits_value(),
                                       -bond.a2.position.defunits_value(),
                                       atol=1e-10)
        if isinstance(dir, str):
            if dir == 'x':
                d = np.array([1.0, 0, 0])
            elif dir == 'y':
                d = np.array([0.0, 1.0, 0.0])
            elif dir == 'z':
                d = np.array([0.0, 0.0, 1.0])
            else:
                raise WtfError()
        else:
            d = normalized(u.array(dir))

        newvec = (bond.a2.position - bond.a1.position).normalized()
        assert abs(1.0 - d.dot(newvec)) < 1e-10
Beispiel #3
0
def test_perpendicular(setupfn):
    arr, expected_norm = setupfn()
    vectorized = len(arr.shape) > 1

    normvecs = mathutils.normalized(arr)
    perpvecs = mathutils.perpendicular(arr)
    assert isinstance(perpvecs, np.ndarray)
    assert not hasattr(perpvecs, 'units')

    # test that vectors are indeed perpendicular
    if vectorized:
        assert (np.abs((normvecs * perpvecs).sum(axis=1)) < 1e-12).all()
    else:
        assert abs(perpvecs.dot(normvecs)) < 1e-12

    # test that they are unit vectors (or 0 if the input vector is zero)
    if not vectorized:
        arr = [arr]
        perpvecs = [perpvecs]
        expected_norm = [expected_norm]

    for i, (vec, perpvec) in enumerate(zip(arr, perpvecs)):
        if expected_norm[i] == 0.0:
            assert mathutils.norm(perpvec) < 1e-12
        else:
            assert np.abs(1.0 - mathutils.norm(perpvec)) < 1e-12
def test_perpendicular(setupfn):
    arr, expected_norm = setupfn()
    vectorized = len(arr.shape) > 1

    normvecs = mathutils.normalized(arr)
    perpvecs = mathutils.perpendicular(arr)
    assert isinstance(perpvecs, np.ndarray)
    assert not hasattr(perpvecs, 'units')

    # test that vectors are indeed perpendicular
    if vectorized:
        assert (np.abs((normvecs * perpvecs).sum(axis=1)) < 1e-12).all()
    else:
        assert abs(perpvecs.dot(normvecs)) < 1e-12

    # test that they are unit vectors (or 0 if the input vector is zero)
    if not vectorized:
        arr = [arr]
        perpvecs = [perpvecs]
        expected_norm = [expected_norm]

    for i, (vec, perpvec) in enumerate(zip(arr, perpvecs)):
        if expected_norm[i] == 0.0:
            assert mathutils.norm(perpvec) < 1e-12
        else:
            assert np.abs(1.0 - mathutils.norm(perpvec)) < 1e-12
def test_bond_alignment_on_axis(benzene):
    mol = benzene.copy()
    directions = [
        'x', 'y', 'z', [1, 2, 3.0], [0, 1, 0], [0.1, 0.1, 0.1] * u.angstrom
    ]

    for i, dir in enumerate(directions):
        bond = mdt.Bond(*random.sample(mol.atoms, 2))
        center = (i % 2) == 0.0
        bond.align(dir, centered=center)

        if center:
            np.testing.assert_allclose(bond.midpoint, np.zeros(3), atol=1.e-12)
            np.testing.assert_allclose(bond.a1.position.defunits_value(),
                                       -bond.a2.position.defunits_value(),
                                       atol=1e-10)
        if isinstance(dir, str):
            if dir == 'x':
                d = np.array([1.0, 0, 0])
            elif dir == 'y':
                d = np.array([0.0, 1.0, 0.0])
            elif dir == 'z':
                d = np.array([0.0, 0.0, 1.0])
            else:
                raise WtfError()
        else:
            d = normalized(u.array(dir))

        newvec = (bond.a2.position - bond.a1.position).normalized()
        assert abs(1.0 - d.dot(newvec)) < 1e-10
Beispiel #6
0
def test_normalized(setupfn):
    arr, expected_norm = setupfn()
    vectorized = len(arr.shape) > 1
    normed = mathutils.normalized(arr)
    if not vectorized:
        arr, expected_norm, normed = [arr], [expected_norm], [normed]
    for v, n, unitvec in zip(arr, expected_norm, normed):
        if n != 0:
            helpers.assert_almost_equal(unitvec, v / n)
def test_normalized(setupfn):
    arr, expected_norm = setupfn()
    vectorized = len(arr.shape) > 1
    normed = mathutils.normalized(arr)
    if not vectorized:
        arr, expected_norm, normed = [arr], [expected_norm], [normed]
    for v, n, unitvec in zip(arr, expected_norm, normed):
        if n != 0:
            helpers.assert_almost_equal(unitvec, v/n)
def test_align_two_bonds(benzene, h2):
    h2 = h2.copy()
    alignbond = list(h2.bonds)[0]
    one_was_different = False

    for targetbond in benzene.bonds:

        # sanity checks before we start:
        assert not np.allclose(alignbond.midpoint.defunits_value(),
                               targetbond.midpoint.defunits_value())
        vec1, vec2 = (normalized(b.a2.position - b.a1.position)
                      for b in (alignbond, targetbond))
        one_was_different = (one_was_different or vec1.dot(vec2) < 0.8)

        alignbond.align(targetbond)

        np.testing.assert_allclose(alignbond.midpoint, targetbond.midpoint)
        vec1, vec2 = (normalized(b.a2.position - b.a1.position)
                      for b in (alignbond, targetbond))
        assert (1.0 - vec1.dot(vec2)) < 1e-8
def test_align_two_bonds(benzene, h2):
    h2 = h2.copy()
    alignbond = list(h2.bonds)[0]
    one_was_different = False

    for targetbond in benzene.bonds:

        # sanity checks before we start:
        assert not np.allclose(alignbond.midpoint.defunits_value(),
                               targetbond.midpoint.defunits_value())
        vec1, vec2 = (normalized(b.a2.position - b.a1.position)
                      for b in (alignbond, targetbond))
        one_was_different = (one_was_different or vec1.dot(vec2) < 0.8)

        alignbond.align(targetbond)

        np.testing.assert_allclose(alignbond.midpoint, targetbond.midpoint)
        vec1, vec2 = (normalized(b.a2.position - b.a1.position)
                      for b in (alignbond, targetbond))
        assert (1.0 - vec1.dot(vec2)) < 1e-8
def angle(a1, a2, a3):
    """ The angle between bonds a2-a1 and a2-a3

    Args:
        a1,a2,a3 (mdt.Atom): the atoms describing the angle

    Returns:
        u.Scalar[length]: the distance
    """
    r21 = (a1.position - a2.position).defunits_value()  # remove units immediately to improve speed
    r23 = (a3.position - a2.position).defunits_value()
    e12 = normalized(r21)
    e23 = normalized(r23)

    dim = len(e12.shape)
    if dim == 2:
        costheta = (e12*e23).sum(axis=1)
    else:
        assert dim == 1
        costheta = np.dot(e12, e23)
    theta = safe_arccos(costheta)
    return theta * u.radians
def distance_gradient(a1, a2):
    r""" Gradient of the distance between two atoms,

    .. math::
        \frac{\partial \mathbf{R}_1}{\partial \mathbf{r}} ||\mathbf{R}_1 - \mathbf{R}_2|| =
           \frac{\mathbf{R}_1 - \mathbf{R}_2}{||\mathbf{R}_1 - \mathbf{R}_2||}

    Args:
        a1,a2 (mdt.Atom): the two atoms

    Returns:
        Tuple[u.Vector[length], u.Vector[length]]: (gradient w.r.t. first atom, gradient w.r.t.
        second atom)
    """
    d = normalized(a1.position-a2.position)
    return d, -d
def distance_gradient(a1, a2):
    r""" Gradient of the distance between two atoms,

    .. math::
        \frac{\partial \mathbf{R}_1}{\partial \mathbf{r}} ||\mathbf{R}_1 - \mathbf{R}_2|| =
           \frac{\mathbf{R}_1 - \mathbf{R}_2}{||\mathbf{R}_1 - \mathbf{R}_2||}

    Args:
        a1,a2 (mdt.Atom): the two atoms

    Returns:
        Tuple[u.Vector[length], u.Vector[length]]: (gradient w.r.t. first atom, gradient w.r.t.
        second atom)
    """
    d = normalized(a1.position-a2.position)
    return d, -d