Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions PQAnalysis/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .cell import Cell, Cells
from .exceptions import (
AtomError,
CellError,
ElementNotFoundError,
ResidueError,
ResidueWarning,
Expand All @@ -28,6 +29,7 @@
"ResidueError": ".exceptions",
"ResidueWarning": ".exceptions",
"AtomError": ".exceptions",
"CellError": ".exceptions",
"Cell": ".cell",
"Cells": ".cell",
"Atom": ".atom",
Expand Down
45 changes: 42 additions & 3 deletions PQAnalysis/core/cell/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
A module containing the Cell class.
"""

import logging
import sys
import warnings

Expand All @@ -14,9 +15,12 @@

from PQAnalysis.type_checking import runtime_type_checking
from PQAnalysis.types import Np3x3NumberArray, Np2DNumberArray, NpnDNumberArray, PositiveReal, Bool
from PQAnalysis.utils.custom_logging import setup_logger
from PQAnalysis.utils.math import allclose_vectorized
from PQAnalysis import __package_name__

from ._standard_properties import _StandardPropertiesMixin
from ..exceptions import CellError



Expand All @@ -26,6 +30,9 @@ class Cell(_StandardPropertiesMixin):
Class for storing unit cell parameters.
"""

logger = logging.getLogger(__package_name__).getChild(__qualname__)
logger = setup_logger(logger)

@runtime_type_checking
def __init__(
self,
Expand Down Expand Up @@ -72,7 +79,30 @@ def setup_box_matrix(self) -> Np3x3NumberArray:
-------
box matrix: Np3x3NumberArray
The box matrix.

Raises
------
CellError
If a box length is not positive, a box angle is not
within the interval (0, 180) or the box angles do not
define a valid unit cell.
"""
if not np.all(np.greater(self.box_lengths, 0)):
self.logger.error(
f"Box lengths must be positive, but got {self.box_lengths}.",
exception=CellError
)

if not (
np.all(np.greater(self.box_angles, 0)) and
np.all(np.less(self.box_angles, 180))
):
self.logger.error(
"Box angles must be within the interval (0, 180) degrees, "
f"but got {self.box_angles}.",
exception=CellError
)

matrix = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]])

alpha, beta, gamma = np.deg2rad(self.box_angles)
Expand All @@ -81,14 +111,23 @@ def setup_box_matrix(self) -> Np3x3NumberArray:
sin_beta = np.sin(beta)
x, y, z = self.box_lengths

radicand = (
sin_beta**2 - (cos_alpha - cos_beta * cos_gamma)**2 / sin_gamma**2
)

if not radicand > 0:
self.logger.error(
f"Box angles {self.box_angles} do not define a valid "
"unit cell.",
exception=CellError
)

matrix[0][0] = x
matrix[0][1] = y * cos_gamma
matrix[0][2] = z * cos_beta
matrix[1][1] = y * sin_gamma
matrix[1][2] = z * (cos_alpha - cos_beta * cos_gamma) / sin_gamma
matrix[2][2] = z * np.sqrt(
sin_beta**2 - (cos_alpha - cos_beta * cos_gamma)**2 / sin_gamma**2
)
matrix[2][2] = z * np.sqrt(radicand)

return matrix

Expand Down
8 changes: 8 additions & 0 deletions PQAnalysis/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,11 @@ class AtomError(PQException):
"""
Exception raised for errors related to the Atom class
"""



class CellError(PQException):

"""
Exception raised for errors related to the Cell class
"""
64 changes: 63 additions & 1 deletion tests/core/test_cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import pytest

from . import pytestmark
from ..conftest import assert_logging_with_exception

from PQAnalysis.core import Cell
from PQAnalysis.core import Cell, CellError



Expand Down Expand Up @@ -35,6 +36,67 @@ def test__init__(self):
)
)

def test__init__non_positive_box_lengths(self, caplog):
assert_logging_with_exception(
caplog,
Cell.__qualname__,
"ERROR",
"Box lengths must be positive, but got [0. 0. 0.].",
CellError,
Cell,
0.0,
0.0,
0.0
)

def test__init__box_angles_out_of_range(self, caplog):
assert_logging_with_exception(
caplog,
Cell.__qualname__,
"ERROR",
"Box angles must be within the interval (0, 180) degrees, "
"but got [190. 90. 90.].",
CellError,
Cell,
10.0,
10.0,
10.0,
190.0,
90.0,
90.0
)

def test__init__impossible_box_angles(self, caplog):
assert_logging_with_exception(
caplog,
Cell.__qualname__,
"ERROR",
"Box angles [170. 10. 10.] do not define a valid unit cell.",
CellError,
Cell,
10.0,
10.0,
10.0,
170.0,
10.0,
10.0
)

def test_set_box_lengths_non_positive(self, caplog):
cell = Cell(1, 2, 3)

def set_box_lengths():
cell.box_lengths = np.array([0.0, 2.0, 3.0])

assert_logging_with_exception(
caplog,
Cell.__qualname__,
"ERROR",
"Box lengths must be positive, but got [0. 2. 3.].",
CellError,
set_box_lengths
)

def test_box_lengths(self):
cell = Cell(1, 2, 3)
assert np.allclose(cell.box_lengths, np.array([1, 2, 3]))
Expand Down
Loading