예제 #1
0
    def add_subs(self, *lbls, inds=-1):
        """Returns the element list and cartesian geometry from a
        combination of substituents.

        Parameters
        ----------
        lbls : list
            A list of substituent labels to be combined.
        inds : int or array_like, optional
            The indices for substitution between substituents. Setting
            inds=-1 (default) makes the last atom the subtituted atom.
            Otherwise a list of indices can be given for the first of
            each pair of substituents.

        Returns
        -------
        elem : (N,) ndarray
            The atomic symbols of the combined substituent.
        xyz : (N, 3) ndarray
            The atomic cartesian coordinates of the combined substituent.
        """
        if isinstance(inds, int):
            inds = (len(lbls) - 1) * [inds]
        elif len(inds) != len(lbls) - 1:
            raise ValueError('Number of inds != number of labels - 1')

        rot = 0
        lbl0 = self.syn[lbls[0].lower()]
        elem = self.elem[lbl0]
        xyz = self.xyz[lbl0]
        for i, label in zip(inds, lbls[1:]):
            dist = np.linalg.norm(xyz - xyz[i], axis=1)
            dist[i] += np.max(dist)
            ibond = np.argmin(dist)
            rot = (rot + 1) % 2
            ax = con.unit_vec(xyz[i] - xyz[ibond])
            lbl = self.syn[label.lower()]
            new_elem = self.elem[lbl]
            new_xyz = displace.rotate(self.xyz[lbl], rot * np.pi, 'Z')
            new_xyz = displace.align_axis(new_xyz, 'Z', ax)
            blen = con.get_covrad(elem[ibond]) + con.get_covrad(new_elem[0])
            new_xyz += xyz[ibond] + blen * ax
            elem = np.hstack((np.delete(elem, i), new_elem))
            xyz = np.vstack((np.delete(xyz, i, axis=0), new_xyz))

        return elem, xyz
예제 #2
0
def test_align_axis_origin():
    xyz = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(xyz, 'Z', 'X', origin=xyz[0])
    soln = np.array([xyz[:, 2] - xyz[0, 2], xyz[:, 1],
                     xyz[0, 2] * np.ones(6)]).T
    assert np.allclose(new_xyz, soln)
예제 #3
0
def test_align_axis_ind():
    soln = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(soln, 'Z', '-Y', ind=[0, 1])
    soln[[0, 1], 1] = -soln[[0, 1], 2]
    soln[[0, 1], 2] = 0.
    assert np.allclose(new_xyz, soln)
예제 #4
0
def test_align_axis_pi_z():
    ax = np.array([0., 0., -2.])
    soln = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(soln, ax, 'Z')
    soln[:, 2] *= -1
    assert np.allclose(new_xyz, soln)
예제 #5
0
def test_align_axis_same():
    ax1 = np.array([1., -2., 0.])
    ax2 = 2 * ax1
    xyz = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(xyz, ax1, ax2)
    assert np.allclose(new_xyz, xyz)
예제 #6
0
def test_align_axis_default():
    xyz = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(xyz, xyz[0] - xyz[1], 'Y')
    soln = np.array([xyz[:, 0], xyz[:, 2], -xyz[:, 1]]).T
    assert np.allclose(new_xyz, soln)
예제 #7
0
def subst(elem, xyz, sublbl, isub, ibond=None, pl=None, vec=None):
    """Returns a molecular geometry with an specified atom replaced by
    substituent.

    Labels are case-insensitive. The index isub gives the position to be
    substituted. If specified, ibond gives the atom bonded to the
    substituent. Otherwise, the nearest atom to isub is used. The
    orientation of the substituent can be given as a vector (the plane
    normal) or an index (the plane containing isub, ibond and pl).

    If isub is given as a list, the entire list of atoms is removed
    and the first index is treated as the position of the substituent.

    Parameters
    ----------
    elem : (N,) array_like
        The atomic symbols of the unsubstituted molecule.
    xyz : (N, 3) array_like
        The atomic cartesian coordinates of the unsubstituted molecule.
    sublbl : str
        The substituent label.
    isub : int or list
        The atomic index (or indices) to be replaced by the substituent.
    ibond : int, optional
        The atomic index of the atom bonded to position isub. If None
        (default), the nearest atom is chosen.
    pl : int or array_like, optional
        The atomic index or vector defining the xz-plane of the
        substituent. If an index is given, the plane normal to the
        isub-ibond-pl plane is used. If None (default), the plane
        is arbitrarily set to [1, 1, 1] and the bond axis is projected
        out.
    vec : (N, 3) array_like, optional
        The atomic cartesian vectors of the unsubstitued molecule. Default
        is None.

    Returns
    -------
    new_elem : (N,) ndarray
        The atomic symbols of the substituted molecule.
    new_xyz : (N, 3) ndarray
        The atomic cartesian coordinates of the substituted molecule.
    new_vec : (N, 3) ndarray
        The atomic cartesian vectors of the substituted molecule.
        Substituent atoms are all set of zero. If vec is None, new_vec
        is all zeros.
    """
    elem = np.array(elem)
    xyz = np.atleast_2d(xyz)
    if not isinstance(isub, int):
        ipos = isub[0]
    else:
        isub = [isub]
        ipos = isub[0]

    if ibond is None:
        dist = np.linalg.norm(xyz - xyz[ipos], axis=1)
        dist[ipos] += np.max(dist)
        ibond = np.argmin(dist)
    elif ibond == ipos:
        raise ValueError('sub and bond indices cannot be the same')

    ax = con.unit_vec(xyz[ipos] - xyz[ibond])
    if pl is None:
        # choose an arbitrary axis and project out the bond axis
        pl = np.ones(3)
        pl -= np.dot(pl, ax) * ax
    elif isinstance(pl, int):
        if pl == ipos:
            raise ValueError('plane and sub indices cannot be the same')
        elif pl == ibond:
            raise ValueError('plane and bond indices cannot be the same')
        pl = np.cross(xyz[ipos] - xyz[ibond], xyz[pl] - xyz[ibond])

    sub_el, sub_xyz = import_sub(sublbl)
    if elem[ipos] == sub_el[0]:
        blen = np.linalg.norm(xyz[ipos] - xyz[ibond])
    else:
        blen = con.get_covrad(elem[ibond]) + con.get_covrad(sub_el[0])

    # rotate to correct orientation and displace to correct position
    sub_xyz = displace.align_axis(sub_xyz, 'Z', ax)
    sub_pl = displace.align_axis([0., 1., 0.], 'Z', ax)
    sub_xyz = displace.align_axis(sub_xyz, sub_pl, pl)
    sub_xyz += xyz[ibond] + blen * ax

    # build the final geometry
    ind1 = [i for i in range(ipos) if i not in isub[1:]]
    ind2 = [i for i in range(ipos + 1, len(elem)) if i not in isub[1:]]
    new_elem = np.hstack((elem[ind1], sub_el, elem[ind2]))
    new_xyz = np.vstack((xyz[ind1], sub_xyz, xyz[ind2]))
    if vec is None:
        return new_elem, new_xyz, None
    else:
        new_vec = np.vstack((vec[ind1], np.zeros((len(sub_el), 3)), vec[ind2]))
        return new_elem, new_xyz, new_vec
예제 #8
0
def test_align_axis_origin():
    xyz = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(xyz, 'z', 'x', origin=xyz[0])
    soln = np.array([xyz[:,2] - xyz[0,2], xyz[:,1], xyz[0,2]*np.ones(6)]).T
    assert np.allclose(new_xyz, soln)
예제 #9
0
def test_align_axis_ind():
    soln = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(soln, 'z', '-y', ind=[0,1])
    soln[[0,1],1] = -soln[[0,1],2]
    soln[[0,1],2] = 0.
    assert np.allclose(new_xyz, soln)
예제 #10
0
def test_align_axis_pi_z():
    ax = np.array([0., 0., -2.])
    soln = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(soln, ax, 'z')
    soln[:,2] *= -1
    assert np.allclose(new_xyz, soln)
예제 #11
0
def test_align_axis_same():
    ax1 = np.array([1., -2., 0.])
    ax2 = 2*ax1
    xyz = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(xyz, ax1, ax2)
    assert np.allclose(new_xyz, xyz)
예제 #12
0
def test_align_axis_default():
    xyz = np.copy(eg.c2h4[1])
    new_xyz = displace.align_axis(xyz, xyz[0]-xyz[1], 'y')
    soln = np.array([xyz[:,0], xyz[:,2], -xyz[:,1]]).T
    assert np.allclose(new_xyz, soln)