Skip to content

Orientation

Module for optimizing the orientation of a molecular system to minimize its bounding box volume.

This module provides functions to rotate a set of atomic coordinates in 3D space and to find the optimal rotation that results in the smallest possible bounding box volume. It leverages the MDAnalysis library for reading molecular structures, NumPy for numerical operations, SciPy for optimization and rotations, and loguru for logging.

The core functionality includes: - Rotating a set of atomic positions by specified Euler angles. - Calculating the volume of the bounding box enclosing a set of positions. - Using optimization algorithms to find the rotation angles that minimize this volume. - A convenience function to load a PDB file, perform the optimization, and optionally save the rotated coordinates to a new PDB file.

This module is particularly useful for preparing molecular structures for simulations or analyses where a compact representation or a specific orientation is desired.

minimize_box(positions)

Rotates a system of particles to minimize the volume of its bounding box.

This function takes the Cartesian coordinates of a system of particles and uses numerical optimization to find the Euler angles that, when applied to the system, result in the smallest possible axis-aligned bounding box volume. The optimization is performed using the scipy.optimize.minimize function with the volume_objective_f as the objective function. The optimization is bounded to rotation angles between 0 and 360 degrees for each axis.

PARAMETER DESCRIPTION
positions

A NumPy array of shape (N, 3) where N is the number of particles, and each row represents the x, y, and z coordinates.

TYPE: NDArray[float64]

RETURNS DESCRIPTION
NDArray[float64]

npt.NDArray[np.float64]: A NumPy array of shape (3,) containing the optimized Euler angles (in degrees) that result in the minimal bounding box volume.

Examples:

>>> import numpy as np
>>> positions = np.array([[0.0, 0.0, 0.0], [2.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
>>> optimized_angles = minimize_box(positions)
>>> print(f"{optimized_angles=}")
optimized_angles=array([0., 0., 0.])

rotate_positions(positions, rotation_v)

Rotates a set of 3D Cartesian coordinates by applying Euler angles.

This function takes an array of atomic positions and a vector of Euler angles (in degrees) and applies the corresponding rotation to the positions. The rotation is performed using the 'xyz' convention for Euler angles, meaning rotations are applied sequentially around the x, y, and z axes.

PARAMETER DESCRIPTION
positions

A NumPy array of shape (N, 3) where N is the number of atoms, and each row represents the x, y, and z coordinates of an atom.

TYPE: NDArray[float64]

rotation_v

A NumPy array of shape (3,) containing the Euler angles in degrees for rotation around the x, y, and z axes, respectively.

TYPE: NDArray[float64]

RETURNS DESCRIPTION
NDArray[float64]

npt.NDArray[np.float64]: A NumPy array of shape (N, 3) containing the rotated atomic coordinates.

Examples:

>>> import numpy as np
>>> positions = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
>>> rotation_angles = np.array(
...     [90.0, 0.0, 0.0]
... )  # Rotate 90 degrees around the x-axis
>>> rotated_positions = rotate_positions(positions, rotation_angles)
>>> print(rotated_positions)
[[ 1.00000000e+00  0.00000000e+00  0.00000000e+00]
 [ 0.00000000e+00  6.12323400e-17  1.00000000e+00]]

run_minimize_box(pdb_path, output_path=None)

Minimizes the bounding box size of a molecular system described in a PDB file by rotating it.

This function serves as a high-level interface to load a molecular structure from a PDB file, optimize its orientation to minimize the bounding box volume using the minimize_box function, and optionally save the rotated coordinates to a new PDB file.

PARAMETER DESCRIPTION
pdb_path

The path to the input PDB file containing the molecular structure.

TYPE: str

output_path

The path to save a new PDB file with the rotated coordinates. If None, no file is written, and only the optimized atomic positions are returned. Defaults to None.

TYPE: str | None DEFAULT: None

RETURNS DESCRIPTION
NDArray[float64]

npt.NDArray[np.float64]: A NumPy array of shape (N, 3) containing the atomic positions that have been rotated to achieve the minimum bounding box volume.

RAISES DESCRIPTION
FileNotFoundError

If the specified pdb_path does not exist.

IOError

If there is an error reading the PDB file or writing to the output file.

Examples:

To load a PDB file named "input.pdb", minimize its bounding box volume, and save the result to "optimized.pdb":

>>> optimized_positions = run_minimize_box(
...     "input.pdb", output_path="optimized.pdb"
... )

To perform the minimization and get the optimized positions without saving to a file:

>>> optimized_positions = run_minimize_box("input.pdb")
>>> print(optimized_positions)
[[...], [...], ...]

volume_objective_f(rotation_v, positions)

Objective function to be minimized, returning the volume of the bounding box.

This function takes a set of Euler angles and a set of atomic positions as input. It first rotates the positions using the provided angles and then calculates the volume of the smallest axis-aligned bounding box that encloses the rotated positions. This function is designed to be used with optimization algorithms to find the rotation that minimizes the bounding box volume.

PARAMETER DESCRIPTION
rotation_v

A NumPy array of shape (3,) containing the Euler angles in degrees for rotation around the x, y, and z axes, respectively.

TYPE: NDArray[float64]

positions

A NumPy array of shape (N, 3) where N is the number of atoms, and each row represents the x, y, and z coordinates of an atom.

TYPE: NDArray[float64]

RETURNS DESCRIPTION
float

The volume of the bounding box enclosing the rotated atomic positions.

TYPE: float

Notes

The bounding box volume is calculated using the get_box_volume function from the simlify.structure.dims module.

Examples:

>>> import numpy as np
>>> positions = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
>>> rotation_angles = np.array([0.0, 0.0, 0.0])
>>> volume = volume_objective_f(rotation_angles, positions)
>>> print(f"{volume=}")
volume=0.0