@@ -0,0 +1,1709 @@
+from abc import *%0Afrom typing import List%0A%0Aimport numpy as np%0Afrom numpy.linalg import norm%0A%0A_normalize = lambda v: v / norm(v)%0A%0A%0Aclass Atom(object):%0A    def __init__(self, x: float, y: float, z: float):%0A        self.r = np.array(%5Bx, y, z%5D)%0A%0A    @staticmethod%0A    def distance(i: 'Atom', j: 'Atom') -%3E float:%0A        return norm(i.r - j.r)%0A%0A    @staticmethod%0A    def angle(i: 'Atom', j: 'Atom', k: 'Atom') -%3E float:%0A        ji = _normalize(j.r - i.r)%0A        jk = _normalize(j.r - k.r)%0A%0A        return np.arccos(np.dot(ji, jk))%0A%0A    @staticmethod%0A    def dihedral(i: 'Atom', j: 'Atom', k: 'Atom', l: 'Atom') -%3E float:%0A        # https://stackoverflow.com/questions/20305272/%0A        # dihedral-torsion-angle-from-four-points-in-cartesian-coordinates-in-python%0A        ji = j.r - i.r%0A        kj = _normalize(k.r - j.r)%0A        lk = l.r - k.r%0A%0A        # vector rejections%0A        # v = projection of ji onto plane perpendicular to kj%0A        #   = ji minus component that aligns with kj%0A        # w = projection of lk onto plane perpendicular to kj%0A        #   = lk minus component that aligns with kj%0A        v = ji - np.dot(ji, kj) * kj%0A        w = lk - np.dot(lk, kj) * kj%0A%0A        # angle between v and w in a plane is the torsion angle%0A        # v and w may not be normalized but that's fine since tan is y/x%0A        x = np.dot(v, w)%0A        y = np.dot(np.cross(kj, v), w)%0A%0A        return np.arctan2(y, x)%0A%0A%0Aclass Structure(ABC):%0A    @property%0A    @abstractmethod%0A    def atoms(self) -%3E List%5BAtom%5D:%0A        return self.atoms%0A%0A    # reads a structure from file at filename and fills atoms%0A    @abstractmethod%0A    def __init__(self, filename: str):%0A        self.atoms = None%0A        raise NotImplementedError%0A

Add structure package: - Atom, which represents an atom of a structure, with distance, angle and dihedral - Structure, which processes a structure from xyz or pdb or other file