Skip to content

3D rotations and translations of coordinates

This module contains function to rotate and translate 3D cartesian coordinates.

snow.misc.rototranslation

ax_from_two_points(coord_pt_1, coord_pt_2)

get the vector connecting two points, oriented from the first to the second.

Parameters:

Name Type Description Default
coord_pt_1 ndarray or list

coordinates of the first point

required
coord_pt_2 ndarray or list

coordinates of the second point

required

Returns:

Name Type Description
ax_connecting ndarray

vector connecting the two points

Source code in snow/misc/rototranslation.py
def ax_from_two_points(coord_pt_1, coord_pt_2):
    """
    get the vector connecting two points, oriented from the first to the second.

    Parameters
    ----------
    coord_pt_1 : np.ndarray or list
        coordinates of the first point
    coord_pt_2 : np.ndarray or list
        coordinates of the second point

    Returns
    -------
    ax_connecting : np.ndarray
        vector connecting the two points

    """

    x_ax = coord_pt_2[0] - coord_pt_1[0]
    y_ax = coord_pt_2[1] - coord_pt_1[1]
    z_ax = coord_pt_2[2] - coord_pt_1[2]

    ax_connecting = np.asarray([x_ax, y_ax, z_ax])

    return ax_connecting

translate_com_to_origin(coords, elements=None)

Shifts the positions to the center of mass reference system (so that the center of mass is in the origin).

If elements are provided, a mass-weighted average of positions is performed, otherwise (elements=None), a simple geometrical average is used.

Parameters:

Name Type Description Default
coords ndarray

Array of atomic coordinates

required
elements list

List of element symbols corresponding to the atoms. Default to None. If None, all positions will have the same weight in the calculation of the center of mass

None

Returns:

Name Type Description
new_coords ndarray

shifted coords

Source code in snow/misc/rototranslation.py
def translate_com_to_origin(coords : np.ndarray, elements=None) -> np.ndarray:
    """
    Shifts the positions to the center of mass reference system (so that the center of mass is in the origin). 

    If elements are provided, a mass-weighted average of positions is performed, otherwise (elements=None), a simple
    geometrical average is used.

    Parameters
    ----------
    coords : np.ndarray
        Array of atomic coordinates
    elements : list
        List of element symbols corresponding to the atoms. Default to None.
        If None, all positions will have the same weight in the calculation of the center of mass

    Returns
    -------
    new_coords : np.ndarray 
        shifted coords
    """

    if elements is not None:
        return coords - com(elements, coords)
    else:
        return coords - gcom(coords)

rotate_around_ax(coords, axis, angle)

Rotate coordinates around a given axis by a given angle (in radians).

Parameters:

Name Type Description Default
coords (array - like, shape(..., 3))

Coordinates to rotate.

required
axis (array - like, shape(3))

Rotation axis.

required
angle float

Rotation angle in radians.

required

Returns:

Name Type Description
new_coords ndarray

Rotated coordinates, same shape as input.

Source code in snow/misc/rototranslation.py
def rotate_around_ax(coords, axis, angle):
    """
    Rotate coordinates around a given axis by a given angle (in radians).

    Parameters
    ----------
    coords : array-like, shape (..., 3)
        Coordinates to rotate.
    axis : array-like, shape (3,)
        Rotation axis.
    angle : float
        Rotation angle in radians.

    Returns
    -------
    new_coords : np.ndarray
        Rotated coordinates, same shape as input.
    """

    axis = np.asarray(axis, dtype=float)

    n = np.linalg.norm(axis)
    if n == 0:
        raise ValueError("Rotation axis must be non-zero.")
    axis = axis / n

    x, y, z = axis
    c = np.cos(angle)
    s = np.sin(angle)
    C = 1.0 - c

    # Rodrigues' rotation matrix
    R = np.array([
        [c + x*x*C,     x*y*C - z*s, x*z*C + y*s],
        [y*x*C + z*s,   c + y*y*C,   y*z*C - x*s],
        [z*x*C - y*s,   z*y*C + x*s, c + z*z*C]
    ])

    # Apply rotation (works for shape (N,3) or (...,3))
    return coords @ R.T

align_axis_to_z(coords, axis)

Rotates the system so that the provided axis is aligned with the z=(0,0,1) axis

Parameters:

Name Type Description Default
coords ndarray

Array of the atomic coordinates

required
axis ndarray

axis to become the new z-axis of the coordinates

required

Returns:

Name Type Description
new_coords ndarray

The transformed coordinates

Source code in snow/misc/rototranslation.py
def align_axis_to_z(coords: np.ndarray, axis: np.ndarray) -> np.ndarray:
    """ 
    Rotates the system so that the provided axis is aligned with the z=(0,0,1) axis

    Parameters
    ----------
    coords : np.ndarray
        Array of the atomic coordinates
    axis : np.ndarray
        axis to become the new z-axis of the coordinates

    Returns
    -------
    new_coords : np.ndarray
        The transformed coordinates
    """

    #two possible bad cases
    if np.allclose(axis, [0., 0., 1.]):
        return coords
    elif np.allclose(axis, [0., 0., -1.]):
        return rotate_around_ax(coords, [1., 0., 0.], np.pi)

    axis = np.asarray(axis, dtype = float)
    axis /= np.linalg.norm(axis) 
    rotation_axis = np.cross(axis, np.array([0, 0, 1]))

    #angle of rotation
    cos_theta = np.dot(axis, np.array([0, 0, 1]))
    sin_theta = np.linalg.norm(rotation_axis)

    angle = np.arctan2(sin_theta, cos_theta)

    #normalizing the rot axis
    rotation_axis = rotation_axis / np.linalg.norm(rotation_axis)

    return rotate_around_ax(coords, rotation_axis, angle)

align_z_to_axis(coords, axis)

Rotates the system so that the original z-axis of the system is aligned with the provided axis

Parameters:

Name Type Description Default
coords ndarray

Array of the atomic coordinates

required
axis ndarray

axis to become the new z-axis of the coordinates

required

Returns:

Name Type Description
new_coords ndarray

The transformed coordinates

Source code in snow/misc/rototranslation.py
def align_z_to_axis(coords: np.ndarray, axis: np.ndarray) -> np.ndarray:
    """ 
    Rotates the system so that the original z-axis of the system 
    is aligned with the provided axis

    Parameters
    ----------
    coords : np.ndarray
        Array of the atomic coordinates
    axis : np.ndarray
        axis to become the new z-axis of the coordinates

    Returns
    -------
    new_coords : np.ndarray
        The transformed coordinates
    """

    #two possible bad cases
    if np.allclose(axis, [0., 0., 1.]):
        return coords
    elif np.allclose(axis, [0., 0., -1.]):
        return rotate_around_ax(coords, [1., 0., 0.], np.pi)

    axis = np.asarray(axis, dtype = float)
    axis /= np.linalg.norm(axis) 
    rotation_axis = np.cross(np.array([0, 0, 1]), axis)

    #angle of rotation
    cos_theta = np.dot(axis, np.array([0, 0, 1]))
    sin_theta = np.linalg.norm(rotation_axis)

    angle = np.arctan2(sin_theta, cos_theta)

    #normalizing the rot axis
    rotation_axis = rotation_axis / np.linalg.norm(rotation_axis)

    return rotate_around_ax(coords, rotation_axis, angle)