diff --git a/MDANSE/Extensions/.gitignore b/MDANSE/Extensions/.gitignore deleted file mode 100644 index d5419cf253..0000000000 --- a/MDANSE/Extensions/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/*.c -/*.cpp -/*.html -/xtc/xtc.c -/xtc/trr.c -/xtc/*.html \ No newline at end of file diff --git a/MDANSE/Extensions/atoms_in_shell.pyx b/MDANSE/Extensions/atoms_in_shell.pyx deleted file mode 100644 index 126987314d..0000000000 --- a/MDANSE/Extensions/atoms_in_shell.pyx +++ /dev/null @@ -1,162 +0,0 @@ - -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import cython -import numpy as np -cimport numpy as np -from numpy cimport ndarray - -cdef extern from "math.h": - - double floor(double x) - double ceil(double x) - double sqrt(double x) - -cdef inline double round(double r): - return floor(r + 0.5) if (r > 0.0) else ceil(r - 0.5) - -def atoms_in_shell_real(ndarray[np.float64_t, ndim=2] config not None, - ndarray[np.float64_t, ndim=2] cell not None, - ndarray[np.float64_t, ndim=2] rcell not None, - int refIndex, - float mini, - float maxi): - - cdef double x, y, z, refx, refy, refz, refsx, refsy, refsz, sdx, sdy, sdz, rx, ry, rz, r2 - - cdef int i - - cdef ndarray[np.float64_t, ndim=2] scaleconfig = np.empty((config.shape[0], 3), dtype=np.float64) - - cdef double mini2 = mini**2 - cdef double maxi2 = maxi**2 - - refx = config[refIndex, 0] - refy = config[refIndex, 1] - refz = config[refIndex, 2] - - for 0 <= i < config.shape[0]: - - x = config[i, 0] - y = config[i, 1] - z = config[i, 2] - - scaleconfig[i, 0] = x*rcell[0, 0] + y*rcell[0, 1] + z*rcell[0, 2] - scaleconfig[i, 1] = x*rcell[1, 0] + y*rcell[1, 1] + z*rcell[1, 2] - scaleconfig[i, 2] = x*rcell[2, 0] + y*rcell[2, 1] + z*rcell[2, 2] - - refsx = scaleconfig[refIndex, 0] - refsy = scaleconfig[refIndex, 1] - refsz = scaleconfig[refIndex, 2] - - indexes = [] - - for 0 <= i < config.shape[0]: - - if i == refIndex: - continue - - sdx = scaleconfig[i, 0] - refsx - sdy = scaleconfig[i, 1] - refsy - sdz = scaleconfig[i, 2] - refsz - - sdx -= round(sdx) - sdy -= round(sdy) - sdz -= round(sdz) - - rx = sdx*cell[0, 0] + sdy*cell[0, 1] + sdz*cell[0, 2] - ry = sdx*cell[1, 0] + sdy*cell[1, 1] + sdz*cell[1, 2] - rz = sdx*cell[2, 0] + sdy*cell[2, 1] + sdz*cell[2, 2] - - r2 = rx*rx + ry*ry + rz*rz - - if r2 >= mini2 and r2 <= maxi2: - indexes.append(i) - - return indexes - -def atoms_in_shell_nopbc(ndarray[np.float64_t, ndim=2] config not None, - int refIndex, - float mini, - float maxi): - - cdef double x, y, z, refx, refy, refz, sdx, sdy, sdz, rx, ry, rz, r2 - - cdef int i - - refx = config[refIndex,0] - refy = config[refIndex,1] - refz = config[refIndex,2] - - cdef double mini2 = mini**2 - cdef double maxi2 = maxi**2 - - indexes = [] - - for 0 <= i < config.shape[0]: - - if i == refIndex: - continue - - rx = config[i,0] - refx - ry = config[i,1] - refy - rz = config[i,2] - refz - - r2 = rx*rx + ry*ry + rz*rz - - if r2 >= mini2 and r2 <= maxi2: - indexes.append(i) - - return indexes - -def atoms_in_shell_box(ndarray[np.float64_t, ndim=2] config not None, - int refIndex, - float mini, - float maxi): - - cdef double x, y, z, refx, refy, refz, sdx, sdy, sdz, r2 - - cdef int i - - cdef double mini2 = mini**2 - cdef double maxi2 = maxi**2 - - refx = config[refIndex,0] - refy = config[refIndex,1] - refz = config[refIndex,2] - - indexes = [] - - for 0 <= i < config.shape[0]: - - if i == refIndex: - continue - - sdx = config[i,0] - refx - sdy = config[i,1] - refy - sdz = config[i,2] - refz - - sdx -= round(sdx) - sdy -= round(sdy) - sdz -= round(sdz) - - r2 = sdx*sdx + sdy*sdy + sdz*sdz - - if r2 >= mini2 and r2 <= maxi2: - indexes.append(i) - - return indexes diff --git a/MDANSE/Extensions/com_trajectory.pyx b/MDANSE/Extensions/com_trajectory.pyx deleted file mode 100644 index 54a8182d4a..0000000000 --- a/MDANSE/Extensions/com_trajectory.pyx +++ /dev/null @@ -1,206 +0,0 @@ - -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import sys - -import cython -import numpy as np -cimport numpy as np - -cdef extern from "math.h": - - double floor(double x) - double ceil(double x) - double sqrt(double x) - -cdef inline double round(double r): - return floor(r + 0.5) if (r > 0.0) else ceil(r - 0.5) - -def _recursive_contiguity( - np.ndarray[np.float64_t, ndim=2] box_coords, - np.ndarray[np.int8_t, ndim=1] processed not None, - bonds, - ref): - - cdef int bat - - cdef double brefx, brefy, brefz, bx, by, bz, bdx, bdy, bdz - - brefx = box_coords[ref,0] - brefy = box_coords[ref,1] - brefz = box_coords[ref,2] - - bonded_atoms = bonds[ref] - - processed[ref] = 1 - - for bat in bonded_atoms: - - if processed[bat] == 1: - continue - - bx = box_coords[bat,0] - by = box_coords[bat,1] - bz = box_coords[bat,2] - - bdx = bx - brefx - bdy = by - brefy - bdz = bz - brefz - - bdx -= round(bdx) - bdy -= round(bdy) - bdz -= round(bdz) - - bx = brefx + bdx - by = brefy + bdy - bz = brefz + bdz - - box_coords[bat,0] = bx - box_coords[bat,1] = by - box_coords[bat,2] = bz - - _recursive_contiguity(box_coords,processed, bonds, bat) - -@cython.boundscheck(False) -@cython.wraparound(False) -@cython.cdivision(True) -def com_trajectory( - np.ndarray[np.float64_t, ndim=3] coords not None, - np.ndarray[np.float64_t, ndim=3] cell not None, - np.ndarray[np.float64_t, ndim=3] rcell not None, - np.ndarray[np.float64_t, ndim=1] masses not None, - chemical_entity_indexes, - selected_indexes, - bonds, - box_coordinates = False): - - cdef double x, y, z, refx, refy, refz, \ - srefx, srefy, srefz, sx, sy, sz, sdx, sdy, sdz, \ - sumMasses, comx, comy, comz - - cdef int i, j, idx, idx0 - - cdef np.ndarray[np.float64_t, ndim=2] trajectory = np.empty((coords.shape[0],3),dtype=np.float64) - - cdef np.ndarray[np.float64_t, ndim=2] com_coords - - cdef np.ndarray[np.int8_t] processed - - cdef np.ndarray[np.float64_t, ndim=3] box_coords = np.empty((coords.shape[0],coords.shape[1],3),dtype=np.float64) - - - old_recursionlimit = sys.getrecursionlimit() - sys.setrecursionlimit(100000) - - # Loop over the time - for 0 <= i < coords.shape[0]: - - # Loop over the atoms - for 0 <= j < coords.shape[1]: - - x = coords[i,j,0] - y = coords[i,j,1] - z = coords[i,j,2] - - # Convert the real coordinates to box coordinates - box_coords[i,j,0] = x*rcell[i,0,0] + y*rcell[i,0,1] + z*rcell[i,0,2] - box_coords[i,j,1] = x*rcell[i,1,0] + y*rcell[i,1,1] + z*rcell[i,1,2] - box_coords[i,j,2] = x*rcell[i,2,0] + y*rcell[i,2,1] + z*rcell[i,2,2] - - # Loop over the time - for 0 <= i < coords.shape[0]: - - com_coords = box_coords[i,:,:] - - processed = np.zeros((com_coords.shape[0],), dtype=np.int8) - - for idxs in chemical_entity_indexes: - - if len(idxs) > 1: - ref_idx = idxs.pop(0) - _recursive_contiguity(com_coords, processed, bonds, ref_idx) - - idx0 = selected_indexes[0] - - # The first atom is taken as reference - srefx = com_coords[idx0,0] - srefy = com_coords[idx0,1] - srefz = com_coords[idx0,2] - - comx = masses[0]*srefx - comy = masses[0]*srefy - comz = masses[0]*srefz - - sumMasses = masses[0] - - # Loop over the other atoms (if any) - for j in range(1,len(selected_indexes)): - - idx = selected_indexes[j] - - sx = com_coords[idx,0] - sy = com_coords[idx,1] - sz = com_coords[idx,2] - - # Update the center of mass - comx += masses[j]*sx - comy += masses[j]*sy - comz += masses[j]*sz - - sumMasses += masses[j] - - if i == 0: - - trajectory[i,0] = comx/sumMasses - trajectory[i,1] = comy/sumMasses - trajectory[i,2] = comz/sumMasses - - # The step i-1 is taken as the reference - else: - - comx /= sumMasses - comy /= sumMasses - comz /= sumMasses - - sdx = comx - trajectory[i-1,0] - sdy = comy - trajectory[i-1,1] - sdz = comz - trajectory[i-1,2] - - sdx -= round(sdx) - sdy -= round(sdy) - sdz -= round(sdz) - - trajectory[i,0] = trajectory[i-1,0] + sdx - trajectory[i,1] = trajectory[i-1,1] + sdy - trajectory[i,2] = trajectory[i-1,2] + sdz - - if not box_coordinates: - - for 0 <= i < trajectory.shape[0]: - - x = trajectory[i,0] - y = trajectory[i,1] - z = trajectory[i,2] - - trajectory[i,0] = x*cell[i,0,0] + y*cell[i,0,1] + z*cell[i,0,2] - trajectory[i,1] = x*cell[i,1,0] + y*cell[i,1,1] + z*cell[i,1,2] - trajectory[i,2] = x*cell[i,2,0] + y*cell[i,2,1] + z*cell[i,2,2] - - sys.setrecursionlimit(old_recursionlimit) - - return trajectory - diff --git a/MDANSE/Extensions/contiguous_coordinates.pyx b/MDANSE/Extensions/contiguous_coordinates.pyx deleted file mode 100644 index 92bb9aa539..0000000000 --- a/MDANSE/Extensions/contiguous_coordinates.pyx +++ /dev/null @@ -1,298 +0,0 @@ - -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import sys - -import cython -import numpy as np -cimport numpy as np -from numpy cimport ndarray - - -cdef extern from "math.h": - - double floor(double x) - double ceil(double x) - double sqrt(double x) - -cdef inline double round(double r): - return floor(r + 0.5) if (r > 0.0) else ceil(r - 0.5) - -def contiguous_coordinates_real(ndarray[np.float64_t, ndim=2] coords not None, - ndarray[np.float64_t, ndim=2] cell not None, - ndarray[np.float64_t, ndim=2] rcell not None, - indexes): - - cdef double x, y, z, sdx, sdy, sdz, newx, newy, newz - - cdef int i - - - cdef ndarray[np.float64_t, ndim=2] contiguous_coords = np.empty((coords.shape[0],3),dtype=np.float64) - - cdef ndarray[np.float64_t, ndim=2] scaleconfig = np.empty((coords.shape[0],3),dtype=np.float64) - - for 0 <= i < coords.shape[0]: - - x = coords[i,0] - y = coords[i,1] - z = coords[i,2] - - scaleconfig[i,0] = x*rcell[0,0] + y*rcell[0,1] + z*rcell[0,2] - scaleconfig[i,1] = x*rcell[1,0] + y*rcell[1,1] + z*rcell[1,2] - scaleconfig[i,2] = x*rcell[2,0] + y*rcell[2,1] + z*rcell[2,2] - - for idxs in indexes: - - if not idxs: - continue - - contiguous_coords[idxs[0],0] = coords[idxs[0],0] - contiguous_coords[idxs[0],1] = coords[idxs[0],1] - contiguous_coords[idxs[0],2] = coords[idxs[0],2] - - if len(idxs) == 1: - continue - - for idx in idxs[1:]: - - sdx = scaleconfig[idx,0] - scaleconfig[idxs[0],0] - sdy = scaleconfig[idx,1] - scaleconfig[idxs[0],1] - sdz = scaleconfig[idx,2] - scaleconfig[idxs[0],2] - - sdx -= round(sdx) - sdy -= round(sdy) - sdz -= round(sdz) - - newx = scaleconfig[idxs[0],0] + sdx - newy = scaleconfig[idxs[0],1] + sdy - newz = scaleconfig[idxs[0],2] + sdz - - contiguous_coords[idx,0] = newx*cell[0,0] + newy*cell[0,1] + newz*cell[0,2] - contiguous_coords[idx,1] = newx*cell[1,0] + newy*cell[1,1] + newz*cell[1,2] - contiguous_coords[idx,2] = newx*cell[2,0] + newy*cell[2,1] + newz*cell[2,2] - - return contiguous_coords - -def contiguous_coordinates_box(ndarray[np.float64_t, ndim=2] coords not None, - ndarray[np.float64_t, ndim=2] cell not None, - indexes): - - cdef double sdx, sdy, sdz, newx, newy, newz - - cdef ndarray[np.float64_t, ndim=2] contiguous_coords = np.empty((coords.shape[0], 3), dtype=np.float64) - - for idxs in indexes: - contiguous_coords[idxs[0], 0] = coords[idxs[0], 0] - contiguous_coords[idxs[0], 1] = coords[idxs[0], 1] - contiguous_coords[idxs[0], 2] = coords[idxs[0], 2] - - if len(idxs) == 1: - continue - - for idx in idxs[1:]: - - sdx = coords[idx, 0] - coords[idxs[0], 0] - sdy = coords[idx, 1] - coords[idxs[0], 1] - sdz = coords[idx, 2] - coords[idxs[0], 2] - - sdx -= round(sdx) - sdy -= round(sdy) - sdz -= round(sdz) - - newx = coords[idxs[0], 0] + sdx - newy = coords[idxs[0], 1] + sdy - newz = coords[idxs[0], 2] + sdz - - contiguous_coords[idx, 0] = newx*cell[0, 0] + newy*cell[0, 1] + newz*cell[0, 2] - contiguous_coords[idx, 1] = newx*cell[1, 0] + newy*cell[1, 1] + newz*cell[1, 2] - contiguous_coords[idx, 2] = newx*cell[2, 0] + newy*cell[2, 1] + newz*cell[2, 2] - - return contiguous_coords - -def contiguous_offsets_real(ndarray[np.float64_t, ndim=2] coords not None, - ndarray[np.float64_t, ndim=2] cell not None, - ndarray[np.float64_t, ndim=2] rcell not None, - indexes): - - cdef double x, y, z, sdx, sdy, sdz - - cdef int i - - cdef ndarray[np.float64_t, ndim=2] scaleconfig = np.empty((coords.shape[0],3),dtype=np.float64) - - cdef ndarray[np.float64_t, ndim=2] offsets = np.zeros((coords.shape[0],3),dtype=np.float64) - - for 0 <= i < coords.shape[0]: - - x = coords[i,0] - y = coords[i,1] - z = coords[i,2] - - scaleconfig[i,0] = x*rcell[0,0] + y*rcell[0,1] + z*rcell[0,2] - scaleconfig[i,1] = x*rcell[1,0] + y*rcell[1,1] + z*rcell[1,2] - scaleconfig[i,2] = x*rcell[2,0] + y*rcell[2,1] + z*rcell[2,2] - - for idxs in indexes: - if len(idxs) == 1: - continue - - for idx in idxs[1:]: - - sdx = scaleconfig[idx,0] - scaleconfig[idxs[0],0] - sdy = scaleconfig[idx,1] - scaleconfig[idxs[0],1] - sdz = scaleconfig[idx,2] - scaleconfig[idxs[0],2] - - offsets[idx,0] = -round(sdx) - offsets[idx,1] = -round(sdy) - offsets[idx,2] = -round(sdz) - - return offsets - -def contiguous_offsets_box(ndarray[np.float64_t, ndim=2] coords not None, - ndarray[np.float64_t, ndim=2] cell not None, - indexes): - - cdef double sdx, sdy, sdz - - cdef ndarray[np.float64_t, ndim=2] offsets = np.zeros((coords.shape[0], 3), dtype=np.float64) - - for idxs in indexes: - if len(idxs) == 1: - continue - - for idx in idxs[1:]: - - sdx = coords[idx, 0] - coords[idxs[0], 0] - sdy = coords[idx, 1] - coords[idxs[0], 1] - sdz = coords[idx, 2] - coords[idxs[0], 2] - - offsets[idx, 0] = -round(sdx) - offsets[idx, 1] = -round(sdy) - offsets[idx, 2] = -round(sdz) - - return offsets - -def _recursive_contiguity( - ndarray[np.float64_t, ndim=2] contiguous_coords, - ndarray[np.float64_t, ndim=2] cell not None, - ndarray[np.float64_t, ndim=2] rcell not None, - ndarray[np.int8_t, ndim=1] processed not None, - bonds, - seed): - - cdef double refx, refy, refz, brefx, brefy, brefz,x, y, z, bx, by, bz, bdx, bdy, bdz - - # The seed atom is marked as processed - processed[seed] = 1 - - # Convert from real to box coordinates for the seed atom - refx = contiguous_coords[seed,0] - refy = contiguous_coords[seed,1] - refz = contiguous_coords[seed,2] - - brefx = refx*rcell[0,0] + refy*rcell[0,1] + refz*rcell[0,2] - brefy = refx*rcell[1,0] + refy*rcell[1,1] + refz*rcell[1,2] - brefz = refx*rcell[2,0] + refy*rcell[2,1] + refz*rcell[2,2] - - bonded_atoms = bonds[seed] - - # Loop over the atoms bonded to the seed - for bat in bonded_atoms: - - # If the bonded atom is marked as processed skip it - if processed[bat] == 1: - continue - - # Convert from real to box coordinates for the bonded atom - x = contiguous_coords[bat,0] - y = contiguous_coords[bat,1] - z = contiguous_coords[bat,2] - - bx = x*rcell[0,0] + y*rcell[0,1] + z*rcell[0,2] - by = x*rcell[1,0] + y*rcell[1,1] + z*rcell[1,2] - bz = x*rcell[2,0] + y*rcell[2,1] + z*rcell[2,2] - - # Apply the PBC - bdx = bx - brefx - bdy = by - brefy - bdz = bz - brefz - - bdx -= round(bdx) - bdy -= round(bdy) - bdz -= round(bdz) - - bx = brefx + bdx - by = brefy + bdy - bz = brefz + bdz - - # Convert back from box to real coordinates - contiguous_coords[bat,0] = bx*cell[0,0] + by*cell[0,1] + bz*cell[0,2] - contiguous_coords[bat,1] = bx*cell[1,0] + by*cell[1,1] + bz*cell[1,2] - contiguous_coords[bat,2] = bx*cell[2,0] + by*cell[2,1] + bz*cell[2,2] - - _recursive_contiguity(contiguous_coords,cell,rcell,processed,bonds,bat) - -def continuous_coordinates( - ndarray[np.float64_t, ndim=2] coords not None, - ndarray[np.float64_t, ndim=2] cell not None, - ndarray[np.float64_t, ndim=2] rcell not None, - chemical_system, - selected_indexes=None): - - cdef int ref_idx - - cdef ndarray[np.float64_t, ndim=2] continuous_coords = coords - - cdef np.ndarray[np.int8_t] processed = np.zeros((coords.shape[0],), dtype=np.int8) - - old_recursionlimit = sys.getrecursionlimit() - sys.setrecursionlimit(100000) - - # Retrieve the top level chemical entities to which belong each of the selected atom - atoms = chemical_system.atom_list - if selected_indexes is None: - selected_indexes = [at.index for at in atoms] - - chemical_entities = set([atoms[idx].top_level_chemical_entity for idx in selected_indexes]) - - # Set the bond network for these chemical entities - bonds = {} - chemical_entities_indexes = [] - for ce in chemical_entities: - for at in ce.atom_list: - bonds[at.index] = [bat.index for bat in at.bonds] - chemical_entities_indexes.append(at.index) - - chemical_entities_indexes.sort() - - for idx in chemical_entities_indexes: - - _recursive_contiguity(continuous_coords, cell, rcell, processed, bonds, idx) - - sys.setrecursionlimit(old_recursionlimit) - - return continuous_coords[selected_indexes,:] - - - - - - - - - diff --git a/MDANSE/Extensions/fast_calculation.pyx b/MDANSE/Extensions/fast_calculation.pyx deleted file mode 100644 index 4933c57643..0000000000 --- a/MDANSE/Extensions/fast_calculation.pyx +++ /dev/null @@ -1,121 +0,0 @@ - -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import cython -import numpy as np -cimport numpy as np - - -cdef extern from "math.h": - - double floor(double x) - double ceil(double x) - double sqrt(double x) - - -cdef inline double round(double r): - return floor(r + 0.5) if (r > 0.0) else ceil(r - 0.5) - - -@cython.cdivision(True) -@cython.boundscheck(False) -@cython.wraparound(False) -def cpt_cluster_connectivity_nopbc( - np.ndarray[np.float64_t, ndim=2] coords, - np.ndarray[np.float64_t, ndim=1] covRadii, - np.float32_t tolerance): - - ''' - Compute the connectivity of an atom cluster. - ''' - bonds = [] - cdef int i, j, nbat - cdef float radius, distance - - nbat = len(covRadii) - - for i in range(nbat-1): - radiusi = covRadii[i] + tolerance - for j in range(i+1,nbat): - radius = radiusi + covRadii[j] - distance = (coords[i,0] - coords[j,0])**2 + (coords[i,1] - coords[j,1])**2 + (coords[i,2] - coords[j,2])**2 - if distance <= (radius*radius): - bonds.append([i,j]) - return bonds - - -@cython.cdivision(True) -@cython.boundscheck(False) -@cython.wraparound(False) -def cpt_cluster_connectivity_pbc( - np.ndarray[np.float64_t, ndim=2] coords, - np.ndarray[np.float64_t, ndim=2] cell not None, - np.ndarray[np.float64_t, ndim=2] rcell not None, - np.ndarray[np.float64_t, ndim=1] covRadii, - np.float32_t tolerance, - bonds): - - ''' - Compute the connectivity of an atom cluster. - ''' - - cdef int i, j, nbat - - cdef float radius, distance, sdx, sdy, sdz, rx, ry, rz, r - - nbat = len(covRadii) - - cdef np.ndarray[np.float64_t, ndim=2] scaleconfig = np.empty((coords.shape[0],3),dtype=np.float64) - - for 0 <= i < coords.shape[0]: - - x = coords[i,0] - y = coords[i,1] - z = coords[i,2] - - scaleconfig[i,0] = x*rcell[0,0] + y*rcell[0,1] + z*rcell[0,2] - scaleconfig[i,1] = x*rcell[1,0] + y*rcell[1,1] + z*rcell[1,2] - scaleconfig[i,2] = x*rcell[2,0] + y*rcell[2,1] + z*rcell[2,2] - - for i in range(nbat-1): - - radiusi = covRadii[i] + tolerance - - for j in range(i+1,nbat): - radius = radiusi + covRadii[j] - - sdx = scaleconfig[j,0] - scaleconfig[i,0] - sdy = scaleconfig[j,1] - scaleconfig[i,1] - sdz = scaleconfig[j,2] - scaleconfig[i,2] - - sdx -= round(sdx) - sdy -= round(sdy) - sdz -= round(sdz) - - rx = sdx*cell[0,0] + sdy*cell[0,1] + sdz*cell[0,2] - ry = sdx*cell[1,0] + sdy*cell[1,1] + sdz*cell[1,2] - rz = sdx*cell[2,0] + sdy*cell[2,1] + sdz*cell[2,2] - - r = rx*rx + ry*ry + rz*rz - - if r <= (radius*radius): - bonds.append([i,j]) - - return bonds - - - diff --git a/MDANSE/Extensions/mic_fast_calc.pyx b/MDANSE/Extensions/mic_fast_calc.pyx deleted file mode 100644 index 3a1555074e..0000000000 --- a/MDANSE/Extensions/mic_fast_calc.pyx +++ /dev/null @@ -1,193 +0,0 @@ - -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -cimport numpy as np -import numpy as np -from numpy cimport ndarray - -from libcpp.vector cimport vector -from libcpp.pair cimport pair -from libcpp cimport bool - -ctypedef pair[ int, vector[int] ] equiv_t -ctypedef vector[double] point - -cdef inline bool exclude_from_bounded_3Dbox(double x, - double y, - double z, - double Xmin, - double Xmax, - double Ymin, - double Ymax, - double Zmin, - double Zmax, - double border): - return not ((Xmin-border <=x) and (x <= Xmax+border) and (Ymin-border <= y) and (y <= Ymax+border) and (Zmin-border <= z) and (z <= Zmax+border)) - -cdef inline bool exclude_from_bounded_2Dbox(double x, - double y, - double Xmin, - double Xmax, - double Ymin, - double Ymax, - double border): - return not ((Xmin-border <=x) and (x <= Xmax+border) and (Ymin-border <= y) and (y <= Ymax+border)) - - - -def mic_generator_2D(double[:,:] pts not None, double border, double[:] box_param): - - cdef int i, m, new_id, nb_init_points, nb_final_points - cdef double x, y, Xmin, Xmax, Ymin, Ymax, j, k, n0, n1 - cdef vector[equiv_t] equivalence - cdef vector[point] new_points - cdef equiv_t e - cdef point n - cdef double lol[3] - cdef ndarray[np.float64_t, ndim = 2] res - - nb_init_points = pts.shape[0] - - Xmax = box_param[0] - Ymax = box_param[1] - Xmin = 0 - Ymin = 0 - - lol[0] = -1 - lol[1] = 0 - lol[2] = 1 - - for i in range(nb_init_points): - x = pts[i,0] - y = pts[i,1] - for j in lol[0:3]: - for k in lol[0:3]: - if j == 0 and k == 0: - continue - n0 = x+j*(Xmax-Xmin) - n1 = y+k*(Ymax-Ymin) - - if exclude_from_bounded_2Dbox(n0, n1, Xmin, Xmax, Ymin, Ymax, border): - continue - n = point() - n.push_back(n0) - n.push_back(n1) - new_points.push_back(n) - new_id = new_points.size() + nb_init_points - for m in range(equivalence.size()): - if equivalence[m].first == i: - equivalence[m].second.push_back(new_id) - continue - e = equiv_t() - e.first=i - e.second.push_back(new_id) - equivalence.push_back(e) - d = {} - for i in range(equivalence.size()): - d[equivalence[i].first] = equivalence[i].second - - nb_final_points = pts.shape[0] + new_points.size() - - res = np.zeros((nb_init_points + new_points.size() ,2), dtype = np.float64) - - for i in range(nb_init_points): - res[i,0] = pts[i,0] - res[i,1] = pts[i,1] - - for i in range(new_points.size()): - j = i + nb_init_points - res[j,0] = new_points[i][0] - res[j,1] = new_points[i][1] - - - return res, d - - - -def mic_generator_3D(double[:,:] pts not None, double border, double[:] box_param): - - cdef int i, j_int, m, new_id, nb_init_points - cdef double x, y, z, Xmin, Xmax, Ymin, Ymax, Zmin, Zmax, j, k, l, n0, n1, n2 - cdef vector[equiv_t] equivalence - cdef vector[point] new_points - cdef equiv_t e - cdef point n - cdef double lol[3] - cdef ndarray[np.float64_t, ndim = 2] res - - nb_init_points = pts.shape[0] - - Xmax = box_param[0] - Ymax = box_param[1] - Zmax = box_param[2] - Xmin = 0 - Ymin = 0 - Zmin = 0 - - lol[0] = -1 - lol[1] = 0 - lol[2] = 1 - - for i in range(nb_init_points): - x = pts[i,0] - y = pts[i,1] - z = pts[i,2] - for j in lol[0:3]: - for k in lol[0:3]: - for l in lol[0:3]: - if j == 0 and k == 0 and l == 0: - continue - n0 = x+j*(Xmax-Xmin) - n1 = y+k*(Ymax-Ymin) - n2 = z+l*(Zmax-Zmin) - - if exclude_from_bounded_3Dbox(n0, n1, n2, Xmin, Xmax, Ymin, Ymax, Zmin, Zmax, border): - continue - n = point() - n.push_back(n0) - n.push_back(n1) - n.push_back(n2) - new_points.push_back(n) - new_id = new_points.size() + nb_init_points - for m in range(equivalence.size()): - if equivalence[m].first == i: - equivalence[m].second.push_back(new_id) - continue - e = equiv_t() - e.first=i - e.second.push_back(new_id) - equivalence.push_back(e) - - d = {} - for i in range(equivalence.size()): - d[equivalence[i].first] = equivalence[i].second - - res = np.zeros((nb_init_points + new_points.size() ,3), dtype = np.float64) - - for i in range(nb_init_points): - res[i,0] = pts[i,0] - res[i,1] = pts[i,1] - res[i,2] = pts[i,2] - - for i in range(new_points.size()): - j = i + nb_init_points - j_int = int(j) - res[j_int,0] = new_points[i][0] - res[j_int,1] = new_points[i][1] - res[j_int,2] = new_points[i][2] - - return res, d diff --git a/MDANSE/Extensions/sas_fast_calc.pyx b/MDANSE/Extensions/sas_fast_calc.pyx deleted file mode 100644 index 88cfdebfc5..0000000000 --- a/MDANSE/Extensions/sas_fast_calc.pyx +++ /dev/null @@ -1,157 +0,0 @@ - -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import cython -import numpy as np -cimport numpy as np -from numpy cimport ndarray -from libc.string cimport memset - -cdef extern from "math.h": - double floor(double x) - double ceil(double x) - double sqrt(double x) - -cdef packed struct neighbour_t: - np.int32_t index - np.float64_t dist - -@cython.boundscheck(False) -@cython.wraparound(False) -def sas(ndarray[np.float64_t, ndim = 2] config not None, - indexes not None, - ndarray[np.float64_t, ndim = 2] vdwRadii_list not None, - ndarray[np.float64_t, ndim = 2] sphere_points not None, - double probe_radius_value): - - # Computes the Solvent Accessible Surface Based on the algorithm published by Shrake, A., and J. A. Rupley. JMB (1973) 79:351-371. - - cdef int total, i, j, k, n, isAccessible, nAccessiblePoints - cdef double sas, dist, v, radi, r, radius, two_times_prob_radius - cdef double Xposi, Yposi, Zposi, Xposj, Yposj, Zposj, Xposk, Yposk, Zposk, Xs, Ys, Zs, Xguess, Yguess, Zguess, dx, dy, dz - - # The solvent accessible surface for the running frame (given by index var). - total = ( config).shape[0] - - # The list of the neighbors indexes. - neighbour_dtype=[("index",np.int32),("dist",np.float64)] - - cdef np.ndarray[neighbour_t, ndim = 1] neighbors = np.recarray(shape=(total,),dtype=neighbour_dtype) - - memset(&neighbors[0], -1, total *sizeof(neighbour_t)) - - sas = 0. - two_times_prob_radius = 2.0*probe_radius_value - for idx in indexes: - - # The position of atoms |idx| in the current configuration. - Xposi = config[idx,0] - Yposi = config[idx,1] - Zposi = config[idx,2] - - # The probe radius of atom |index|. - radius = vdwRadii_list[idx][1] - radius = radius + two_times_prob_radius - # Loop over all the atoms. - for item in vdwRadii_list: - k = item[0] - v = item[1] - # Skip the case where the atoms is itself. - if k == idx: - neighbors[k].index = k - neighbors[k].dist = -1 - continue - - Xposk = config[k,0] - Yposk = config[k,1] - Zposk = config[k,2] - # ?? identical to --> dist = self.configuration['trajectory']['instance'].universe.distance(self.atoms[index],self.atoms[k]) - # The distance between atoms |index| and |k|. - dx = Xposi - Xposk - dy = Yposi - Yposk - dz = Zposi - Zposk - - dist = dx*dx + dy*dy + dz*dz - - # If the distance is less than the probe radius + the VDW radius of atoms |k|, atom |k| is considered to be a neighbor of atom |index|. - if dist < (radius + v)*(radius + v): - neighbors[k].index = k - neighbors[k].dist = v - else: - neighbors[k].index = k - neighbors[k].dist = -1. - - # Sort the neighbors by increasing distance. -> not usefull ? cost of the sort / cost to parse the full list - - neighbors.sort(order = 'dist') - # The probe radius of atoms |idx|. - radi = radius + probe_radius_value - - # A counter for the number of atoms |idx| sphere points accessible to solvent. - nAccessiblePoints = 0 - - # Loop over the sphere points surrounding atoms |idx|.\ - nb_neighbors = neighbors.shape[0] - for Spoint in sphere_points: - - # The running point is first considered to be accessible. - isAccessible = True - Xs = Spoint[0] - Ys = Spoint[1] - Zs = Spoint[2] - # Build the sphere point vector. - Xguess = Xs*radi + Xposi - Yguess = Ys*radi + Yposi - Zguess = Zs*radi + Zposi - - # Loop over the neighbors of atoms |idx|. - - for 0 <= n < nb_neighbors: - if neighbors[n].dist == -1: - continue - j = neighbors[n].index - # The position of neighbors |j|. - Xposj = config[j,0] - Yposj = config[j,1] - Zposj = config[j,2] - - # The probe radius of neighbor |j|. - r = vdwRadii_list[j][1] - r = r + probe_radius_value - - # The squared distance between the neighbors |j| and the running sphere point. - dx = Xposj - Xguess - dy = Yposj - Yguess - dz = Zposj - Zguess - - dist = dx*dx + dy*dy + dz*dz - - # If the squared distance is less than the squared probe radius, the running sphere point is not accessible. - if dist < r*r: - isAccessible = False - break - - # Increase the number of accessible point if the running sphere point is found to be accessible. - if isAccessible: - nAccessiblePoints += 1 - - # Updates the SAS with the contribution for atom |i|. - sas += nAccessiblePoints*radi*radi - # reset neighbors list - memset(&neighbors[0], -1, total *sizeof(neighbour_t)) - - return sas \ No newline at end of file diff --git a/MDANSE/Extensions/van_hove.pyx b/MDANSE/Extensions/van_hove.pyx deleted file mode 100644 index d8699fd71f..0000000000 --- a/MDANSE/Extensions/van_hove.pyx +++ /dev/null @@ -1,165 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import cython -cimport numpy as np -from numpy cimport ndarray - - -cdef extern from "math.h": - double floor(double x) - double ceil(double x) - double sqrt(double x) - - -cdef inline double round(double r): - return floor(r + 0.5) if (r > 0.0) else ceil(r - 0.5) - - -def van_hove_distinct( - double[:,:] cell, - int[:] molindex, - int[:] symbolindex, - double[:,:,:] intra, - double[:,:,:] inter, - double[:,:] scaleconfig_t0, - double[:,:] scaleconfig_t1, - double rmin, - double dr -): - """Calculates the distance histogram between the configurations at - times t0 and t1. Distances are calculated using the minimum image - convention which are all worked out using the fractional coordinates - with the unit cell of the configuration at time t1. The function can - be used to calculate the distinct part of the van Hove function. - - This function was an generalization of a Pyrex adaptation of a - FORTRAN implementation made by Miguel Angel Gonzalez (Institut Laue - Langevin) for the calculation of intra and intermolecular distance - histograms. - - Parameters - ---------- - cell : np.ndarray - The transpose of the direct matrix of the configuration at - time t1. - molindex : np.ndarray - An array which maps atom indexes to molecule indexes. - symbolindex : np.ndarray - An array which maps atom indexes to symbol indexes. - intra : np.ndarray - An output array to save the distance histogram results of - intramolecular atom differences. - inter : np.ndarray - An output array to save the distance histogram results of - intermolecular atom differences. - scaleconfig_t0 : np.ndarray - The coordinates of the configuration at t0 in fractional - coordinate in the unit cell of the configuration at time t1. - scaleconfig_t1 : np.ndarray - The coordinates of the configuration at t1 in fractional - coordinate in the unit cell of the configuration at time t1. - rmin : float - The minimum distance of the histogram. - dr : float - The distances between histogram bins. - """ - - cdef double sdx, sdy, sdz, rx, ry, rz, r - cdef int i, j, bin, nbins - nbins = intra.shape[2] - - for i in range(scaleconfig_t0.shape[0] - 1): - - sx = scaleconfig_t0[i,0] - sy = scaleconfig_t0[i,1] - sz = scaleconfig_t0[i,2] - - for j in range(i + 1, scaleconfig_t0.shape[0]): - - sdx = scaleconfig_t1[j,0] - sx - sdy = scaleconfig_t1[j,1] - sy - sdz = scaleconfig_t1[j,2] - sz - - sdx -= round(sdx) - sdy -= round(sdy) - sdz -= round(sdz) - - rx = sdx*cell[0,0] + sdy*cell[0,1] + sdz*cell[0,2] - ry = sdx*cell[1,0] + sdy*cell[1,1] + sdz*cell[1,2] - rz = sdx*cell[2,0] + sdy*cell[2,1] + sdz*cell[2,2] - - r = sqrt(rx*rx + ry*ry + rz*rz) - bin = ((r-rmin)/dr) - if ((bin < 0) or (bin >= nbins)): - continue - - if molindex[i] == molindex[j]: - intra[symbolindex[i],symbolindex[j],bin] += 1.0 - else: - inter[symbolindex[i],symbolindex[j],bin] += 1.0 - - -def van_hove_self( - double[:,:] xyz, - double[:,:] histograms, - double[:] cell_vols, - double rmin, - double dr, - int n_configs, - int n_frames -): - """Calculates the distance histogram between an atom at time t0 - and the same atom at a time t0 + t. The results from this function - can be used to calculate the self part of the van Hove function. - - Parameters - ---------- - xyz : np.ndarray - The trajectory of an atom. - histograms : np.ndarray - The histograms to be updated. - cell_vols : np.ndarray - The cell volumes. - rmin : float - The minimum distance of the histogram. - dr : float - The distances between histogram bins. - n_configs : int - Number of configs to be averaged over. - n_frames : int - Number of correlation frames. - """ - cdef int i, j, bin, nbins - cdef double x0, y0, z0, x1, y1, z1, r - nbins = histograms.shape[0] - - for i in range(n_configs): - x0 = xyz[i,0] - y0 = xyz[i,1] - z0 = xyz[i,2] - - for j in range(n_frames): - rx = xyz[i+j,0] - x0 - ry = xyz[i+j,1] - y0 - rz = xyz[i+j,2] - z0 - - r = sqrt(rx*rx + ry*ry + rz*rz) - bin = ((r-rmin)/dr) - if ((bin < 0) or (bin >= nbins)): - continue - - histograms[bin, j] += cell_vols[i+j] diff --git a/MDANSE/Extensions/xtc/include/ms_stdint.h b/MDANSE/Extensions/xtc/include/ms_stdint.h deleted file mode 100644 index cb2acd9384..0000000000 --- a/MDANSE/Extensions/xtc/include/ms_stdint.h +++ /dev/null @@ -1,259 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2013 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the product nor the names of its contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#if _MSC_VER >= 1600 // [ -#include -#else // ] _MSC_VER >= 1600 [ - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -// These #ifndef's are needed to prevent collisions with . -// Check out Issue 9 for the details. -#ifndef INTMAX_C // [ -# define INTMAX_C INT64_C -#endif // INTMAX_C ] -#ifndef UINTMAX_C // [ -# define UINTMAX_C UINT64_C -#endif // UINTMAX_C ] - -#endif // __STDC_CONSTANT_MACROS ] - -#endif // _MSC_VER >= 1600 ] - -#endif // _MSC_STDINT_H_ ] diff --git a/MDANSE/Extensions/xtc/include/trr_header.h b/MDANSE/Extensions/xtc/include/trr_header.h deleted file mode 100644 index d482821f40..0000000000 --- a/MDANSE/Extensions/xtc/include/trr_header.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _trr_header_h_ -#define _trr_header_h_ - -typedef struct /* This struct describes the order and the */ -/* sizes of the structs in a trjfile, sizes are given in bytes. */ -{ - int bDouble; /* Double precision? */ - int ir_size; /* Backward compatibility */ - int e_size; /* Backward compatibility */ - int box_size; /* Non zero if a box is present */ - int vir_size; /* Backward compatibility */ - int pres_size; /* Backward compatibility */ - int top_size; /* Backward compatibility */ - int sym_size; /* Backward compatibility */ - int x_size; /* Non zero if coordinates are present */ - int v_size; /* Non zero if velocities are present */ - int f_size; /* Non zero if forces are present */ - - int natoms; /* The total number of atoms */ - int step; /* Current step number */ - int nre; /* Backward compatibility */ - float tf; /* Current time */ - float lambdaf; /* Current value of lambda */ - double td; /* Current time */ - double lambdad; /* Current value of lambda */ -} t_trnheader; - -int do_trnheader(XDRFILE *xd, char bRead, t_trnheader *sh); - -#endif diff --git a/MDANSE/Extensions/xtc/include/xdr_seek.h b/MDANSE/Extensions/xtc/include/xdr_seek.h deleted file mode 100644 index 9bc7313929..0000000000 --- a/MDANSE/Extensions/xtc/include/xdr_seek.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _xdr_seek_h -#define _xdr_seek_h - -// for int64_t on older M$ Visual Studio -#if _MSC_VER && _MSVC_VER < 1600 && !__INTEL_COMPILER - #include "ms_stdint.h" -#else - #include -#endif - -#include "xdrfile.h" - -int64_t xdr_tell(XDRFILE *xd); -int xdr_seek(XDRFILE *xd, int64_t pos, int whence); -int xdr_flush(XDRFILE* xd); - -#endif diff --git a/MDANSE/Extensions/xtc/include/xdrfile.h b/MDANSE/Extensions/xtc/include/xdrfile.h deleted file mode 100644 index f86cda1d44..0000000000 --- a/MDANSE/Extensions/xtc/include/xdrfile.h +++ /dev/null @@ -1,631 +0,0 @@ - /* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- - * - * $Id$ - * - * Copyright (c) 2009-2014, Erik Lindahl & David van der Spoel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/*! \file xdrfile.h - * \brief Interface to read/write portabile binary files using XDR. - * - * This file provides an interface to read & write portably binary files, - * using XDR - the external data representation standard defined in RFC 1014. - * - * There are several advantages to the XDR approach: - * - * -# It is portable. And not just portable between big/small integer endian, - * but truly portable if you have system XDR routines. For example: - * - It doesn't matter if the character representation is ASCII or EBCDIC. - * - Some systems are small endian but use big endian order of the two - * dword in a double precision floating-point variable. The system XDR - * libraries will read/write this correctly. - * - Some systems (VAX...) don't use IEEE floating point. Their system - * XDR libraries will convert to/from this automatically. - * -# XDR libraries are required for NFS and lots of other network functions. - * This means there isn't a single Unix-like system that doesn't have them. - * -# There is NO extra metadata whatsoever, and we write plain XDR files. - * If you write a float, it will take exactly 4 bytes in the file. - * (All basic datatypes are 4 bytes, double fp 8 bytes). - * -# You can read/write the files by calling the system XDR routines directly - * too - you don't have to use the routines defined in this file. - * -# It is no problem if your system doesn't have XDR libraries (MS Windows). - * We have written our own versions of the necessary routines that work if - * your system uses ASCII for strings and IEEE floating-point. All types - * of byte and dword endian for integer and floating-point are supported. - * -# You can use these routines for any type of data, but since we designed - * them for Gromacs we also provide a special routine to write coordinates - * with (adjustable) lossy compression. The default precision will give you - * three decimals guaranteed accuracy, and reduces the filesize to 1/10th - * of normal binary data. - * - * We do not support getting or setting positions in XDR files, since it can - * break in horrible ways for large (64-bit) files, resulting in silent data - * corruption. Note that it works great to open/read/write 64-bit files if - * your system supports it; it is just the random access we cannot trust! - * - * We also provide wrapper routines so this module can be used from FORTRAN - - * see the file xdrfile_fortran.txt in the Gromacs distribution for - * documentation on the FORTRAN interface! - */ - -#ifndef _XDRFILE_H_ -#define _XDRFILE_H_ - - -#ifdef __cplusplus -extern "C" -{ -#endif - - /*! \brief Abstract datatype for an portable binary file handle - * - * This datatype essentially works just like the standard FILE type in C. - * The actual contents is hidden in the implementation, so you can only - * define pointers to it, for use with the xdrfile routines. - * - * If you \a really need to see the definition it is in xdrfile.c, but you - * cannot access elements of the structure outside that file. - * - * \warning The implementation is completely different from the C standard - * library FILE, so don't even think about using an XDRFILE pointer as an - * argument to a routine that needs a standard FILE pointer. - */ - typedef struct XDRFILE XDRFILE; - - enum { exdrOK, exdrHEADER, exdrSTRING, exdrDOUBLE, - exdrINT, exdrFLOAT, exdrUINT, exdr3DX, exdrCLOSE, exdrMAGIC, - exdrNOMEM, exdrENDOFFILE, exdrFILENOTFOUND, exdrNR }; - - extern char *exdr_message[exdrNR]; - -#define DIM 3 - typedef float matrix[DIM][DIM]; - typedef float rvec[DIM]; - typedef int mybool; - - - /*! \brief Open a portable binary file, just like fopen() - * - * Use this routine much like calls to the standard library function - * fopen(). The only difference is that the returned pointer should only - * be used with routines defined in this header. - * - * \param path Full or relative path (including name) of the file - * \param mode "r" for reading, "w" for writing, "a" for append. - * - * \return Pointer to abstract xdr file datatype, or NULL if an error occurs. - * - */ - XDRFILE * - xdrfile_open (const char * path, - const char * mode); - - - /*! \brief Close a previously opened portable binary file, just like fclose() - * - * Use this routine much like calls to the standard library function - * fopen(). The only difference is that it is used for an XDRFILE handle - * instead of a FILE handle. - * - * \param xfp Pointer to an abstract XDRFILE datatype - * - * \return 0 on success, non-zero on error. - */ - int - xdrfile_close (XDRFILE * xfp); - - - - - /*! \brief Read one or more \a char type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of characters to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of characters read - */ - int - xdrfile_read_char(char * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a characters type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of characters to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of characters written - */ - int - xdrfile_write_char(char * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Read one or more \a unsigned \a char type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of unsigned characters to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of unsigned characters read - */ - int - xdrfile_read_uchar(unsigned char * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a unsigned \a characters type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of unsigned characters to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of unsigned characters written - */ - int - xdrfile_write_uchar(unsigned char * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Read one or more \a short type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of shorts to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of shorts read - */ - int - xdrfile_read_short(short * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a short type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of shorts to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of shorts written - */ - int - xdrfile_write_short(short * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Read one or more \a unsigned \a short type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of unsigned shorts to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of unsigned shorts read - */ - int - xdrfile_read_ushort(unsigned short * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a unsigned \a short type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of unsigned shorts to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of unsigned shorts written - */ - int - xdrfile_write_ushort(unsigned short * ptr, - int ndata, - XDRFILE * xfp); - - - /*! \brief Read one or more \a integer type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of integers to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of integers read - * - * The integer data type is assumed to be less than or equal to 32 bits. - * - * We do not provide any routines for reading/writing 64-bit integers, since - * - Not all XDR implementations support it - * - Not all machines have 64-bit integers - * - * Split your 64-bit data into two 32-bit integers for portability! - */ - int - xdrfile_read_int(int * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a integer type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of integers to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of integers written - * - * The integer data type is assumed to be less than or equal to 32 bits. - * - * We do not provide any routines for reading/writing 64-bit integers, since - * - Not all XDR implementations support it - * - Not all machines have 64-bit integers - * - * Split your 64-bit data into two 32-bit integers for portability! - */ - int - xdrfile_write_int(int * ptr, - int ndata, - XDRFILE * xfp); - - /*! \brief Read one or more \a unsigned \a integers type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of unsigned integers to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of unsigned integers read - * - * The integer data type is assumed to be less than or equal to 32 bits. - * - * We do not provide any routines for reading/writing 64-bit integers, since - * - Not all XDR implementations support it - * - Not all machines have 64-bit integers - * - * Split your 64-bit data into two 32-bit integers for portability! - */ - int - xdrfile_read_uint(unsigned int * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a unsigned \a integer type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of unsigned integers to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of unsigned integers written - * - * The integer data type is assumed to be less than or equal to 32 bits. - * - * We do not provide any routines for reading/writing 64-bit integers, since - * - Not all XDR implementations support it - * - Not all machines have 64-bit integers - * - * Split your 64-bit data into two 32-bit integers for portability! - */ - int - xdrfile_write_uint(unsigned int * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Read one or more \a float type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of floats to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of floats read - */ - int - xdrfile_read_float(float * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a float type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of floats to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of floats written - */ - int - xdrfile_write_float(float * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Read one or more \a double type variable(s) - * - * \param ptr Pointer to memory where data should be written - * \param ndata Number of doubles to read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of doubles read - */ - int - xdrfile_read_double(double * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Write one or more \a double type variable(s) - * - * \param ptr Pointer to memory where data should be read - * \param ndata Number of double to write. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of doubles written - */ - int - xdrfile_write_double(double * ptr, - int ndata, - XDRFILE * xfp); - - - - /*! \brief Read a string (array of characters) - * - * \param ptr Pointer to memory where data should be written - * \param maxlen Maximum length of string. If no end-of-string is encountered, - * one byte less than this is read and end-of-string appended. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of characters read, including end-of-string - */ - int - xdrfile_read_string(char * ptr, - int maxlen, - XDRFILE * xfp); - - - - /*! \brief Write a string (array of characters) - * - * \param ptr Pointer to memory where data should be read - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of characters written, including end-of-string - */ - int - xdrfile_write_string(char * ptr, - XDRFILE * xfp); - - - - /*! \brief Read raw bytes from file (unknown datatype) - * - * \param ptr Pointer to memory where data should be written - * \param nbytes Number of bytes to read. No conversion whatsoever is done. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of bytes read from file - */ - int - xdrfile_read_opaque(char * ptr, - int nbytes, - XDRFILE * xfp); - - - - - /*! \brief Write raw bytes to file (unknown datatype) - * - * \param ptr Pointer to memory where data should be read - * \param nbytes Number of bytes to write. No conversion whatsoever is done. - * \param xfp Handle to portable binary file, created with xdrfile_open() - * - * \return Number of bytes written to file - */ - int - xdrfile_write_opaque(char * ptr, - int nbytes, - XDRFILE * xfp); - - - - - - - /*! \brief Compress coordiates in a float array to XDR file - * - * This routine will perform \a lossy compression on the three-dimensional - * coordinate data data specified and store it in the XDR file. - * - * The lossy part of the compression consists of multiplying each - * coordinate with the precision argument and then rounding to integers. - * We suggest a default value of 1000.0, which means you are guaranteed - * three decimals of accuracy. The only limitation is that scaled coordinates - * must still fit in an integer variable, so if the precision is 1000.0 the - * coordinate magnitudes must be less than +-2e6. - * - * \param ptr Pointer to coordinates to compress (length 3*ncoord) - * \param ncoord Number of coordinate triplets in data - * \param precision Scaling factor for lossy compression. If it is <=0, - * the default value of 1000.0 is used. - * \param xfp Handle to portably binary file - * - * \return Number of coordinate triplets written. - * IMPORTANT: Check that this is equal to ncoord - if it is - * negative, an error occured. This should not happen with - * normal data, but if your coordinates are NaN or very - * large (>1e6) it is not possible to use the compression. - * - * \warning The compression algorithm is not part of the XDR standard, - * and very complicated, so you will need this xdrfile module - * to read it later. - */ - int - xdrfile_compress_coord_float(float * ptr, - int ncoord, - float precision, - XDRFILE * xfp); - - - - - /*! \brief Decompress coordiates from XDR file to array of floats - * - * This routine will decompress three-dimensional coordinate data previously - * stored in an XDR file and store it in the specified array of floats. - * - * The precision used during the earlier compression is read from the file - * and returned - you cannot adjust the accuracy at this stage. - * - * \param ptr Pointer to coordinates to compress (length>= 3*ncoord) - * \param ncoord Max number of coordinate triplets to read on input, actual - * number of coordinate triplets read on return. If this - * is smaller than the number of coordinates in the frame an - * error will occur. - * \param precision The precision used in the previous compression will be - * written to this variable on return. - * \param xfp Handle to portably binary file - * - * \return Number of coordinate triplets read. If this is negative, - * an error occured. - * - * \warning Since we cannot count on being able to set/get the - * position of large files (>2Gb), it is not possible to - * recover from errors by re-reading the frame if the - * storage area you provided was too small. To avoid this - * from happening, we recommend that you store the number of - * coordinates triplet as an integer either in a header or - * just before the compressed coordinate data, so you can - * read it first and allocated enough memory. - */ - int - xdrfile_decompress_coord_float(float * ptr, - int * ncoord, - float * precision, - XDRFILE * xfp); - - - - - /*! \brief Compress coordiates in a double array to XDR file - * - * This routine will perform \a lossy compression on the three-dimensional - * coordinate data data specified and store it in the XDR file. Double will - * NOT give you any extra precision since the coordinates are compressed. This - * routine just avoids allocating a temporary array of floats. - * - * The lossy part of the compression consists of multiplying each - * coordinate with the precision argument and then rounding to integers. - * We suggest a default value of 1000.0, which means you are guaranteed - * three decimals of accuracy. The only limitation is that scaled coordinates - * must still fit in an integer variable, so if the precision is 1000.0 the - * coordinate magnitudes must be less than +-2e6. - * - * \param ptr Pointer to coordinates to compress (length 3*ncoord) - * \param ncoord Number of coordinate triplets in data - * \param precision Scaling factor for lossy compression. If it is <=0, the - * default value of 1000.0 is used. - * \param xfp Handle to portably binary file - * - * \return Number of coordinate triplets written. - * IMPORTANT: Check that this is equal to ncoord - if it is - * negative, an error occured. This should not happen with - * normal data, but if your coordinates are NaN or very - * large (>1e6) it is not possible to use the compression. - * - * \warning The compression algorithm is not part of the XDR standard, - * and very complicated, so you will need this xdrfile module - * to read it later. - */ - int - xdrfile_compress_coord_double(double * ptr, - int ncoord, - double precision, - XDRFILE * xfp); - - - - - /*! \brief Decompress coordiates from XDR file to array of doubles - * - * This routine will decompress three-dimensional coordinate data previously - * stored in an XDR file and store it in the specified array of doubles. - * Double will NOT give you any extra precision since the coordinates are - * compressed. This routine just avoids allocating a temporary array of floats. - * - * The precision used during the earlier compression is read from the file - * and returned - you cannot adjust the accuracy at this stage. - * - * \param ptr Pointer to coordinates to compress (length>= 3*ncoord) - * \param ncoord Max number of coordinate triplets to read on input, actual - * number of coordinate triplets read on return. If this - * is smaller than the number of coordinates in the frame an - * error will occur. - * \param precision The precision used in the previous compression will be - * written to this variable on return. - * \param xfp Handle to portably binary file - * - * \return Number of coordinate triplets read. If this is negative, - * an error occured. - * - * \warning Since we cannot count on being able to set/get the - * position of large files (>2Gb), it is not possible to - * recover from errors by re-reading the frame if the - * storage area you provided was too small. To avoid this - * from happening, we recommend that you store the number of - * coordinates triplet as an integer either in a header or - * just before the compressed coordinate data, so you can - * read it first and allocated enough memory. - */ - int - xdrfile_decompress_coord_double(double * ptr, - int * ncoord, - double * precision, - XDRFILE * xfp); - - - -#ifdef __cplusplus -} -#endif - -#endif /* _XDRFILE_H_ */ diff --git a/MDANSE/Extensions/xtc/include/xdrfile_trr.h b/MDANSE/Extensions/xtc/include/xdrfile_trr.h deleted file mode 100644 index 9d97785e47..0000000000 --- a/MDANSE/Extensions/xtc/include/xdrfile_trr.h +++ /dev/null @@ -1,61 +0,0 @@ - /* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- - * - * $Id$ - * - * Copyright (c) 2009-2014, Erik Lindahl & David van der Spoel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _xdrfile_trr_h -#define _xdrfile_trr_h - -#ifdef __cplusplus -extern "C" { -#endif - -#include "xdrfile.h" - - /* All functions return exdrOK if successful. - * (error codes defined in xdrfile.h). - */ - - /* This function returns the number of atoms in the xtc file in *natoms */ - extern int read_trr_natoms(char *fn,int *natoms); - extern int read_trr_nframes(char* fn, unsigned long *nframes); - - /* Read one frame of an open xtc file. If either of x,v,f,box are - NULL the arrays will be read from the file but not used. */ - extern int read_trr(XDRFILE *xd,int natoms,int *step,float *t,float *lambda, - matrix box,rvec *x,rvec *v,rvec *f); - - /* Write a frame to xtc file */ - extern int write_trr(XDRFILE *xd,int natoms,int step,float t,float lambda, - matrix box,rvec *x,rvec *v,rvec *f); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/MDANSE/Extensions/xtc/include/xdrfile_xtc.h b/MDANSE/Extensions/xtc/include/xdrfile_xtc.h deleted file mode 100644 index 7dd4a7dcb2..0000000000 --- a/MDANSE/Extensions/xtc/include/xdrfile_xtc.h +++ /dev/null @@ -1,61 +0,0 @@ - /* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- - * - * $Id$ - * - * Copyright (c) 2009-2014, Erik Lindahl & David van der Spoel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _xdrfile_xtc_h -#define _xdrfile_xtc_h - -#ifdef __cplusplus -extern "C" { -#endif - -#include "xdrfile.h" - - /* All functions return exdrOK if succesfull. - * (error codes defined in xdrfile.h). - */ - - /* This function returns the number of atoms in the xtc file in *natoms */ - extern int read_xtc_natoms(char *fn,int *natoms); - - int read_xtc_nframes(char* fn, unsigned long *nframes); - - /* Read one frame of an open xtc file */ - extern int read_xtc(XDRFILE *xd,int natoms,int *step,float *time, - matrix box,rvec *x,float *prec); - - /* Write a frame to xtc file */ - extern int write_xtc(XDRFILE *xd, - int natoms,int step,float time, - matrix box,rvec *x,float prec); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/MDANSE/Extensions/xtc/src/README b/MDANSE/Extensions/xtc/src/README deleted file mode 100644 index 1083c8e6dc..0000000000 --- a/MDANSE/Extensions/xtc/src/README +++ /dev/null @@ -1,17 +0,0 @@ -Low-level C libraries for manipulating GROMACS XTC and TRR files. This code -is derived from xdrfile-1.1.4, available at - -http://www.gromacs.org/Developer_Zone/Programming_Guide/XTC_Library -ftp://ftp.gromacs.org/pub/contrib/xdrfile-1.1.4.tar.gz - -These files are licensed under a BSD 2-clause license, and copyright -Erik Lindahl, David van der Spoel, Robert T. McGibbon. - -Changes from upsteam's xdrfile-1.1.4 include: - - More descriptive error strings printed inside xdrfile.c - - Fix for a segfault on malformed xtc files inside xdrfile_decompress_coord_float (https://github.com/mdtraj/mdtraj/pull/607, https://mailman-1.sys.kth.se/pipermail/gromacs.org_gmx-developers/2014-September/007942.html) - - Addition of read_xtc_nframes function in xdrfile_xtc.c - - Addition of read_trr_nframes function in xdrfile_trr.c - - Bugfix in do_trnheader to return the appropriate error code when reading magic, and properly check the value of the magic in xdrfile_trr.c - - Bugfix of float exception (divide by zero) in xdrfile.c, see https://github.com/SimTk/mdtraj/issues/616 - - Implemented efficient seeking pattern inspired by xdrlib2 (part of MDAnalysis) diff --git a/MDANSE/Extensions/xtc/src/xdr_seek.c b/MDANSE/Extensions/xtc/src/xdr_seek.c deleted file mode 100644 index 51768d8299..0000000000 --- a/MDANSE/Extensions/xtc/src/xdr_seek.c +++ /dev/null @@ -1,55 +0,0 @@ -/* 64 bit fileseek operations */ -#define _FILE_OFFSETS_BITS 64 -#include "xdr_seek.h" -#include - -/// copied from xtcfile.c (version 1.1.4) -struct XDRFILE -{ - FILE * fp; /**< pointer to standard C library file handle */ - void * /*this used to be (XDR*) */ xdr; /**< pointer to corresponding XDR handle */ - char mode; /**< r=read, w=write, a=append */ - int * buf1; /**< Buffer for internal use */ - int buf1size; /**< Current allocated length of buf1 */ - int * buf2; /**< Buffer for internal use */ - int buf2size; /**< Current allocated length of buf2 */ -}; -//// end of copied - -int64_t xdr_tell(XDRFILE *xd) -{ - FILE* fptr = xd->fp; - -#ifndef _WIN32 - // use posix 64 bit ftell version - return ftello(fptr); -#elif defined(_MSVC_VER) && !__INTEL_COMPILER - return _ftelli64(fptr); -#else - return ftell(fptr); -#endif -} - -int xdr_seek(XDRFILE *xd, int64_t pos, int whence) -{ - int result = 1; - FILE* fptr = xd->fp; - -#ifndef _WIN32 - // use posix 64 bit ftell version - result = fseeko(fptr, pos, whence) < 0 ? exdrNR : exdrOK; -#elif _MSVC_VER && !__INTEL_COMPILER - result = _fseeki64(fptr, pos, whence) < 0 ? exdrNR : exdrOK; -#else - result = fseek(fptr, pos, whence) < 0 ? exdrNR : exdrOK; -#endif - if (result != exdrOK) - return result; - - return exdrOK; -} - -int xdr_flush(XDRFILE* xdr) -{ - return fflush(xdr->fp); -} diff --git a/MDANSE/Extensions/xtc/src/xdrfile.c b/MDANSE/Extensions/xtc/src/xdrfile.c deleted file mode 100644 index d9c8cef62e..0000000000 --- a/MDANSE/Extensions/xtc/src/xdrfile.c +++ /dev/null @@ -1,2634 +0,0 @@ - /* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- - * - * $Id$ - * - * Copyright (c) 2009-2014, Erik Lindahl & David van der Spoel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Get HAVE_RPC_XDR_H, F77_FUNC from config.h if available */ -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#define _FILE_OFFSET_BITS 64 - -/* get fixed-width types if we are using ANSI C99 */ -#ifdef HAVE_STDINT_H -# include -#elif (defined HAVE_INTTYPES_H) -# include -#endif - - -#ifdef HAVE_RPC_XDR_H -# include -# include -#endif - -#include "xdrfile.h" - -/* Default FORTRAN name mangling is: lower case name, append underscore */ -#ifndef F77_FUNC -#define F77_FUNC(name,NAME) name ## _ -#endif - -char *exdr_message[exdrNR] = { - "OK", - "Header", - "String", - "Double", - "Integer", - "Float", - "Unsigned integer", - "Compressed 3D coordinate", - "Closing file", - "Magic number", - "Not enough memory", - "End of file", - "File not found" -}; - -/* - * Declare our own XDR routines statically if no libraries are present. - * Actual implementation is at the end of this file. - * - * We don't want the low-level XDR implementation as part of the Gromacs - * documentation, so skip it for doxygen too... - */ -#if (!defined HAVE_RPC_XDR_H && !defined DOXYGEN) - -enum xdr_op -{ - XDR_ENCODE = 0, - XDR_DECODE = 1, - XDR_FREE = 2 -}; - - -/* We need integer types that are guaranteed to be 4 bytes wide. - * If ANSI C99 headers were included they are already defined - * as int32_t and uint32_t. Check, and if not define them ourselves. - * Since it is just our workaround for missing ANSI C99 types, avoid adding - * it to the doxygen documentation. - */ -#if !(defined INT32_MAX || defined DOXYGEN) -# if (INT_MAX == 2147483647) -# define int32_t int -# define uint32_t unsigned int -# define INT32_MAX 2147483647 -# elif (LONG_MAX == 2147483647) -# define int32_t long -# define uint32_t unsigned long -# define INT32_MAX 2147483647L -# else -# error ERROR: No 32 bit wide integer type found! -# error Use system XDR libraries instead, or update xdrfile.c -# endif -#endif - -typedef struct XDR XDR; - -struct XDR -{ - enum xdr_op x_op; - struct xdr_ops - { - int (*x_getlong) (XDR *__xdrs, int32_t *__lp); - int (*x_putlong) (XDR *__xdrs, int32_t *__lp); - int (*x_getbytes) (XDR *__xdrs, char *__addr, unsigned int __len); - int (*x_putbytes) (XDR *__xdrs, char *__addr, unsigned int __len); - /* two next routines are not 64-bit IO safe - don't use! */ - unsigned int (*x_getpostn) (XDR *__xdrs); - int (*x_setpostn) (XDR *__xdrs, unsigned int __pos); - void (*x_destroy) (XDR *__xdrs); - } - *x_ops; - char *x_private; -}; - -static int xdr_char (XDR *xdrs, char *ip); -static int xdr_u_char (XDR *xdrs, unsigned char *ip); -static int xdr_short (XDR *xdrs, short *ip); -static int xdr_u_short (XDR *xdrs, unsigned short *ip); -static int xdr_int (XDR *xdrs, int *ip); -static int xdr_u_int (XDR *xdrs, unsigned int *ip); -static int xdr_float (XDR *xdrs, float *ip); -static int xdr_double (XDR *xdrs, double *ip); -static int xdr_string (XDR *xdrs, char **ip, unsigned int maxsize); -static int xdr_opaque (XDR *xdrs, char *cp, unsigned int cnt); -static void xdrstdio_create (XDR *xdrs, FILE *fp, enum xdr_op xop); - -#define xdr_getpos(xdrs) \ - (*(xdrs)->x_ops->x_getpostn)(xdrs) -#define xdr_setpos(xdrs, pos) \ - (*(xdrs)->x_ops->x_setpostn)(xdrs, pos) -#define xdr_destroy(xdrs) \ - do { \ - if ((xdrs)->x_ops->x_destroy) \ - (*(xdrs)->x_ops->x_destroy)(xdrs); \ - } while (0) -#endif /* end of our own XDR declarations */ - - - - - -/** Contents of the abstract XDRFILE data structure. - * - * @internal - * - * This structure is used to provide an XDR file interface that is - * virtual identical to the standard UNIX fopen/fread/fwrite/fclose. - */ -struct XDRFILE -{ - FILE * fp; /**< pointer to standard C library file handle */ - XDR * xdr; /**< pointer to corresponding XDR handle */ - char mode; /**< r=read, w=write, a=append */ - int * buf1; /**< Buffer for internal use */ - int buf1size; /**< Current allocated length of buf1 */ - int * buf2; /**< Buffer for internal use */ - int buf2size; /**< Current allocated length of buf2 */ -}; - - - - -/************************************************************* - * Implementation of higher-level routines to read/write * - * portable data based on the XDR standard. These should be * - * called from C - see further down for Fortran77 wrappers. * - *************************************************************/ - -XDRFILE * -xdrfile_open(const char *path, const char *mode) -{ - char newmode[5]; - enum xdr_op xdrmode; - XDRFILE *xfp; - - /* make sure XDR files are opened in binary mode... */ - if(*mode=='w' || *mode=='W') - { - sprintf(newmode,"wb+"); - xdrmode=XDR_ENCODE; - } else if(*mode == 'a' || *mode == 'A') - { - sprintf(newmode,"ab+"); - xdrmode = XDR_ENCODE; - } else if(*mode == 'r' || *mode == 'R') - { - sprintf(newmode,"rb"); - xdrmode = XDR_DECODE; - } else /* cannot determine mode */ - return NULL; - - if((xfp=(XDRFILE *)malloc(sizeof(XDRFILE)))==NULL) - return NULL; - if((xfp->fp=fopen(path,newmode))==NULL) - { - free(xfp); - return NULL; - } - if((xfp->xdr=(XDR *)malloc(sizeof(XDR)))==NULL) - { - fclose(xfp->fp); - free(xfp); - return NULL; - } - xfp->mode=*mode; - xdrstdio_create((XDR *)(xfp->xdr),xfp->fp,xdrmode); - xfp->buf1 = xfp->buf2 = NULL; - xfp->buf1size = xfp->buf2size = 0; - return xfp; -} - -int -xdrfile_close(XDRFILE *xfp) -{ - int ret=exdrCLOSE; - if(xfp) - { - /* flush and destroy XDR stream */ - if(xfp->xdr) - xdr_destroy((XDR *)(xfp->xdr)); - free(xfp->xdr); - /* close the file */ - ret=fclose(xfp->fp); - if(xfp->buf1size) - free(xfp->buf1); - if(xfp->buf2size) - free(xfp->buf2); - free(xfp); - } - return ret; /* return 0 if ok */ -} - - - -int -xdrfile_read_int(int *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - - return i; -} - -int -xdrfile_write_int(int *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - - -int -xdrfile_read_uint(unsigned int *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - - return i; -} - -int -xdrfile_write_uint(unsigned int *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - -int -xdrfile_read_char(char *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - - return i; -} - -int -xdrfile_write_char(char *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - - -int -xdrfile_read_uchar(unsigned char *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - - return i; -} - -int -xdrfile_write_uchar(unsigned char *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - -int -xdrfile_read_short(short *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - - return i; -} - -int -xdrfile_write_short(short *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - - -int -xdrfile_read_ushort(unsigned short *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - - return i; -} - -int -xdrfile_write_ushort(unsigned short *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - -int -xdrfile_read_float(float *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - -int -xdrfile_write_float(float *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - -int -xdrfile_read_double(double *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - -int -xdrfile_write_double(double *ptr, int ndata, XDRFILE* xfp) -{ - int i=0; - /* read write is encoded in the XDR struct */ - while(ixdr),ptr+i)) - i++; - return i; -} - -int -xdrfile_read_string(char *ptr, int maxlen, XDRFILE* xfp) -{ - int i; - if(xdr_string((XDR *)(xfp->xdr),&ptr,maxlen)) { - i=0; - while(ixdr),&ptr,len)) - return len; - else - return 0; -} - - -int -xdrfile_read_opaque(char *ptr, int cnt, XDRFILE* xfp) -{ - if(xdr_opaque((XDR *)(xfp->xdr),ptr,cnt)) - return cnt; - else - return 0; -} - - -int -xdrfile_write_opaque(char *ptr, int cnt, XDRFILE* xfp) -{ - if(xdr_opaque((XDR *)(xfp->xdr),ptr,cnt)) - return cnt; - else - return 0; -} - - -/* Internal support routines for reading/writing compressed coordinates - * sizeofint - calculate smallest number of bits necessary - * to represent a certain integer. - */ -static int -sizeofint(int size) { - unsigned int num = 1; - int num_of_bits = 0; - - while (size >= num && num_of_bits < 32) - { - num_of_bits++; - num <<= 1; - } - return num_of_bits; -} - - -/* - * sizeofints - calculate 'bitsize' of compressed ints - * - * given a number of small unsigned integers and the maximum value - * return the number of bits needed to read or write them with the - * routines encodeints/decodeints. You need this parameter when - * calling those routines. - * (However, in some cases we can just use the variable 'smallidx' - * which is the exact number of bits, and them we dont need to call - * this routine). - */ -static int -sizeofints(int num_of_ints, unsigned int sizes[]) -{ - int i, num; - unsigned int num_of_bytes, num_of_bits, bytes[32], bytecnt, tmp; - num_of_bytes = 1; - bytes[0] = 1; - num_of_bits = 0; - for (i=0; i < num_of_ints; i++) - { - tmp = 0; - for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) - { - tmp = bytes[bytecnt] * sizes[i] + tmp; - bytes[bytecnt] = tmp & 0xff; - tmp >>= 8; - } - while (tmp != 0) - { - bytes[bytecnt++] = tmp & 0xff; - tmp >>= 8; - } - num_of_bytes = bytecnt; - } - num = 1; - num_of_bytes--; - while (bytes[num_of_bytes] >= num) - { - num_of_bits++; - num *= 2; - } - return num_of_bits + num_of_bytes * 8; - -} - - -/* - * encodebits - encode num into buf using the specified number of bits - * - * This routines appends the value of num to the bits already present in - * the array buf. You need to give it the number of bits to use and you had - * better make sure that this number of bits is enough to hold the value. - * Num must also be positive. - */ -static void -encodebits(int buf[], int num_of_bits, int num) -{ - - unsigned int cnt, lastbyte; - int lastbits; - unsigned char * cbuf; - - cbuf = ((unsigned char *)buf) + 3 * sizeof(*buf); - cnt = (unsigned int) buf[0]; - lastbits = buf[1]; - lastbyte =(unsigned int) buf[2]; - while (num_of_bits >= 8) - { - lastbyte = (lastbyte << 8) | ((num >> (num_of_bits -8)) /* & 0xff*/); - cbuf[cnt++] = lastbyte >> lastbits; - num_of_bits -= 8; - } - if (num_of_bits > 0) - { - lastbyte = (lastbyte << num_of_bits) | num; - lastbits += num_of_bits; - if (lastbits >= 8) - { - lastbits -= 8; - cbuf[cnt++] = lastbyte >> lastbits; - } - } - buf[0] = cnt; - buf[1] = lastbits; - buf[2] = lastbyte; - if (lastbits>0) - { - cbuf[cnt] = lastbyte << (8 - lastbits); - } -} - -/* - * encodeints - encode a small set of small integers in compressed format - * - * this routine is used internally by xdr3dfcoord, to encode a set of - * small integers to the buffer for writing to a file. - * Multiplication with fixed (specified maximum) sizes is used to get - * to one big, multibyte integer. Allthough the routine could be - * modified to handle sizes bigger than 16777216, or more than just - * a few integers, this is not done because the gain in compression - * isn't worth the effort. Note that overflowing the multiplication - * or the byte buffer (32 bytes) is unchecked and whould cause bad results. - * THese things are checked in the calling routines, so make sure not - * to remove those checks... - */ - -static void -encodeints(int buf[], int num_of_ints, int num_of_bits, - unsigned int sizes[], unsigned int nums[]) -{ - - int i; - unsigned int bytes[32], num_of_bytes, bytecnt, tmp; - - tmp = nums[0]; - num_of_bytes = 0; - do - { - bytes[num_of_bytes++] = tmp & 0xff; - tmp >>= 8; - } while (tmp != 0); - - for (i = 1; i < num_of_ints; i++) - { - if (nums[i] >= sizes[i]) - { - fprintf(stderr,"(xdrfile error) major breakdown in encodeints - num %u doesn't " - "match size %u\n", nums[i], sizes[i]); - abort(); - } - /* use one step multiply */ - tmp = nums[i]; - for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++) - { - tmp = bytes[bytecnt] * sizes[i] + tmp; - bytes[bytecnt] = tmp & 0xff; - tmp >>= 8; - } - while (tmp != 0) - { - bytes[bytecnt++] = tmp & 0xff; - tmp >>= 8; - } - num_of_bytes = bytecnt; - } - if (num_of_bits >= num_of_bytes * 8) - { - for (i = 0; i < num_of_bytes; i++) - { - encodebits(buf, 8, bytes[i]); - } - encodebits(buf, num_of_bits - num_of_bytes * 8, 0); - } - else - { - for (i = 0; i < num_of_bytes-1; i++) - { - encodebits(buf, 8, bytes[i]); - } - encodebits(buf, num_of_bits- (num_of_bytes -1) * 8, bytes[i]); - } -} - - -/* - * decodebits - decode number from buf using specified number of bits - * - * extract the number of bits from the array buf and construct an integer - * from it. Return that value. - * - */ - -static int -decodebits(int buf[], int num_of_bits) -{ - - int cnt, num; - unsigned int lastbits, lastbyte; - unsigned char * cbuf; - int mask = (1 << num_of_bits) -1; - - cbuf = ((unsigned char *)buf) + 3 * sizeof(*buf); - cnt = buf[0]; - lastbits = (unsigned int) buf[1]; - lastbyte = (unsigned int) buf[2]; - - num = 0; - while (num_of_bits >= 8) - { - lastbyte = ( lastbyte << 8 ) | cbuf[cnt++]; - num |= (lastbyte >> lastbits) << (num_of_bits - 8); - num_of_bits -=8; - } - if (num_of_bits > 0) - { - if (lastbits < num_of_bits) - { - lastbits += 8; - lastbyte = (lastbyte << 8) | cbuf[cnt++]; - } - lastbits -= num_of_bits; - num |= (lastbyte >> lastbits) & ((1 << num_of_bits) -1); - } - num &= mask; - buf[0] = cnt; - buf[1] = lastbits; - buf[2] = lastbyte; - return num; -} - -/* - * decodeints - decode 'small' integers from the buf array - * - * this routine is the inverse from encodeints() and decodes the small integers - * written to buf by calculating the remainder and doing divisions with - * the given sizes[]. You need to specify the total number of bits to be - * used from buf in num_of_bits. - * - */ - -static void -decodeints(int buf[], int num_of_ints, int num_of_bits, - unsigned int sizes[], int nums[]) -{ - - int bytes[32]; - int i, j, num_of_bytes, p, num; - - bytes[1] = bytes[2] = bytes[3] = 0; - num_of_bytes = 0; - while (num_of_bits > 8) - { - bytes[num_of_bytes++] = decodebits(buf, 8); - num_of_bits -= 8; - } - if (num_of_bits > 0) - { - bytes[num_of_bytes++] = decodebits(buf, num_of_bits); - } - for (i = num_of_ints-1; i > 0; i--) - { - num = 0; - for (j = num_of_bytes-1; j >=0; j--) - { - num = (num << 8) | bytes[j]; - p = num / sizes[i]; - bytes[j] = p; - num = num - p * sizes[i]; - } - nums[i] = num; - } - nums[0] = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); -} - - -static const int magicints[] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 10, 12, 16, 20, 25, 32, 40, 50, 64, - 80, 101, 128, 161, 203, 256, 322, 406, 512, 645, 812, 1024, 1290, - 1625, 2048, 2580, 3250, 4096, 5060, 6501, 8192, 10321, 13003, - 16384, 20642, 26007, 32768, 41285, 52015, 65536,82570, 104031, - 131072, 165140, 208063, 262144, 330280, 416127, 524287, 660561, - 832255, 1048576, 1321122, 1664510, 2097152, 2642245, 3329021, - 4194304, 5284491, 6658042, 8388607, 10568983, 13316085, 16777216 -}; - -#define FIRSTIDX 9 -/* note that magicints[FIRSTIDX-1] == 0 */ -#define LASTIDX (sizeof(magicints) / sizeof(*magicints)) - -/* Compressed coordinate routines - modified from the original - * implementation by Frans v. Hoesel to make them threadsafe. - */ -int -xdrfile_decompress_coord_float(float *ptr, - int *size, - float *precision, - XDRFILE* xfp) -{ - int minint[3], maxint[3], *lip; - int smallidx, minidx, maxidx; - unsigned sizeint[3], sizesmall[3], bitsizeint[3], size3; - int k, *buf1, *buf2, lsize, flag; - int smallnum, smaller, larger, i, is_smaller, run; - float *lfp, inv_precision; - int tmp, *thiscoord, prevcoord[3]; - unsigned int bitsize; - const float* ptrstart = ptr; - - bitsizeint[0] = 0; - bitsizeint[1] = 0; - bitsizeint[2] = 0; - - if(xfp==NULL || ptr==NULL) { - fprintf(stderr, "(xdrfile error) Null pointer issue\n"); - return -1; - } - tmp=xdrfile_read_int(&lsize,1,xfp); - if(tmp==0) { - fprintf(stderr, "(xdrfile error) Size could not be read\n"); - return -1; /* return if we could not read size */ - } - if (*size < lsize) - { - fprintf(stderr, "(xdrfile error) Requested to decompress %d coords, file contains %d\n", - *size, lsize); - return -1; - } - *size = lsize; - size3 = *size * 3; - if(size3>xfp->buf1size) - { - if((xfp->buf1=(int *)malloc(sizeof(int)*size3))==NULL) - { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for decompressing coordinates.\n"); - return -1; - } - xfp->buf1size=size3; - xfp->buf2size=size3*1.2; - if((xfp->buf2=(int *)malloc(sizeof(int)*xfp->buf2size))==NULL) - { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for decompressing coordinates.\n"); - return -1; - } - } - /* Dont bother with compression for three atoms or less */ - if(*size<=9) - { - return xdrfile_read_float(ptr,size3,xfp)/3; - /* return number of coords, not floats */ - } - /* Compression-time if we got here. Read precision first */ - xdrfile_read_float(precision,1,xfp); - - /* avoid repeated pointer dereferencing. */ - buf1=xfp->buf1; - buf2=xfp->buf2; - /* buf2[0-2] are special and do not contain actual data */ - buf2[0] = buf2[1] = buf2[2] = 0; - xdrfile_read_int(minint,3,xfp); - xdrfile_read_int(maxint,3,xfp); - - sizeint[0] = maxint[0] - minint[0]+1; - sizeint[1] = maxint[1] - minint[1]+1; - sizeint[2] = maxint[2] - minint[2]+1; - - /* check if one of the sizes is to big to be multiplied */ - if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff) - { - bitsizeint[0] = sizeofint(sizeint[0]); - bitsizeint[1] = sizeofint(sizeint[1]); - bitsizeint[2] = sizeofint(sizeint[2]); - bitsize = 0; /* flag the use of large sizes */ - } - else - { - bitsize = sizeofints(3, sizeint); - } - - if (xdrfile_read_int(&smallidx,1,xfp) == 0) { - fprintf(stderr,"(xdrfile error) Undocumented error 1"); - return 0; /* not sure what has happened here or why we return... */ - } - tmp=smallidx+8; - maxidx = (LASTIDXtmp) ? FIRSTIDX : tmp; - smaller = magicints[tmp] / 2; - smallnum = magicints[smallidx] / 2; - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx] ; - larger = magicints[maxidx]; - - /* buf2[0] holds the length in bytes */ - - if (xdrfile_read_int(buf2,1,xfp) == 0) { - fprintf(stderr, "(xdrfile error) Undocumented error 2"); - return 0; - } - if (xdrfile_read_opaque((char *)&(buf2[3]),(unsigned int)buf2[0],xfp) == 0) { - fprintf(stderr, "(xdrfile error) Undocumented error 3"); - return 0; - } - buf2[0] = buf2[1] = buf2[2] = 0; - - lfp = ptr; - inv_precision = 1.0 / * precision; - run = 0; - i = 0; - lip = buf1; - while ( i < lsize ) - { - thiscoord = (int *)(lip) + i * 3; - - if (bitsize == 0) - { - thiscoord[0] = decodebits(buf2, bitsizeint[0]); - thiscoord[1] = decodebits(buf2, bitsizeint[1]); - thiscoord[2] = decodebits(buf2, bitsizeint[2]); - } - else - { - decodeints(buf2, 3, bitsize, sizeint, thiscoord); - } - - i++; - thiscoord[0] += minint[0]; - thiscoord[1] += minint[1]; - thiscoord[2] += minint[2]; - - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - - flag = decodebits(buf2, 1); - is_smaller = 0; - if (flag == 1) - { - run = decodebits(buf2, 5); - is_smaller = run % 3; - run -= is_smaller; - is_smaller--; - } - if ((lfp-ptrstart)+run > size3) - { - fprintf(stderr, "(xdrfile error) Buffer overrun during decompression.\n"); - return 0; - } - if (run > 0) - { - thiscoord += 3; - for (k = 0; k < run; k+=3) - { - decodeints(buf2, 3, smallidx, sizesmall, thiscoord); - i++; - thiscoord[0] += prevcoord[0] - smallnum; - thiscoord[1] += prevcoord[1] - smallnum; - thiscoord[2] += prevcoord[2] - smallnum; - if (k == 0) { - /* interchange first with second atom for better - * compression of water molecules - */ - tmp = thiscoord[0]; thiscoord[0] = prevcoord[0]; - prevcoord[0] = tmp; - tmp = thiscoord[1]; thiscoord[1] = prevcoord[1]; - prevcoord[1] = tmp; - tmp = thiscoord[2]; thiscoord[2] = prevcoord[2]; - prevcoord[2] = tmp; - *lfp++ = prevcoord[0] * inv_precision; - *lfp++ = prevcoord[1] * inv_precision; - *lfp++ = prevcoord[2] * inv_precision; - } else { - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - } - *lfp++ = thiscoord[0] * inv_precision; - *lfp++ = thiscoord[1] * inv_precision; - *lfp++ = thiscoord[2] * inv_precision; - } - } - else - { - *lfp++ = thiscoord[0] * inv_precision; - *lfp++ = thiscoord[1] * inv_precision; - *lfp++ = thiscoord[2] * inv_precision; - } - smallidx += is_smaller; - if (is_smaller < 0) - { - smallnum = smaller; - - if (smallidx > FIRSTIDX) - { - smaller = magicints[smallidx - 1] /2; - } - else - { - smaller = 0; - } - } - else if (is_smaller > 0) - { - smaller = smallnum; - smallnum = magicints[smallidx] / 2; - } - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx]; - if (sizesmall[0]==0 || sizesmall[1]==0 || sizesmall[2]==0) - { - fprintf(stderr, "(xdrfile error) Undefined error.\n"); - return 0; - } - } - return *size; -} - -int -xdrfile_compress_coord_float(float *ptr, - int size, - float precision, - XDRFILE* xfp) -{ - int minint[3], maxint[3], mindiff, *lip, diff; - int lint1, lint2, lint3, oldlint1, oldlint2, oldlint3, smallidx; - int minidx, maxidx; - unsigned sizeint[3], sizesmall[3], bitsizeint[3], size3, *luip; - int k, *buf1, *buf2; - int smallnum, smaller, larger, i, j, is_small, is_smaller, run, prevrun; - float *lfp, lf; - int tmp, tmpsum, *thiscoord, prevcoord[3]; - unsigned int tmpcoord[30]; - int errval=1; - unsigned int bitsize; - - if(xfp==NULL) - return -1; - size3=3*size; - - bitsizeint[0] = 0; - bitsizeint[1] = 0; - bitsizeint[2] = 0; - - if(size3>xfp->buf1size) - { - if((xfp->buf1=(int *)malloc(sizeof(int)*size3))==NULL) - { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for compressing coordinates.\n"); - return -1; - } - xfp->buf1size=size3; - xfp->buf2size=size3*1.2; - if((xfp->buf2=(int *)malloc(sizeof(int)*xfp->buf2size))==NULL) - { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for compressing coordinates.\n"); - return -1; - } - } - if(xdrfile_write_int(&size,1,xfp)==0) - return -1; /* return if we could not write size */ - /* Dont bother with compression for three atoms or less */ - if(size<=9) - { - return xdrfile_write_float(ptr,size3,xfp)/3; - /* return number of coords, not floats */ - } - /* Compression-time if we got here. Write precision first */ - if (precision <= 0) - precision = 1000; - xdrfile_write_float(&precision,1,xfp); - /* avoid repeated pointer dereferencing. */ - buf1=xfp->buf1; - buf2=xfp->buf2; - /* buf2[0-2] are special and do not contain actual data */ - buf2[0] = buf2[1] = buf2[2] = 0; - minint[0] = minint[1] = minint[2] = INT_MAX; - maxint[0] = maxint[1] = maxint[2] = INT_MIN; - prevrun = -1; - lfp = ptr; - lip = buf1; - mindiff = INT_MAX; - oldlint1 = oldlint2 = oldlint3 = 0; - while(lfp < ptr + size3 ) - { - /* find nearest integer */ - if (*lfp >= 0.0) - lf = *lfp * precision + 0.5; - else - lf = *lfp * precision - 0.5; - if (fabs(lf) > INT_MAX-2) - { - /* scaling would cause overflow */ - fprintf(stderr, "(xdrfile error) Internal overflow compressing coordinates.\n"); - errval=0; - } - lint1 = lf; - if (lint1 < minint[0]) minint[0] = lint1; - if (lint1 > maxint[0]) maxint[0] = lint1; - *lip++ = lint1; - lfp++; - if (*lfp >= 0.0) - lf = *lfp * precision + 0.5; - else - lf = *lfp * precision - 0.5; - if (fabs(lf) > INT_MAX-2) - { - /* scaling would cause overflow */ - fprintf(stderr, "(xdrfile error) Internal overflow compressing coordinates.\n"); - errval=0; - } - lint2 = lf; - if (lint2 < minint[1]) minint[1] = lint2; - if (lint2 > maxint[1]) maxint[1] = lint2; - *lip++ = lint2; - lfp++; - if (*lfp >= 0.0) - lf = *lfp * precision + 0.5; - else - lf = *lfp * precision - 0.5; - if (fabs(lf) > INT_MAX-2) - { - errval=0; - } - lint3 = lf; - if (lint3 < minint[2]) minint[2] = lint3; - if (lint3 > maxint[2]) maxint[2] = lint3; - *lip++ = lint3; - lfp++; - diff = abs(oldlint1-lint1)+abs(oldlint2-lint2)+abs(oldlint3-lint3); - if (diff < mindiff && lfp > ptr + 3) - mindiff = diff; - oldlint1 = lint1; - oldlint2 = lint2; - oldlint3 = lint3; - } - xdrfile_write_int(minint,3,xfp); - xdrfile_write_int(maxint,3,xfp); - - if ((float)maxint[0] - (float)minint[0] >= INT_MAX-2 || - (float)maxint[1] - (float)minint[1] >= INT_MAX-2 || - (float)maxint[2] - (float)minint[2] >= INT_MAX-2) { - /* turning value in unsigned by subtracting minint - * would cause overflow - */ - fprintf(stderr, "(xdrfile error) Internal overflow compressing coordinates.\n"); - errval=0; - } - sizeint[0] = maxint[0] - minint[0]+1; - sizeint[1] = maxint[1] - minint[1]+1; - sizeint[2] = maxint[2] - minint[2]+1; - - /* check if one of the sizes is to big to be multiplied */ - if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff) - { - bitsizeint[0] = sizeofint(sizeint[0]); - bitsizeint[1] = sizeofint(sizeint[1]); - bitsizeint[2] = sizeofint(sizeint[2]); - bitsize = 0; /* flag the use of large sizes */ - } - else - { - bitsize = sizeofints(3, sizeint); - } - lip = buf1; - luip = (unsigned int *) buf1; - smallidx = FIRSTIDX; - while (smallidx < LASTIDX && magicints[smallidx] < mindiff) - { - smallidx++; - } - xdrfile_write_int(&smallidx,1,xfp); - tmp=smallidx+8; - maxidx = (LASTIDXtmp) ? FIRSTIDX : tmp; - smaller = magicints[tmp] / 2; - smallnum = magicints[smallidx] / 2; - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx]; - larger = magicints[maxidx] / 2; - i = 0; - while (i < size) - { - is_small = 0; - thiscoord = (int *)(luip) + i * 3; - if (smallidx < maxidx && i >= 1 && - abs(thiscoord[0] - prevcoord[0]) < larger && - abs(thiscoord[1] - prevcoord[1]) < larger && - abs(thiscoord[2] - prevcoord[2]) < larger) { - is_smaller = 1; - } - else if (smallidx > minidx) - { - is_smaller = -1; - } - else - { - is_smaller = 0; - } - if (i + 1 < size) - { - if (abs(thiscoord[0] - thiscoord[3]) < smallnum && - abs(thiscoord[1] - thiscoord[4]) < smallnum && - abs(thiscoord[2] - thiscoord[5]) < smallnum) - { - /* interchange first with second atom for better - * compression of water molecules - */ - tmp = thiscoord[0]; thiscoord[0] = thiscoord[3]; - thiscoord[3] = tmp; - tmp = thiscoord[1]; thiscoord[1] = thiscoord[4]; - thiscoord[4] = tmp; - tmp = thiscoord[2]; thiscoord[2] = thiscoord[5]; - thiscoord[5] = tmp; - is_small = 1; - } - } - tmpcoord[0] = thiscoord[0] - minint[0]; - tmpcoord[1] = thiscoord[1] - minint[1]; - tmpcoord[2] = thiscoord[2] - minint[2]; - if (bitsize == 0) - { - encodebits(buf2, bitsizeint[0], tmpcoord[0]); - encodebits(buf2, bitsizeint[1], tmpcoord[1]); - encodebits(buf2, bitsizeint[2], tmpcoord[2]); - } - else - { - encodeints(buf2, 3, bitsize, sizeint, tmpcoord); - } - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - thiscoord = thiscoord + 3; - i++; - - run = 0; - if (is_small == 0 && is_smaller == -1) - is_smaller = 0; - while (is_small && run < 8*3) - { - tmpsum=0; - for(j=0;j<3;j++) - { - tmp=thiscoord[j] - prevcoord[j]; - tmpsum+=tmp*tmp; - } - if (is_smaller == -1 && tmpsum >= smaller * smaller) - { - is_smaller = 0; - } - - tmpcoord[run++] = thiscoord[0] - prevcoord[0] + smallnum; - tmpcoord[run++] = thiscoord[1] - prevcoord[1] + smallnum; - tmpcoord[run++] = thiscoord[2] - prevcoord[2] + smallnum; - - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - - i++; - thiscoord = thiscoord + 3; - is_small = 0; - if (i < size && - abs(thiscoord[0] - prevcoord[0]) < smallnum && - abs(thiscoord[1] - prevcoord[1]) < smallnum && - abs(thiscoord[2] - prevcoord[2]) < smallnum) - { - is_small = 1; - } - } - if (run != prevrun || is_smaller != 0) - { - prevrun = run; - encodebits(buf2, 1, 1); /* flag the change in run-length */ - encodebits(buf2, 5, run+is_smaller+1); - } - else - { - encodebits(buf2, 1, 0); /* flag the fact that runlength did not change */ - } - for (k=0; k < run; k+=3) - { - encodeints(buf2, 3, smallidx, sizesmall, &tmpcoord[k]); - } - if (is_smaller != 0) - { - smallidx += is_smaller; - if (is_smaller < 0) - { - smallnum = smaller; - smaller = magicints[smallidx-1] / 2; - } - else - { - smaller = smallnum; - smallnum = magicints[smallidx] / 2; - } - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx]; - } - } - if (buf2[1] != 0) buf2[0]++; - xdrfile_write_int(buf2,1,xfp); /* buf2[0] holds the length in bytes */ - tmp=xdrfile_write_opaque((char *)&(buf2[3]),(unsigned int)buf2[0],xfp); - if(tmp==(unsigned int)buf2[0]) - return size; - else - return -1; -} - - -int -xdrfile_decompress_coord_double(double *ptr, - int *size, - double *precision, - XDRFILE* xfp) -{ - int minint[3], maxint[3], *lip; - int smallidx, minidx, maxidx; - unsigned sizeint[3], sizesmall[3], bitsizeint[3], size3; - int k, *buf1, *buf2, lsize, flag; - int smallnum, smaller, larger, i, is_smaller, run; - double *lfp, inv_precision; - float float_prec, tmpdata[30]; - int tmp, *thiscoord, prevcoord[3]; - unsigned int bitsize; - - bitsizeint[0] = 0; - bitsizeint[1] = 0; - bitsizeint[2] = 0; - - if(xfp==NULL || ptr==NULL) - return -1; - tmp=xdrfile_read_int(&lsize,1,xfp); - if(tmp==0) - return -1; /* return if we could not read size */ - if (*size < lsize) - { - fprintf(stderr, "(xdrfile error) Requested to decompress %d coords, file contains %d\n", - *size, lsize); - return -1; - } - *size = lsize; - size3 = *size * 3; - if(size3>xfp->buf1size) - { - if((xfp->buf1=(int *)malloc(sizeof(int)*size3))==NULL) - { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for decompression coordinates.\n"); - return -1; - } - xfp->buf1size=size3; - xfp->buf2size=size3*1.2; - if((xfp->buf2=(int *)malloc(sizeof(int)*xfp->buf2size))==NULL) - { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for decompressing coordinates.\n"); - return -1; - } - } - /* Dont bother with compression for three atoms or less */ - if(*size<=9) - { - tmp=xdrfile_read_float(tmpdata,size3,xfp); - for(i=0;i<9*3;i++) - ptr[i]=tmpdata[i]; - return tmp/3; - /* return number of coords, not floats */ - } - /* Compression-time if we got here. Read precision first */ - xdrfile_read_float(&float_prec,1,xfp); - *precision=float_prec; - /* avoid repeated pointer dereferencing. */ - buf1=xfp->buf1; - buf2=xfp->buf2; - /* buf2[0-2] are special and do not contain actual data */ - buf2[0] = buf2[1] = buf2[2] = 0; - xdrfile_read_int(minint,3,xfp); - xdrfile_read_int(maxint,3,xfp); - - sizeint[0] = maxint[0] - minint[0]+1; - sizeint[1] = maxint[1] - minint[1]+1; - sizeint[2] = maxint[2] - minint[2]+1; - - /* check if one of the sizes is to big to be multiplied */ - if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff) - { - bitsizeint[0] = sizeofint(sizeint[0]); - bitsizeint[1] = sizeofint(sizeint[1]); - bitsizeint[2] = sizeofint(sizeint[2]); - bitsize = 0; /* flag the use of large sizes */ - } - else - { - bitsize = sizeofints(3, sizeint); - } - - if (xdrfile_read_int(&smallidx,1,xfp) == 0) - return 0; - tmp=smallidx+8; - maxidx = (LASTIDXtmp) ? FIRSTIDX : tmp; - smaller = magicints[tmp] / 2; - smallnum = magicints[smallidx] / 2; - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx] ; - larger = magicints[maxidx]; - - /* buf2[0] holds the length in bytes */ - - if (xdrfile_read_int(buf2,1,xfp) == 0) - return 0; - if (xdrfile_read_opaque((char *)&(buf2[3]),(unsigned int)buf2[0],xfp) == 0) - return 0; - buf2[0] = buf2[1] = buf2[2] = 0; - - lfp = ptr; - inv_precision = 1.0 / * precision; - run = 0; - i = 0; - lip = buf1; - while ( i < lsize ) - { - thiscoord = (int *)(lip) + i * 3; - - if (bitsize == 0) - { - thiscoord[0] = decodebits(buf2, bitsizeint[0]); - thiscoord[1] = decodebits(buf2, bitsizeint[1]); - thiscoord[2] = decodebits(buf2, bitsizeint[2]); - } else { - decodeints(buf2, 3, bitsize, sizeint, thiscoord); - } - - i++; - thiscoord[0] += minint[0]; - thiscoord[1] += minint[1]; - thiscoord[2] += minint[2]; - - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - - flag = decodebits(buf2, 1); - is_smaller = 0; - if (flag == 1) - { - run = decodebits(buf2, 5); - is_smaller = run % 3; - run -= is_smaller; - is_smaller--; - } - if (run > 0) - { - thiscoord += 3; - for (k = 0; k < run; k+=3) - { - decodeints(buf2, 3, smallidx, sizesmall, thiscoord); - i++; - thiscoord[0] += prevcoord[0] - smallnum; - thiscoord[1] += prevcoord[1] - smallnum; - thiscoord[2] += prevcoord[2] - smallnum; - if (k == 0) - { - /* interchange first with second atom for better - * compression of water molecules - */ - tmp = thiscoord[0]; thiscoord[0] = prevcoord[0]; - prevcoord[0] = tmp; - tmp = thiscoord[1]; thiscoord[1] = prevcoord[1]; - prevcoord[1] = tmp; - tmp = thiscoord[2]; thiscoord[2] = prevcoord[2]; - prevcoord[2] = tmp; - *lfp++ = prevcoord[0] * inv_precision; - *lfp++ = prevcoord[1] * inv_precision; - *lfp++ = prevcoord[2] * inv_precision; - } - else - { - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - } - *lfp++ = thiscoord[0] * inv_precision; - *lfp++ = thiscoord[1] * inv_precision; - *lfp++ = thiscoord[2] * inv_precision; - } - } else { - *lfp++ = thiscoord[0] * inv_precision; - *lfp++ = thiscoord[1] * inv_precision; - *lfp++ = thiscoord[2] * inv_precision; - } - smallidx += is_smaller; - if (is_smaller < 0) { - smallnum = smaller; - if (smallidx > FIRSTIDX) { - smaller = magicints[smallidx - 1] /2; - } else { - smaller = 0; - } - } else if (is_smaller > 0) { - smaller = smallnum; - smallnum = magicints[smallidx] / 2; - } - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx] ; - } - return *size; -} - -int -xdrfile_compress_coord_double(double *ptr, - int size, - double precision, - XDRFILE* xfp) -{ - int minint[3], maxint[3], mindiff, *lip, diff; - int lint1, lint2, lint3, oldlint1, oldlint2, oldlint3, smallidx; - int minidx, maxidx; - unsigned sizeint[3], sizesmall[3], bitsizeint[3], size3, *luip; - int k, *buf1, *buf2; - int smallnum, smaller, larger, i, j, is_small, is_smaller, run, prevrun; - double *lfp; - float float_prec, lf,tmpdata[30]; - int tmp, tmpsum, *thiscoord, prevcoord[3]; - unsigned int tmpcoord[30]; - int errval=1; - unsigned int bitsize; - - bitsizeint[0] = 0; - bitsizeint[1] = 0; - bitsizeint[2] = 0; - - if(xfp==NULL) - return -1; - size3=3*size; - if(size3>xfp->buf1size) { - if((xfp->buf1=(int *)malloc(sizeof(int)*size3))==NULL) { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for compressing coordinates.\n"); - return -1; - } - xfp->buf1size=size3; - xfp->buf2size=size3*1.2; - if((xfp->buf2=(int *)malloc(sizeof(int)*xfp->buf2size))==NULL) { - fprintf(stderr, "(xdrfile error) Cannot allocate memory for compressing coordinates.\n"); - return -1; - } - } - if(xdrfile_write_int(&size,1,xfp)==0) - return -1; /* return if we could not write size */ - /* Dont bother with compression for three atoms or less */ - if(size<=9) { - for(i=0;i<9*3;i++) - tmpdata[i]=ptr[i]; - return xdrfile_write_float(tmpdata,size3,xfp)/3; - /* return number of coords, not floats */ - } - /* Compression-time if we got here. Write precision first */ - if (precision <= 0) - precision = 1000; - float_prec=precision; - xdrfile_write_float(&float_prec,1,xfp); - /* avoid repeated pointer dereferencing. */ - buf1=xfp->buf1; - buf2=xfp->buf2; - /* buf2[0-2] are special and do not contain actual data */ - buf2[0] = buf2[1] = buf2[2] = 0; - minint[0] = minint[1] = minint[2] = INT_MAX; - maxint[0] = maxint[1] = maxint[2] = INT_MIN; - prevrun = -1; - lfp = ptr; - lip = buf1; - mindiff = INT_MAX; - oldlint1 = oldlint2 = oldlint3 = 0; - while(lfp < ptr + size3 ) { - /* find nearest integer */ - if (*lfp >= 0.0) - lf = (float)*lfp * float_prec + 0.5; - else - lf = (float)*lfp * float_prec - 0.5; - if (fabs(lf) > INT_MAX-2) { - /* scaling would cause overflow */ - fprintf(stderr, "(xdrfile error) Internal overflow compressing coordinates.\n"); - errval=0; - } - lint1 = lf; - if (lint1 < minint[0]) minint[0] = lint1; - if (lint1 > maxint[0]) maxint[0] = lint1; - *lip++ = lint1; - lfp++; - if (*lfp >= 0.0) - lf = (float)*lfp * float_prec + 0.5; - else - lf = (float)*lfp * float_prec - 0.5; - if (fabs(lf) > INT_MAX-2) { - /* scaling would cause overflow */ - fprintf(stderr, "(xdrfile error) Internal overflow compressing coordinates.\n"); - errval=0; - } - lint2 = lf; - if (lint2 < minint[1]) minint[1] = lint2; - if (lint2 > maxint[1]) maxint[1] = lint2; - *lip++ = lint2; - lfp++; - if (*lfp >= 0.0) - lf = (float)*lfp * float_prec + 0.5; - else - lf = (float)*lfp * float_prec - 0.5; - if (fabs(lf) > INT_MAX-2) { - errval=0; - } - lint3 = lf; - if (lint3 < minint[2]) minint[2] = lint3; - if (lint3 > maxint[2]) maxint[2] = lint3; - *lip++ = lint3; - lfp++; - diff = abs(oldlint1-lint1)+abs(oldlint2-lint2)+abs(oldlint3-lint3); - if (diff < mindiff && lfp > ptr + 3) - mindiff = diff; - oldlint1 = lint1; - oldlint2 = lint2; - oldlint3 = lint3; - } - xdrfile_write_int(minint,3,xfp); - xdrfile_write_int(maxint,3,xfp); - - if ((float)maxint[0] - (float)minint[0] >= INT_MAX-2 || - (float)maxint[1] - (float)minint[1] >= INT_MAX-2 || - (float)maxint[2] - (float)minint[2] >= INT_MAX-2) { - /* turning value in unsigned by subtracting minint - * would cause overflow - */ - fprintf(stderr, "(xdrfile error) Internal overflow compressing coordinates.\n"); - errval=0; - } - sizeint[0] = maxint[0] - minint[0]+1; - sizeint[1] = maxint[1] - minint[1]+1; - sizeint[2] = maxint[2] - minint[2]+1; - - /* check if one of the sizes is to big to be multiplied */ - if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff) { - bitsizeint[0] = sizeofint(sizeint[0]); - bitsizeint[1] = sizeofint(sizeint[1]); - bitsizeint[2] = sizeofint(sizeint[2]); - bitsize = 0; /* flag the use of large sizes */ - } else { - bitsize = sizeofints(3, sizeint); - } - lip = buf1; - luip = (unsigned int *) buf1; - smallidx = FIRSTIDX; - while (smallidx < LASTIDX && magicints[smallidx] < mindiff) { - smallidx++; - } - xdrfile_write_int(&smallidx,1,xfp); - tmp=smallidx+8; - maxidx = (LASTIDXtmp) ? FIRSTIDX : tmp; - smaller = magicints[tmp] / 2; - smallnum = magicints[smallidx] / 2; - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx]; - larger = magicints[maxidx] / 2; - i = 0; - while (i < size) { - is_small = 0; - thiscoord = (int *)(luip) + i * 3; - if (smallidx < maxidx && i >= 1 && - abs(thiscoord[0] - prevcoord[0]) < larger && - abs(thiscoord[1] - prevcoord[1]) < larger && - abs(thiscoord[2] - prevcoord[2]) < larger) { - is_smaller = 1; - } else if (smallidx > minidx) { - is_smaller = -1; - } else { - is_smaller = 0; - } - if (i + 1 < size) { - if (abs(thiscoord[0] - thiscoord[3]) < smallnum && - abs(thiscoord[1] - thiscoord[4]) < smallnum && - abs(thiscoord[2] - thiscoord[5]) < smallnum) { - /* interchange first with second atom for better - * compression of water molecules - */ - tmp = thiscoord[0]; thiscoord[0] = thiscoord[3]; - thiscoord[3] = tmp; - tmp = thiscoord[1]; thiscoord[1] = thiscoord[4]; - thiscoord[4] = tmp; - tmp = thiscoord[2]; thiscoord[2] = thiscoord[5]; - thiscoord[5] = tmp; - is_small = 1; - } - } - tmpcoord[0] = thiscoord[0] - minint[0]; - tmpcoord[1] = thiscoord[1] - minint[1]; - tmpcoord[2] = thiscoord[2] - minint[2]; - if (bitsize == 0) { - encodebits(buf2, bitsizeint[0], tmpcoord[0]); - encodebits(buf2, bitsizeint[1], tmpcoord[1]); - encodebits(buf2, bitsizeint[2], tmpcoord[2]); - } else { - encodeints(buf2, 3, bitsize, sizeint, tmpcoord); - } - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - thiscoord = thiscoord + 3; - i++; - - run = 0; - if (is_small == 0 && is_smaller == -1) - is_smaller = 0; - while (is_small && run < 8*3) { - tmpsum=0; - for(j=0;j<3;j++) { - tmp=thiscoord[j] - prevcoord[j]; - tmpsum+=tmp*tmp; - } - if (is_smaller == -1 && tmpsum >= smaller * smaller) { - is_smaller = 0; - } - - tmpcoord[run++] = thiscoord[0] - prevcoord[0] + smallnum; - tmpcoord[run++] = thiscoord[1] - prevcoord[1] + smallnum; - tmpcoord[run++] = thiscoord[2] - prevcoord[2] + smallnum; - - prevcoord[0] = thiscoord[0]; - prevcoord[1] = thiscoord[1]; - prevcoord[2] = thiscoord[2]; - - i++; - thiscoord = thiscoord + 3; - is_small = 0; - if (i < size && - abs(thiscoord[0] - prevcoord[0]) < smallnum && - abs(thiscoord[1] - prevcoord[1]) < smallnum && - abs(thiscoord[2] - prevcoord[2]) < smallnum) { - is_small = 1; - } - } - if (run != prevrun || is_smaller != 0) { - prevrun = run; - encodebits(buf2, 1, 1); /* flag the change in run-length */ - encodebits(buf2, 5, run+is_smaller+1); - } else { - encodebits(buf2, 1, 0); /* flag the fact that runlength did not change */ - } - for (k=0; k < run; k+=3) { - encodeints(buf2, 3, smallidx, sizesmall, &tmpcoord[k]); - } - if (is_smaller != 0) { - smallidx += is_smaller; - if (is_smaller < 0) { - smallnum = smaller; - smaller = magicints[smallidx-1] / 2; - } else { - smaller = smallnum; - smallnum = magicints[smallidx] / 2; - } - sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx]; - } - } - if (buf2[1] != 0) buf2[0]++; - xdrfile_write_int(buf2,1,xfp); /* buf2[0] holds the length in bytes */ - tmp=xdrfile_write_opaque((char *)&(buf2[3]),(unsigned int)buf2[0],xfp); - if(tmp==(unsigned int)buf2[0]) - return size; - else - return -1; -} - - -/* Dont try do document Fortran interface, since - * Doxygen barfs at the F77_FUNC macro - */ -#ifndef DOXYGEN - -/************************************************************* - * Fortran77 interface for reading/writing portable data * - * The routine are not threadsafe when called from Fortran * - * (as they are when called from C) unless you compile with * - * this file with posix thread support. * - * Note that these are not multithread-safe. * - *************************************************************/ -#define MAX_FORTRAN_XDR 1024 -static XDRFILE *f77xdr[MAX_FORTRAN_XDR]; /* array of file handles */ -static int f77init = 1; /* zero array first time */ - -/* internal to this file: C<-->Fortran string conversion */ -static int ftocstr(char *dest, int dest_len, char *src, int src_len); -static int ctofstr(char *dest, int dest_len, char *src); - - -void -F77_FUNC(xdropen,XDROPEN)(int *fid, char *filename, char *mode, - int fn_len, int mode_len) -{ - char cfilename[512]; - char cmode[5]; - int i; - - /* zero array at first invocation */ - if(f77init) { - for(i=0;i= src && *p == ' ' ); - srclen = p - src + 1; - destlen--; - dest[0] = 0; - if (srclen > destlen) - return 1; - while (srclen--) - (*dest++ = *src++); - *dest = '\0'; - return 0; -} - - -static int ctofstr(char *dest, int destlen, char *src) -{ - while (destlen && *src) { - *dest++ = *src++; - destlen--; - } - while (destlen--) - *dest++ = ' '; - return 0; -} - - -void -F77_FUNC(xdrrstring,XDRRSTRING)(int *fid, char *str, int *ret, int len) -{ - char *cstr; - - if((cstr=(char*)malloc((len+1)*sizeof(char)))==NULL) { - *ret = 0; - return; - } - if (ftocstr(cstr, len+1, str, len)) { - *ret = 0; - free(cstr); - return; - } - - *ret = xdrfile_read_string(cstr, len+1,f77xdr[*fid]); - ctofstr( str, len , cstr); - free(cstr); -} - -void -F77_FUNC(xdrwstring,XDRWSTRING)(int *fid, char *str, int *ret, int len) -{ - char *cstr; - - if((cstr=(char*)malloc((len+1)*sizeof(char)))==NULL) { - *ret = 0; - return; - } - if (ftocstr(cstr, len+1, str, len)) { - *ret = 0; - free(cstr); - return; - } - - *ret = xdrfile_write_string(cstr, f77xdr[*fid]); - ctofstr( str, len , cstr); - free(cstr); -} - -void -F77_FUNC(xdrropaque,XDRROPAQUE)(int *fid, char *data, int *ndata, int *ret) -{ - *ret = xdrfile_read_opaque(data,*ndata,f77xdr[*fid]); -} - -void -F77_FUNC(xdrwopaque,XDRWOPAQUE)(int *fid, char *data, int *ndata, int *ret) -{ - *ret = xdrfile_write_opaque(data,*ndata,f77xdr[*fid]); -} - - -/* Write single-precision compressed 3d coordinates */ -void -F77_FUNC(xdrccs,XDRCCS)(int *fid, float *data, int *ncoord, - float *precision, int *ret) -{ - *ret = xdrfile_compress_coord_float(data,*ncoord,*precision,f77xdr[*fid]); -} - - -/* Read single-precision compressed 3d coordinates */ -void -F77_FUNC(xdrdcs,XDRDCS)(int *fid, float *data, int *ncoord, - float *precision, int *ret) -{ - *ret = xdrfile_decompress_coord_float(data,ncoord,precision,f77xdr[*fid]); -} - - -/* Write compressed 3d coordinates from double precision data */ -void -F77_FUNC(xdrccd,XDRCCD)(int *fid, double *data, int *ncoord, - double *precision, int *ret) -{ - *ret = xdrfile_compress_coord_double(data,*ncoord,*precision,f77xdr[*fid]); -} - -/* Read compressed 3d coordinates into double precision data */ -void -F77_FUNC(xddcd,XDRDCD)(int *fid, double *data, int *ncoord, - double *precision, int *ret) -{ - *ret = xdrfile_decompress_coord_double(data,ncoord,precision,f77xdr[*fid]); -} - - - - - - - -#endif /* DOXYGEN */ - -/************************************************************* - * End of higher-level routines - dont change things below! * - *************************************************************/ - - - - - - - - - - - - - - - - - - - - -/************************************************************* - * The rest of this file contains our own implementation * - * of the XDR calls in case you are compiling without them. * - * You do NOT want to change things here since it would make * - * things incompatible with the standard RPC/XDR routines. * - *************************************************************/ -#ifndef HAVE_RPC_XDR_H - -/* - * What follows is a modified version of the Sun XDR code. For reference - * we include their copyright and license: - * - * Sun RPC is a product of Sun Microsystems, Inc. and is provided for - * unrestricted use provided that this legend is included on all tape - * media and as a part of the software program in whole or part. Users - * may copy or modify Sun RPC without charge, but are not authorized - * to license or distribute it to anyone else except as part of a product or - * program developed by the user. - * - * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE - * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. - * - * Sun RPC is provided with no support and without any obligation on the - * part of Sun Microsystems, Inc. to assist in its use, correction, - * modification or enhancement. - * - * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE - * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC - * OR ANY PART THEREOF. - * - * In no event will Sun Microsystems, Inc. be liable for any lost revenue - * or profits or other special, indirect and consequential damages, even if - * Sun has been advised of the possibility of such damages. - * - * Sun Microsystems, Inc. - * 2550 Garcia Avenue - * Mountain View, California 94043 - */ - -/* INT_MAX is defined in limits.h according to ANSI C */ -#if (INT_MAX > 2147483647) -# error Error: Cannot use builtin XDR support when size of int -# error is larger than 4 bytes. Use your system XDR libraries -# error instead, or modify the source code in xdrfile.c -#endif /* Check for 4 byte int type */ - - - - - -typedef int (*xdrproc_t) (XDR *, void *,...); - -#define xdr_getlong(xdrs, longp) \ - (*(xdrs)->x_ops->x_getlong)(xdrs, longp) -#define xdr_putlong(xdrs, longp) \ - (*(xdrs)->x_ops->x_putlong)(xdrs, longp) -#define xdr_getbytes(xdrs, addr, len) \ - (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len) -#define xdr_putbytes(xdrs, addr, len) \ - (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len) - -#define BYTES_PER_XDR_UNIT 4 -static char xdr_zero[BYTES_PER_XDR_UNIT] = {0, 0, 0, 0}; - -static int32_t -xdr_swapbytes(int32_t x) -{ - int32_t y,i; - char *px=(char *)&x; - char *py=(char *)&y; - - for(i=0;i<4;i++) - py[i]=px[3-i]; - - return y; -} - -static int32_t -xdr_htonl(int32_t x) -{ - int s=0x1234; - if( *((char *)&s)==(char)0x34) { - /* smallendian,swap bytes */ - return xdr_swapbytes(x); - } else { - /* bigendian, do nothing */ - return x; - } -} - -static int32_t -xdr_ntohl(int x) -{ - int s=0x1234; - if( *((char *)&s)==(char)0x34) { - /* smallendian, swap bytes */ - return xdr_swapbytes(x); - } else { - /* bigendian, do nothing */ - return x; - } -} - -static int -xdr_int (XDR *xdrs, int *ip) -{ - int32_t i32; - - switch (xdrs->x_op) - { - case XDR_ENCODE: - i32 = (int32_t) *ip; - return xdr_putlong (xdrs, &i32); - - case XDR_DECODE: - if (!xdr_getlong (xdrs, &i32)) - { - return 0; - } - *ip = (int) i32; - case XDR_FREE: - return 1; - } - return 0; -} - -static int -xdr_u_int (XDR *xdrs, unsigned int *up) -{ - uint32_t ui32; - - switch (xdrs->x_op) - { - case XDR_ENCODE: - ui32 = (uint32_t) * up; - return xdr_putlong (xdrs, (int32_t *)&ui32); - - case XDR_DECODE: - if (!xdr_getlong (xdrs, (int32_t *)&ui32)) - { - return 0; - } - *up = (uint32_t) ui32; - case XDR_FREE: - return 1; - } - return 0; -} - -static int -xdr_short (XDR *xdrs, short *sp) -{ - int32_t i32; - - switch (xdrs->x_op) - { - case XDR_ENCODE: - i32 = (int32_t) *sp; - return xdr_putlong (xdrs, &i32); - - case XDR_DECODE: - if (!xdr_getlong (xdrs, &i32)) - { - return 0; - } - *sp = (short) i32; - return 1; - - case XDR_FREE: - return 1; - } - return 0; -} - -static int -xdr_u_short (XDR *xdrs, unsigned short *sp) -{ - uint32_t ui32; - - switch (xdrs->x_op) - { - case XDR_ENCODE: - ui32 = (uint32_t) *sp; - return xdr_putlong (xdrs, (int32_t *)&ui32); - - case XDR_DECODE: - if (!xdr_getlong (xdrs, (int32_t *)&ui32)) - { - return 0; - } - *sp = (unsigned short) ui32; - return 1; - - case XDR_FREE: - return 1; - } - return 0; -} - -static int -xdr_char (XDR *xdrs, char *cp) -{ - int i; - - i = (*cp); - if (!xdr_int (xdrs, &i)) - { - return 0; - } - *cp = i; - return 1; -} - -static int -xdr_u_char (XDR *xdrs, unsigned char *cp) -{ - unsigned int u; - - u = (*cp); - if (!xdr_u_int (xdrs, &u)) - { - return 0; - } - *cp = u; - return 1; -} - -/* - * XDR opaque data - * Allows the specification of a fixed size sequence of opaque bytes. - * cp points to the opaque object and cnt gives the byte length. - */ -static int -xdr_opaque (XDR *xdrs, char *cp, unsigned int cnt) -{ - unsigned int rndup; - static char crud[BYTES_PER_XDR_UNIT]; - - /* - * if no data we are done - */ - if (cnt == 0) - return 1; - - /* - * round byte count to full xdr units - */ - rndup = cnt % BYTES_PER_XDR_UNIT; - if (rndup > 0) - rndup = BYTES_PER_XDR_UNIT - rndup; - - switch (xdrs->x_op) - { - case XDR_DECODE: - if (!xdr_getbytes (xdrs, cp, cnt)) - { - return 0; - } - if (rndup == 0) - return 1; - return xdr_getbytes (xdrs, (char *)crud, rndup); - - case XDR_ENCODE: - if (!xdr_putbytes (xdrs, cp, cnt)) - { - return 0; - } - if (rndup == 0) - return 1; - return xdr_putbytes (xdrs, xdr_zero, rndup); - - case XDR_FREE: - return 1; - } -#undef BYTES_PER_XDR_UNIT - return 0; -} - - -/* - * XDR null terminated ASCII strings - */ -static int -xdr_string (XDR *xdrs, char **cpp, unsigned int maxsize) -{ - char *sp = *cpp; /* sp is the actual string pointer */ - unsigned int size; - unsigned int nodesize; - - /* - * first deal with the length since xdr strings are counted-strings - */ - switch (xdrs->x_op) - { - case XDR_FREE: - if (sp == NULL) - { - return 1; /* already free */ - } - /* fall through... */ - case XDR_ENCODE: - if (sp == NULL) - return 0; - size = strlen (sp); - break; - case XDR_DECODE: - break; - } - if (!xdr_u_int (xdrs, &size)) - { - return 0; - } - if (size > maxsize) - { - return 0; - } - nodesize = size + 1; - - /* - * now deal with the actual bytes - */ - switch (xdrs->x_op) - { - case XDR_DECODE: - if (nodesize == 0) - { - return 1; - } - if (sp == NULL) - *cpp = sp = (char *) malloc (nodesize); - if (sp == NULL) - { - (void) fputs ("xdr_string: out of memory\n", stderr); - return 0; - } - sp[size] = 0; - /* fall into ... */ - - case XDR_ENCODE: - return xdr_opaque (xdrs, sp, size); - - case XDR_FREE: - free (sp); - *cpp = NULL; - return 1; - } - return 0; -} - - - -/* Floating-point stuff */ - -static int -xdr_float(XDR *xdrs, float *fp) -{ - switch (xdrs->x_op) { - - case XDR_ENCODE: - if (sizeof(float) == sizeof(int32_t)) - return (xdr_putlong(xdrs, (int32_t *)fp)); - else if (sizeof(float) == sizeof(int)) { - int32_t tmp = *(int *)fp; - return (xdr_putlong(xdrs, &tmp)); - } - break; - - case XDR_DECODE: - if (sizeof(float) == sizeof(int32_t)) - return (xdr_getlong(xdrs, (int32_t *)fp)); - else if (sizeof(float) == sizeof(int)) { - int32_t tmp; - if (xdr_getlong(xdrs, &tmp)) { - *(int *)fp = tmp; - return (1); - } - } - break; - - case XDR_FREE: - return (1); - } - return (0); -} - - -static int -xdr_double(XDR *xdrs, double *dp) -{ - /* Gromacs detects floating-point stuff at compile time, which is faster */ -#ifdef GROMACS -# ifndef FLOAT_FORMAT_IEEE754 -# error non-IEEE floating point system, or you defined GROMACS yourself... -# endif - int LSW; -# ifdef IEEE754_BIG_ENDIAN_WORD_ORDER - int LSW=1; -# else - int LSW=0; -# endif /* Big endian word order */ -#else - /* Outside Gromacs we rely on dynamic detection of FP order. */ - int LSW; /* Least significant fp word */ - - double x=0.987654321; /* Just a number */ - unsigned char ix = *((char *)&x); - - /* Possible representations in IEEE double precision: - * (S=small endian, B=big endian) - * - * Byte order, Word order, Hex - * S S b8 56 0e 3c dd 9a ef 3f - * B S 3c 0e 56 b8 3f ef 9a dd - * S B dd 9a ef 3f b8 56 0e 3c - * B B 3f ef 9a dd 3c 0e 56 b8 - */ - if(ix==0xdd || ix==0x3f) - LSW=1; /* Big endian word order */ - else if(ix==0xb8 || ix==0x3c) - LSW=0; /* Small endian word order */ - else { /* Catch strange errors */ - fprintf(stderr,"Cannot detect floating-point word order.\n" - "Do you have a non-IEEE system?\n" - "Use system XDR libraries or fix xdr_double().\n"); - abort(); - } -#endif /* end of dynamic detection of fp word order */ - - switch (xdrs->x_op) { - - case XDR_ENCODE: - if (2*sizeof(int32_t) == sizeof(double)) { - int32_t *lp = (int32_t *)dp; - return (xdr_putlong(xdrs, lp+!LSW) && - xdr_putlong(xdrs, lp+LSW)); - } else if (2*sizeof(int) == sizeof(double)) { - int *ip = (int *)dp; - int32_t tmp[2]; - tmp[0] = ip[!LSW]; - tmp[1] = ip[LSW]; - return (xdr_putlong(xdrs, tmp) && - xdr_putlong(xdrs, tmp+1)); - } - break; - - case XDR_DECODE: - if (2*sizeof(int32_t) == sizeof(double)) { - int32_t *lp = (int32_t *)dp; - return (xdr_getlong(xdrs, lp+!LSW) && - xdr_getlong(xdrs, lp+LSW)); - } else if (2*sizeof(int) == sizeof(double)) { - int *ip = (int *)dp; - int32_t tmp[2]; - if (xdr_getlong(xdrs, tmp+!LSW) && - xdr_getlong(xdrs, tmp+LSW)) { - ip[0] = tmp[0]; - ip[1] = tmp[1]; - return (1); - } - } - break; - - case XDR_FREE: - return (1); - } - return (0); -} - - -static int xdrstdio_getlong (XDR *, int32_t *); -static int xdrstdio_putlong (XDR *, int32_t *); -static int xdrstdio_getbytes (XDR *, char *, unsigned int); -static int xdrstdio_putbytes (XDR *, char *, unsigned int); -static unsigned int xdrstdio_getpos (XDR *); -static int xdrstdio_setpos (XDR *, unsigned int); -static void xdrstdio_destroy (XDR *); - -/* - * Ops vector for stdio type XDR - */ -static const struct xdr_ops xdrstdio_ops = - { - xdrstdio_getlong, /* deserialize a long int */ - xdrstdio_putlong, /* serialize a long int */ - xdrstdio_getbytes, /* deserialize counted bytes */ - xdrstdio_putbytes, /* serialize counted bytes */ - xdrstdio_getpos, /* get offset in the stream */ - xdrstdio_setpos, /* set offset in the stream */ - xdrstdio_destroy, /* destroy stream */ - }; - -/* - * Initialize a stdio xdr stream. - * Sets the xdr stream handle xdrs for use on the stream file. - * Operation flag is set to op. - */ -static void -xdrstdio_create (XDR *xdrs, FILE *file, enum xdr_op op) -{ - xdrs->x_op = op; - - xdrs->x_ops = (struct xdr_ops *) &xdrstdio_ops; - xdrs->x_private = (char *) file; -} - -/* - * Destroy a stdio xdr stream. - * Cleans up the xdr stream handle xdrs previously set up by xdrstdio_create. - */ -static void -xdrstdio_destroy (XDR *xdrs) -{ - (void) fflush ((FILE *) xdrs->x_private); - /* xx should we close the file ?? */ -} - -static int -xdrstdio_getlong (XDR *xdrs, int32_t *lp) -{ - int32_t mycopy; - - if (fread ((char *) & mycopy, 4, 1, (FILE *) xdrs->x_private) != 1) - return 0; - *lp = (int32_t) xdr_ntohl (mycopy); - return 1; -} - -static int -xdrstdio_putlong (XDR *xdrs, int32_t *lp) -{ - int32_t mycopy = xdr_htonl (*lp); - lp = &mycopy; - if (fwrite ((char *) lp, 4, 1, (FILE *) xdrs->x_private) != 1) - return 0; - return 1; -} - -static int -xdrstdio_getbytes (XDR *xdrs, char *addr, unsigned int len) -{ - if ((len != 0) && (fread (addr, (int) len, 1, - (FILE *) xdrs->x_private) != 1)) - return 0; - return 1; -} - -static int -xdrstdio_putbytes (XDR *xdrs, char *addr, unsigned int len) -{ - if ((len != 0) && (fwrite (addr, (int) len, 1, - (FILE *) xdrs->x_private) != 1)) - return 0; - return 1; -} - -/* 32 bit fileseek operations */ -static unsigned int -xdrstdio_getpos (XDR *xdrs) -{ - return (unsigned int) ftell ((FILE *) xdrs->x_private); -} - -static int -xdrstdio_setpos (XDR *xdrs, unsigned int pos) -{ - return fseek ((FILE *) xdrs->x_private, pos, 0) < 0 ? 0 : 1; -} - - - -#endif /* HAVE_RPC_XDR_H not defined */ diff --git a/MDANSE/Extensions/xtc/src/xdrfile_trr.c b/MDANSE/Extensions/xtc/src/xdrfile_trr.c deleted file mode 100644 index ce8ffd4fd5..0000000000 --- a/MDANSE/Extensions/xtc/src/xdrfile_trr.c +++ /dev/null @@ -1,532 +0,0 @@ - /* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- - * - * $Id$ - * - * Copyright (c) 2009-2014, Erik Lindahl & David van der Spoel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "xdrfile.h" -#include "xdrfile_trr.h" - -#define BUFSIZE 128 -#define GROMACS_MAGIC 1993 - -typedef struct /* This struct describes the order and the */ -/* sizes of the structs in a trjfile, sizes are given in bytes. */ -{ - mybool bDouble; /* Double precision? */ - int ir_size; /* Backward compatibility */ - int e_size; /* Backward compatibility */ - int box_size; /* Non zero if a box is present */ - int vir_size; /* Backward compatibility */ - int pres_size; /* Backward compatibility */ - int top_size; /* Backward compatibility */ - int sym_size; /* Backward compatibility */ - int x_size; /* Non zero if coordinates are present */ - int v_size; /* Non zero if velocities are present */ - int f_size; /* Non zero if forces are present */ - - int natoms; /* The total number of atoms */ - int step; /* Current step number */ - int nre; /* Backward compatibility */ - float tf; /* Current time */ - float lambdaf; /* Current value of lambda */ - double td; /* Current time */ - double lambdad; /* Current value of lambda */ -} t_trnheader; - -static int nFloatSize(t_trnheader *sh,int *nflsz) -{ - int nflsize=0; - - if (sh->box_size) - nflsize = sh->box_size/(DIM*DIM); - else if (sh->x_size) - nflsize = sh->x_size/(sh->natoms*DIM); - else if (sh->v_size) - nflsize = sh->v_size/(sh->natoms*DIM); - else if (sh->f_size) - nflsize = sh->f_size/(sh->natoms*DIM); - else - return exdrHEADER; - - if (((nflsize != sizeof(float)) && (nflsize != sizeof(double)))) - return exdrHEADER; - - *nflsz = nflsize; - - return exdrOK; -} - -extern int do_trnheader(XDRFILE *xd,mybool bRead,t_trnheader *sh) -{ - int magic=GROMACS_MAGIC; - int nflsz,slen,result; - char *version = "GMX_trn_file"; - char buf[BUFSIZE]; - - if (xdrfile_read_int(&magic,1,xd) != 1) { - /* modification by RTM to return the right EOF code - this is what's happening in the XTC code */ - if (bRead) - return exdrENDOFFILE; - else - return exdrINT; - } - if (magic != GROMACS_MAGIC) - return exdrMAGIC; - if (bRead) - { - if (xdrfile_read_int(&slen,1,xd) != 1) - return exdrINT; - if (slen != strlen(version)+1) - return exdrSTRING; - if (xdrfile_read_string(buf,BUFSIZE,xd) <= 0) - return exdrSTRING; - } - else - { - slen = strlen(version)+1; - if (xdrfile_read_int(&slen,1,xd) != 1) - return exdrINT; - if (xdrfile_write_string(version,xd) != (strlen(version)+1) ) - return exdrSTRING; - } - if (xdrfile_read_int(&sh->ir_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->e_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->box_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->vir_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->pres_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->top_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->sym_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->x_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->v_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->f_size,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->natoms,1,xd) != 1) - return exdrINT; - - if ((result = nFloatSize(sh,&nflsz)) != exdrOK) - return result; - sh->bDouble = (nflsz == sizeof(double)); - - if (xdrfile_read_int(&sh->step,1,xd) != 1) - return exdrINT; - if (xdrfile_read_int(&sh->nre,1,xd) != 1) - return exdrINT; - if (sh->bDouble) - { - if (xdrfile_read_double(&sh->td,1,xd) != 1) - return exdrDOUBLE; - sh->tf = sh->td; - if (xdrfile_read_double(&sh->lambdad,1,xd) != 1) - return exdrDOUBLE; - sh->lambdaf = sh->lambdad; - } - else - { - if (xdrfile_read_float(&sh->tf,1,xd) != 1) - return exdrFLOAT; - sh->td = sh->tf; - if (xdrfile_read_float(&sh->lambdaf,1,xd) != 1) - return exdrFLOAT; - sh->lambdad = sh->lambdaf; - } - - return exdrOK; -} - -static int do_htrn(XDRFILE *xd,mybool bRead,t_trnheader *sh, - matrix box,rvec *x,rvec *v,rvec *f) -{ - double pvd[DIM*DIM]; - double *dx=NULL; - float pvf[DIM*DIM]; - float *fx=NULL; - int i,j; - - if (sh->bDouble) - { - if (sh->box_size != 0) - { - if (!bRead) - { - for(i=0; (ivir_size != 0) - { - if (xdrfile_read_double(pvd,DIM*DIM,xd) != DIM*DIM) - return exdrDOUBLE; - } - - if (sh->pres_size!= 0) - { - if (xdrfile_read_double(pvd,DIM*DIM,xd) != DIM*DIM) - return exdrDOUBLE; - } - - if ((sh->x_size != 0) || (sh->v_size != 0) || (sh->f_size != 0)) { - dx = (double *)calloc(sh->natoms*DIM,sizeof(dx[0])); - if (NULL == dx) - return exdrNOMEM; - } - if (sh->x_size != 0) - { - if (!bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jnatoms*DIM,xd) == sh->natoms*DIM) - { - if (bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jv_size != 0) - { - if (!bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jnatoms*DIM,xd) == sh->natoms*DIM) - { - for(i=0; (inatoms); i++) - for(j=0; (jf_size != 0) - { - if (!bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jnatoms*DIM,xd) == sh->natoms*DIM) - { - for(i=0; (inatoms); i++) - { - for(j=0; (jx_size != 0) || (sh->v_size != 0) || (sh->f_size != 0)) { - free(dx); - } - } - else - /* Float */ - { - if (sh->box_size != 0) - { - if (!bRead) - { - for(i=0; (ivir_size != 0) - { - if (xdrfile_read_float(pvf,DIM*DIM,xd) != DIM*DIM) - return exdrFLOAT; - } - - if (sh->pres_size!= 0) - { - if (xdrfile_read_float(pvf,DIM*DIM,xd) != DIM*DIM) - return exdrFLOAT; - } - - if ((sh->x_size != 0) || (sh->v_size != 0) || (sh->f_size != 0)) { - fx = (float *)calloc(sh->natoms*DIM,sizeof(fx[0])); - if (NULL == fx) - return exdrNOMEM; - } - if (sh->x_size != 0) - { - if (!bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jnatoms*DIM,xd) == sh->natoms*DIM) - { - if (bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jv_size != 0) - { - if (!bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jnatoms*DIM,xd) == sh->natoms*DIM) - { - for(i=0; (inatoms); i++) - for(j=0; (jf_size != 0) - { - if (!bRead) - { - for(i=0; (inatoms); i++) - for(j=0; (jnatoms*DIM,xd) == sh->natoms*DIM) - { - for(i=0; (inatoms); i++) - for(j=0; (jx_size != 0) || (sh->v_size != 0) || (sh->f_size != 0)) { - free(fx); - } - } - return exdrOK; -} - -static int do_trn(XDRFILE *xd,mybool bRead,int *step,float *t,float *lambda, - matrix box,int *natoms,rvec *x,rvec *v,rvec *f) -{ - t_trnheader *sh; - int result; - - sh = (t_trnheader *)calloc(1,sizeof(*sh)); - - if (!bRead) { - sh->box_size = (NULL != box) ? sizeof(matrix):0; - sh->x_size = ((NULL != x) ? (*natoms*sizeof(x[0])):0); - sh->v_size = ((NULL != v) ? (*natoms*sizeof(v[0])):0); - sh->f_size = ((NULL != f) ? (*natoms*sizeof(f[0])):0); - sh->natoms = *natoms; - sh->step = *step; - sh->nre = 0; - sh->td = *t; - sh->lambdad = *lambda; - sh->tf = *t; - sh->lambdaf = *lambda; - } - if ((result = do_trnheader(xd,bRead,sh)) != exdrOK) - return result; - - if (bRead) { - *natoms = sh->natoms; - *step = sh->step; - *t = sh->td; - *lambda = sh->lambdad; - } - if ((result = do_htrn(xd,bRead,sh,box,x,v,f)) != exdrOK) - return result; - - free(sh); - - return exdrOK; -} - -/************************************************************ - * - * The following routines are the exported ones - * - ************************************************************/ - -int read_trr_natoms(char *fn,int *natoms) -{ - XDRFILE *xd; - t_trnheader sh; - int result; - - xd = xdrfile_open(fn,"r"); - if (NULL == xd) - return exdrFILENOTFOUND; - if ((result = do_trnheader(xd,1,&sh)) != exdrOK) { - xdrfile_close(xd); - return result; - } - xdrfile_close(xd); - *natoms = sh.natoms; - - return exdrOK; -} - -int read_trr_nframes(char *fn, unsigned long *nframes) { - XDRFILE *xd; - int result, step; - float time, lambda; - int natoms; - matrix box; - rvec *x; - *nframes = 0; - - read_trr_natoms(fn, &natoms); - x = malloc(natoms * sizeof(*x)); - - xd = xdrfile_open(fn, "r"); - if (NULL == xd) - return exdrFILENOTFOUND; - - do { - result = read_trr(xd, natoms, &step, &time, &lambda, - box, x, NULL, NULL); - if (exdrENDOFFILE != result) { - (*nframes)++; - } - } while (result == exdrOK); - - xdrfile_close(xd); - free(x); - return exdrOK; -} - -int write_trr(XDRFILE *xd,int natoms,int step,float t,float lambda, - matrix box,rvec *x,rvec *v,rvec *f) -{ - return do_trn(xd,0,&step,&t,&lambda,box,&natoms,x,v,f); -} - -int read_trr(XDRFILE *xd,int natoms,int *step,float *t,float *lambda, - matrix box,rvec *x,rvec *v,rvec *f) -{ - return do_trn(xd,1,step,t,lambda,box,&natoms,x,v,f); -} diff --git a/MDANSE/Extensions/xtc/src/xdrfile_xtc.c b/MDANSE/Extensions/xtc/src/xdrfile_xtc.c deleted file mode 100644 index c084f69d9f..0000000000 --- a/MDANSE/Extensions/xtc/src/xdrfile_xtc.c +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- - * - * $Id$ - * - * Copyright (c) 2009-2014, Erik Lindahl & David van der Spoel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "xdrfile.h" -#include "xdrfile_xtc.h" - -#define MAGIC 1995 - -enum { FALSE, TRUE }; - -static int xtc_header(XDRFILE *xd,int *natoms,int *step,float *time,mybool bRead) -{ - int result,magic,n=1; - - /* Note: read is same as write. He he he */ - magic = MAGIC; - if ((result = xdrfile_write_int(&magic,n,xd)) != n) - { - if (bRead) - return exdrENDOFFILE; - else - return exdrINT; - } - if (magic != MAGIC) - return exdrMAGIC; - if ((result = xdrfile_write_int(natoms,n,xd)) != n) - return exdrINT; - if ((result = xdrfile_write_int(step,n,xd)) != n) - return exdrINT; - if ((result = xdrfile_write_float(time,n,xd)) != n) - return exdrFLOAT; - - return exdrOK; -} - -static int xtc_coord(XDRFILE *xd,int *natoms,matrix box,rvec *x,float *prec, - mybool bRead) -{ - int i,j,result; - - /* box */ - result = xdrfile_read_float(box[0],DIM*DIM,xd); - if (DIM*DIM != result) - return exdrFLOAT; - else - { - if (bRead) - { - result = xdrfile_decompress_coord_float(x[0],natoms,prec,xd); - if (result != *natoms) - return exdr3DX; - } - else - { - result = xdrfile_compress_coord_float(x[0],*natoms,*prec,xd); - if (result != *natoms) - return exdr3DX; - } - } - return exdrOK; -} - -int read_xtc_natoms(char *fn,int *natoms) -{ - XDRFILE *xd; - int step,result; - float time; - - xd = xdrfile_open(fn,"r"); - if (NULL == xd) - return exdrFILENOTFOUND; - result = xtc_header(xd,natoms,&step,&time,TRUE); - xdrfile_close(xd); - - return result; -} - -int read_xtc_nframes(char* fn, unsigned long *nframes) { - XDRFILE *xd; - int result, step; - float time; - int natoms; - matrix box; - rvec *x; - float prec; - *nframes = 0; - - read_xtc_natoms(fn, &natoms); - x = malloc(natoms * sizeof(*x)); - - xd = xdrfile_open(fn, "r"); - if (NULL == xd) - return exdrFILENOTFOUND; - - do { - result = read_xtc(xd, natoms, &step, &time, box, x, &prec); - if (exdrENDOFFILE != result) { - (*nframes)++; - } - } while (result == exdrOK); - - xdrfile_close(xd); - free(x); - return exdrOK; -} - -int read_xtc(XDRFILE *xd, - int natoms,int *step,float *time, - matrix box,rvec *x,float *prec) -/* Read subsequent frames */ -{ - int result; - - if ((result = xtc_header(xd,&natoms,step,time,TRUE)) != exdrOK) - return result; - - if ((result = xtc_coord(xd,&natoms,box,x,prec,1)) != exdrOK) - return result; - - return exdrOK; -} - -int write_xtc(XDRFILE *xd, - int natoms,int step,float time, - matrix box,rvec *x,float prec) -/* Write a frame to xtc file */ -{ - int result; - - if ((result = xtc_header(xd,&natoms,&step,&time,FALSE)) != exdrOK) - return result; - - if ((result = xtc_coord(xd,&natoms,box,x,&prec,0)) != exdrOK) - return result; - - return exdrOK; -} diff --git a/MDANSE/Extensions/xtc/trr.pyx b/MDANSE/Extensions/xtc/trr.pyx deleted file mode 100644 index e2c48a09b7..0000000000 --- a/MDANSE/Extensions/xtc/trr.pyx +++ /dev/null @@ -1,774 +0,0 @@ -# cython: c_string_type=str, c_string_encoding=ascii -############################################################################## -# MDTraj: A Python Library for Loading, Saving, and Manipulating -# Molecular Dynamics Trajectories. -# Copyright 2012-2013 Stanford University and the Authors -# -# Authors: Robert McGibbon -# Contributors: -# -# MDTraj is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as -# published by the Free Software Foundation, either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with MDTraj. If not, see . -############################################################################## - - -############################################################################### -# Imports -############################################################################### - -import os -import warnings - -import numpy as np - -cimport numpy as np - -np.import_array() - -from MDANSE.mdtraj.utils import cast_indices, ensure_type - -cimport xdrlib - -cimport trrlib -from libc.stdio cimport SEEK_CUR, SEEK_SET - -ctypedef np.npy_int64 int64_t - -__all__ = ['TRRTrajectoryFile'] - -############################################################################### -# globals -############################################################################### - -cdef int _EXDROK = 0 # OK -cdef int _EXDRENDOFFILE = 11 # End of file -_EXDR_ERROR_MESSAGES = { - 1: "Header", - 2: "String", - 3: "Double", - 4: "Integer", - 5: "Float", - 6: "Unsigned integer", - 7: "Compressed 3d coordinate", - 8: "Closing file", - 9: " Magic number", - 10: 'Not enough memory', - 12: "File not found" -} - -# numpy variable types include the specific numpy of bytes of each, but the c -# variables in our interface file don't. this could get bad if we're on a wierd -# machine, so lets make sure first -if sizeof(int) != sizeof(np.int32_t): - raise RuntimeError('Integers on your compiler are not 32 bits. This is not good.') -if sizeof(float) != sizeof(np.float32_t): - raise RuntimeError('Floats on your compiler are not 32 bits. This is not good') - -############################################################################### -# Code -############################################################################### - -def load_trr(filename, top=None, stride=None, atom_indices=None, frame=None): - """load_trr(filename, top=None, stride=None, atom_indices=None, frame=None) - - Load a Gromacs TRR file from disk. - - The .trr format is a cross-platform compressed binary trajectory format - produced by the gromacs software that stores atomic coordinates, box - vectors, and time information. - - Parameters - ---------- - filename : str - Filename of TRR trajectory file. - top : {str, Trajectory, Topology} - The TRR format does not contain topology information. Pass in either the - path to a pdb file, a trajectory, or a topology to supply this information. - stride : int, default=None - Only read every stride-th frame - atom_indices : array_like, optional - If not none, then read only a subset of the atoms coordinates from the - file. This may be slightly slower than the standard read because it - requires an extra copy, but will save memory. - frame : int, optional - Use this option to load only a single frame from a trajectory on disk. - If frame is None, the default, the entire trajectory will be loaded. - If supplied, ``stride`` will be ignored. - - Examples - -------- - >>> import mdtraj as md # doctest: +SKIP - >>> traj = md.load_trr('output.trr', top='topology.pdb') # doctest: +SKIP - >>> print traj # doctest: +SKIP - # doctest: +SKIP - - Returns - ------- - trajectory : md.Trajectory - The resulting trajectory, as an md.Trajectory object. - - See Also - -------- - mdtraj.TRRTrajectoryFile : Low level interface to TRR files - """ - # we make it not required in the signature, but required here. although this - # is a little wierd, its good because this function is usually called by a - # dispatch from load(), where top comes from **kwargs. So if its not supplied - # we want to give the user an informative error message - from mdtraj.core.trajectory import Trajectory, _parse_topology - if top is None: - raise ValueError('"top" argument is required for load_trr') - - if not isinstance(filename, (str, os.PathLike)): - raise TypeError('filename must be of type string for load_trr. ' - 'you supplied %s' % type(filename)) - - topology = _parse_topology(top) - atom_indices = cast_indices(atom_indices) - with TRRTrajectoryFile(str(filename), 'r') as f: - if frame is not None: - f.seek(frame) - n_frames = 1 - else: - n_frames = None - - return f.read_as_traj(topology, n_frames=n_frames, stride=stride, - atom_indices=atom_indices) - - -cdef class TRRTrajectoryFile(object): - """TRRTrajectoryFile(filename, mode='r', force_overwrite=True, **kwargs) - - Interface for reading and writing to a GROMACS TRR file. - This is a file-like objec that supports both reading and writing. - It also supports the context manager protocol, so you can use it - with the python 'with' statement. - - The conventional units in the TRR file are nanometers and picoseconds. - The format only supports saving coordinates, the time, the md step, - and the unit cell parametrs (box vectors) - - Parameters - ---------- - filename : str - The filename to open. A path to a file on disk. - mode : {'r', 'w'} - The mode in which to open the file, either 'r' for read or 'w' for write. - force_overwrite : bool - If opened in write mode, and a file by the name of `filename` already - exists on disk, should we overwrite it? - - Other Parameters - ---------------- - min_chunk_size : int, default=100 - In read mode, we need to allocate a buffer in which to store the data - without knowing how many frames are in the file. This parameter is the - minimum size of the buffer to allocate. - chunk_size_multiplier, int, default=1.5 - In read mode, we need to allocate a buffer in which to store the data - without knowing how many frames are in the file. We can *guess* this - information based on the size of the file on disk, but it's not perfect. - This parameter inflates the guess by a multiplicative factor. - - Examples - -------- - >>> # load up the data from a trr - >>> with TRRTrajectoryFile('traj.trr') as f: - >>> xyz, time, step, box, lambdas = f.read() - - See Also - -------- - mdtraj.load_trr : High-level wrapper that returns a ``md.Trajectory`` - """ - cdef trrlib.XDRFILE* fh - cdef str filename - cdef int n_atoms # number of atoms in the file - cdef int64_t n_frames # numnber of frames in the file, cached - cdef int64_t frame_counter # current position in the file, in read mode - cdef char is_open # is the file handle currently open? - cdef int64_t approx_n_frames # appriximate number of frames in the file, as guessed based on its size - cdef char* mode # mode in which the file is open, either 'r' or 'w' - cdef int min_chunk_size - cdef float chunk_size_multiplier - cdef char with_unitcell # used in mode='w' to know if we're writing unitcells or nor - cdef readonly char* distance_unit - cdef np.ndarray _offsets - cdef int has_velocities - cdef int has_forces - - def __cinit__(self, char* filename, char* mode='r', force_overwrite=True, **kwargs): - """Open a GROMACS TRR file for reading/writing. - """ - self.distance_unit = 'nanometers' - self.is_open = False - self.frame_counter = 0 - self.n_frames = -1 - self.filename = filename - self._offsets = None - - if str(mode) == 'r': - self.n_atoms = 0 - if not os.path.exists(filename): - raise IOError("The file '%s' doesn't exist" % filename) - trrlib.read_trr_natoms(filename, &self.n_atoms) - if self.n_atoms <= 0: - raise IOError('Malformed TRR file. Number of atoms <= 0. ' - 'Are you sure this is a valid GROMACS TRR file?') - - self.fh = trrlib.xdrfile_open(filename, b'r') - if self.fh is NULL: - raise IOError('File not found: "%s"' % filename) - - # check for velocities/forces; store number found - self.has_velocities = -1 - self.has_forces = -1 - self.has_velocities, self.has_forces = self._check_has_velocities_forces() - if self.has_velocities < 0 or self.has_forces < 0: - raise RuntimeError("Could not determine whether velocities" - " or forces are present!") - - self.approx_n_frames = self._estimate_n_frames_from_filesize(os.stat(filename).st_size) - - self.min_chunk_size = max(kwargs.pop('min_chunk_size', 100), 1) - self.chunk_size_multiplier = max(kwargs.pop('chunk_size_multiplier', 1.5), 0.01) - - elif str(mode) == 'w': - if force_overwrite and os.path.exists(filename): - os.unlink(filename) - if not force_overwrite and os.path.exists(filename): - raise IOError('"%s" already exists' % filename) - - self.fh = trrlib.xdrfile_open(filename, 'w') - if self.fh is NULL: - raise IOError('Unable to open file "%s"' % filename) - else: - raise ValueError('mode must be one of "r" or "w". ' - 'you supplied %s' % mode) - - for key in kwargs.keys(): - warnings.warn('kwarg "%s" was not recognized or processed' % key) - - self.is_open = True - self.mode = mode - - def _check_has_velocities_forces(self): - # return values are nonzero if the file contains velocities/forces - cdef trrlib.t_trnheader header - cdef int frame_size, header_size - cdef int64_t n_frames, old_pos - - old_pos = xdrlib.xdr_tell(self.fh) - try: - xdrlib.xdr_seek(self.fh, 0, SEEK_SET) - if trrlib.do_trnheader(self.fh, 1, &header) != 0: - raise RuntimeError("could not read header of first frame!") - finally: - xdrlib.xdr_seek(self.fh, old_pos, SEEK_SET) - - has_velocities = header.v_size - has_forces = header.f_size - return has_velocities, has_forces - - def _estimate_n_frames_from_filesize(self, filesize): - # read the first header, sum the size of all data fields and extrapolate. - cdef trrlib.t_trnheader header - cdef int frame_size, header_size - cdef int64_t n_frames, old_pos - - old_pos = xdrlib.xdr_tell(self.fh) - try: - xdrlib.xdr_seek(self.fh, 0, SEEK_SET) - if trrlib.do_trnheader(self.fh, 1, &header) != 0: - raise RuntimeError("could not read header of first frame!") - finally: - xdrlib.xdr_seek(self.fh, old_pos, SEEK_SET) - - header_size = xdrlib.xdr_tell(self.fh) - frame_size = sum(( &header)[i] for i in range(1, 11)) - n_frames = filesize / (frame_size + header_size) - - return n_frames - - def __dealloc__(self): - self.close() - - def close(self): - """Close the TRR file""" - if self.is_open: - trrlib.xdrfile_close(self.fh) - self.is_open = False - - def read(self, n_frames=None, stride=None, atom_indices=None): - """read(n_frames=None, stride=None, atom_indices=None) - - Read data from a TRR file - - Parameters - ---------- - n_frames : int, None - The number of frames you would like to read from the file. - If None, all of the remaining frames will be loaded. - stride : int, optional - Read only every stride-th frame. - atom_indices : array_like, optional - If not none, then read only a subset of the atoms coordinates from the - file. This may be slightly slower than the standard read because it required - an extra copy, but will save memory. - - Returns - ------- - xyz : np.ndarray, shape=(n_frames, n_atoms, 3), dtype=np.float32 - The cartesian coordinates, in nanometers - time : np.ndarray, shape=(n_frames), dtype=np.float32 - The simulation time, in picoseconds, corresponding to each frame - step : np.ndarray, shape=(n_frames), dtype=np.int32 - The step in the simulation corresponding to each frame - box : np.ndarray, shape=(n_frames, 3, 3), dtype=np.float32 - The box vectors in each frame. - lambd : np.ndarray, shape=(n_frames), dtype=np.float32 - The alchemical lambda value of each frame. - - Notes - ----- - The TRR format DOES support saving velocities and forces, but those - fields are not read (or written) by the current implementation of this - wrapper. However, this functionality is accessible in the internal - TRRTrajectoryFile._read()/._write() methods. - """ - if not str(self.mode) == 'r': - raise ValueError('read() is only available when file is opened in mode="r"') - if not self.is_open: - raise IOError('file must be open to read from it.') - - stride = int(stride) if stride is not None else 1 - - if n_frames is not None: - # if they supply the number of frames they want, that's easy - if not int(n_frames) == n_frames: - raise ValueError('n_frames must be an int, you supplied "%s"' % n_frames) - # vel and forces will be `None` here, but must be unpacked - xyz, time, step, box, lambd, vel, forces = \ - self._read(int(n_frames), atom_indices, stride=stride) - if np.all(np.logical_and(box < 1e-10, box > -1e-10)): - box = None - return xyz, time, step, box, lambd - - # if they want ALL of the remaining frames, we need to guess at the chunk - # size, and then check the exit status to make sure we're really at the EOF - all_xyz, all_time, all_step, all_box, all_lambd = [], [], [], [], [] - - while True: - # guess the size of the chunk to read, based on how many frames we - # think are in the file and how many we've currently read - chunk = max(abs(int((self.approx_n_frames - self.frame_counter) * self.chunk_size_multiplier)), - self.min_chunk_size) - - xyz, time, step, box, lambd, vel, forces = \ - self._read(chunk, atom_indices, stride=stride) - if len(xyz) <= 0: - break - - all_xyz.append(xyz) - all_time.append(time) - all_step.append(step) - all_box.append(box) - all_lambd.append(lambd) - - all_xyz = np.concatenate(all_xyz) - all_time = np.concatenate(all_time) - all_step = np.concatenate(all_step) - all_box = np.concatenate(all_box) - all_lambd = np.concatenate(all_lambd) - if np.all(np.logical_and(all_box < 1e-10, all_box > -1e-10)): - all_box = None - return all_xyz, all_time, all_step, all_box, all_lambd - - def _read(self, int64_t n_frames, atom_indices, - bint get_velocities=False, get_forces=False, stride=1): - """Read a specified number of TRR frames from the buffer""" - - cdef int64_t i = 0 - cdef int status = _EXDROK - cdef int status_seek = _EXDROK - cdef int n_atoms_to_read - cdef bint efficient_striding = stride > 1 and self._offsets is not None - - # check that velocities/forces are present if requested - if get_velocities and self.has_velocities == 0: - raise RuntimeError("Velocities requested, but none in file") - if get_forces and self.has_forces == 0: - raise RuntimeError("Forces requested, but none in file") - - if atom_indices is None: - n_atoms_to_read = self.n_atoms - elif isinstance(atom_indices, slice): - n_atoms_to_read = len(np.arange(self.n_atoms)[atom_indices]) - else: - atom_indices = np.asarray(atom_indices) - if min(atom_indices) < 0: - raise ValueError('atom_indices should be zero indexed. you gave an index less than zero') - if max(atom_indices) >= self.n_atoms: - raise ValueError('atom indices should be zero indexed. you gave an index bigger than the number of atoms') - n_atoms_to_read = len(atom_indices) - - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] xyz = \ - np.empty((n_frames, n_atoms_to_read, 3), dtype=np.float32) - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] time = \ - np.empty(n_frames, dtype=np.float32) - cdef np.ndarray[ndim=1, dtype=np.int32_t, mode='c'] step = \ - np.empty(n_frames, dtype=np.int32) - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] lambd = \ - np.empty(n_frames, dtype=np.float32) - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] box = \ - np.empty((n_frames, 3, 3), dtype=np.float32) - - # striding dummy, only used if efficient_striding is false or at the end of the file. - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] xyz_stride = \ - np.empty([1, n_atoms_to_read, 3], dtype=np.float32) - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] time_stride = \ - np.empty(1, dtype=np.float32) - cdef np.ndarray[ndim=1, dtype=np.int32_t, mode='c'] step_stride = \ - np.empty(1, dtype=np.int32) - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] lambd_stride = \ - np.empty(1, dtype=np.float32) - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] box_stride = \ - np.empty((1, 3, 3), dtype=np.float32) - - # only used if get_velocities/get_forces is True - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] vel - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] forces - - # only used if atom_indices is given - cdef np.ndarray[dtype=np.float32_t, ndim=2] xyz_buffer - cdef np.ndarray[dtype=np.float32_t, ndim=2] vel_buffer - cdef np.ndarray[dtype=np.float32_t, ndim=2] forces_buffer - if atom_indices is not None: - xyz_buffer = np.zeros((self.n_atoms, 3), dtype=np.float32) - vel_buffer = np.zeros((self.n_atoms, 3), dtype=np.float32) - forces_buffer = np.zeros((self.n_atoms, 3), dtype=np.float32) - - if get_velocities: - vel = np.empty((n_frames, n_atoms_to_read, 3), dtype=np.float32) - if get_forces: - forces = np.empty((n_frames, n_atoms_to_read, 3), dtype=np.float32) - - - while (i < n_frames) and (status != _EXDRENDOFFILE): - if atom_indices is None: - frame_vel = NULL - if get_velocities: - frame_vel = &vel[i,0,0] - frame_forces = NULL - if get_forces: - frame_forces = &forces[i,0,0] - status = trrlib.read_trr(self.fh, self.n_atoms, - &step[i], - &time[i], &lambd[i], - &box[i,0,0], - &xyz[i,0,0], - frame_vel, frame_forces) - else: - frame_vel = NULL - if get_velocities: - frame_vel = &vel_buffer[0,0] - frame_forces = NULL - if get_forces: - frame_forces = &forces_buffer[0,0] - status = trrlib.read_trr(self.fh, self.n_atoms, - &step[i], - &time[i], &lambd[i], - &box[i,0,0], - &xyz_buffer[0,0], - frame_vel, frame_forces) - xyz[i, :, :] = xyz_buffer[atom_indices, :] - if get_velocities: - vel[i, :, :] = vel_buffer[atom_indices, :] - if get_forces: - forces[i, :, :] = forces_buffer[atom_indices, :] - - if status != _EXDRENDOFFILE and status != _EXDROK: - raise RuntimeError('TRR read error: %s' % _EXDR_ERROR_MESSAGES.get(status, 'unknown')) - i += 1 - - if stride > 1: - if efficient_striding and self.frame_counter + stride < len(self): - self.seek(stride, whence=1) - else: - for _ in range(stride - 1): - seek_status = trrlib.read_trr(self.fh, self.n_atoms, - &step_stride[0], - &time_stride[0], - &lambd_stride[0], - &box_stride[0,0,0], - &xyz_stride[0,0,0], - NULL, NULL) - if seek_status != _EXDROK: - break - - if status == _EXDRENDOFFILE: - xyz = xyz[:i-1] - box = box[:i-1] - time = time[:i-1] - step = step[:i-1] - lambd = lambd[:i-1] - if get_velocities: - vel = vel[:i-1] - if get_forces: - forces = forces[:i-1] - - # if we are using seek, the framecounter already points to the right absolute position, - # otherwise we increment the counter relatively - if not efficient_striding: - self.frame_counter += i - - vel_return = vel if get_velocities else None - forces_return = forces if get_forces else None - - return xyz, time, step, box, lambd, vel_return, forces_return - - def write(self, xyz, time=None, step=None, box=None, lambd=None): - """write(xyz, time=None, step=None, box=None, lambd=None) - - Write data to a TRR file - - Parameters - ---------- - xyz : np.ndarray, dtype=np.float32, shape=(n_frames, n_atoms, 3) - The cartesian coordinates of the atoms, in nanometers - time : np.ndarray, dtype=float32, shape=(n_frames), optional - The simulation time corresponding to each frame, in picoseconds. - If not supplied, the numbers 0..n_frames will be written. - step : np.ndarray, dtype=int32, shape=(n_frames), optional - The simulation timestep corresponding to each frame, in steps. - If not supplied, the numbers 0..n_frames will be written - box : np.ndarray, dtype=float32, shape=(n_frames, 3, 3), optional - The periodic box vectors of the simulation in each frame, in nanometers. - If not supplied, the vectors (1,0,0), (0,1,0) and (0,0,1) will - be written for each frame. - lambd : np.ndarray, dtype=np.float32, shape=(n_frames), optional - The alchemical lambda value at each frame. If not supplied, all - zeros will be written. - """ - if str(self.mode) != 'w': - raise ValueError('write() is only available when the file is opened in mode="w"') - - # do typechecking, and then dispatch to the c level function - xyz = ensure_type(xyz, dtype=np.float32, ndim=3, name='xyz', can_be_none=False, - add_newaxis_on_deficient_ndim=True, warn_on_cast=False) - n_frames = len(xyz) - time = ensure_type(time, dtype=np.float32, ndim=1, name='time', can_be_none=True, - shape=(n_frames,), add_newaxis_on_deficient_ndim=True, - warn_on_cast=False) - step = ensure_type(step, dtype=np.int32, ndim=1, name='step', can_be_none=True, - shape=(n_frames,), add_newaxis_on_deficient_ndim=True, - warn_on_cast=False) - box = ensure_type(box, dtype=np.float32, ndim=3, name='box', can_be_none=True, - shape=(n_frames, 3, 3), add_newaxis_on_deficient_ndim=True, - warn_on_cast=False) - lambd = ensure_type(lambd, dtype=np.float32, ndim=1, name='lambd', can_be_none=True, - shape=(n_frames,), add_newaxis_on_deficient_ndim=True, - warn_on_cast=False) - - if self.frame_counter == 0: - self.n_atoms = xyz.shape[1] - self.with_unitcell = (box is not None) - else: - if not self.n_atoms == xyz.shape[1]: - raise ValueError("This file has %d atoms, but you're now trying to write %d atoms" % (self.n_atoms, xyz.shape[1])) - if self.with_unitcell and (box is None): - raise ValueError("The file that you're saving to expects each frame " - "to contain unitcell information, but you did not supply it.") - if (not self.with_unitcell) and (box is not None): - raise ValueError("The file that you're saving to was created without " - "unitcell information.") - - if time is None: - time = np.arange(0, n_frames, dtype=np.float32) - if step is None: - step = np.arange(0, n_frames, dtype=np.int32) - if box is None: - # make each box[i] be the all zeros, which indicates the lack of - # a unitcell - box = np.zeros((n_frames, 3, 3), dtype=np.float32) - if lambd is None: - lambd = np.zeros(n_frames, dtype=np.float32) - - self._write(xyz, time, step, box, lambd) - - def _write(self, - np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] xyz not None, - np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] time not None, - np.ndarray[ndim=1, dtype=np.int32_t, mode='c'] step not None, - np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] box not None, - np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] lambd not None, - np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] vel=None, - np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] forces=None - ): - - cdef int n_frames = len(xyz) - cdef int n_atoms = xyz.shape[1] - cdef int status, i - - # all same length - assert n_frames == len(box) == len(step) == len(time) == len(lambd) - - if vel is not None: - assert n_frames == len(vel) - if forces is not None: - assert n_frames == len(forces) - - for i in range(n_frames): - if vel is not None: - write_vel = &vel[i, 0, 0] - else: - write_vel = NULL - if forces is not None: - write_forces = &forces[i, 0, 0] - else: - write_forces = NULL - status = trrlib.write_trr(self.fh, n_atoms, step[i], time[i], - lambd[i], &box[i, 0, 0], - &xyz[i, 0, 0], - write_vel, write_forces) - if status != _EXDROK: - raise RuntimeError('TRR write error: %s' % status) - - self.frame_counter += n_frames - return status - - def seek(self, int offset, int whence=0): - """seek(offset, whence=0) - - Move to a new file position - - Parameters - ---------- - offset : int - A number of frames. - whence : {0, 1, 2} - 0: offset from start of file, offset should be >=0. - 1: move relative to the current position, positive or negative - 2: move relative to the end of file, offset should be <= 0. - Seeking beyond the end of a file is not supported - """ - cdef int status - cdef int64_t pos - - if str(self.mode) != 'r': - raise NotImplementedError('seek() only available in mode="r" currently') - if whence == 0 and offset >= 0: - absolute = offset - elif whence == 1: - absolute = offset + self.frame_counter - elif whence == 2 and offset <= 0: - raise NotImplementedError('offsets from the end are not supported yet') - else: - raise IOError('Invalid argument') - - if absolute < 0 or absolute >= len(self.offsets): - raise IOError('TRR Seek out of bounds: given absolute position: {}'.format(absolute)) - - pos = self.offsets[absolute] - status = xdrlib.xdr_seek(self.fh, pos, SEEK_SET) - if status != 0: - raise RuntimeError('TRR seek error: %s' % status) - - self.frame_counter = absolute - - def tell(self): - """Current file position - - Returns - ------- - offset : int - The current frame in the file. - """ - if str(self.mode) != 'r': - raise NotImplementedError('tell() only available in mode="r" currently') - return int(self.frame_counter) - - @property - def offsets(self): - """get byte offsets from current xtc file - See Also - -------- - set_offsets - """ - if self._offsets is None: - self.n_frames, self._offsets = self._calc_len_and_offsets() - return self._offsets - - @offsets.setter - def offsets(self, offsets): - """set frame offsets""" - self._offsets = offsets - - def _calc_len_and_offsets(self): - """read byte offsets from TRR file directly""" - cdef int status, i, frame_size, header_size - cdef int64_t file_size, frame_offset - cdef unsigned long n_frames - cdef np.ndarray[ndim=1, dtype=np.npy_int64] offsets - cdef trrlib.t_trnheader header - - import os - file_size = os.stat(self.filename).st_size - cdef int64_t old_pos = xdrlib.xdr_tell(self.fh) - - try: - xdrlib.xdr_seek(self.fh, 0, SEEK_SET) - if trrlib.do_trnheader(self.fh, 1, &header) != 0: - raise RuntimeError("could not read header of first frame!") - header_size = xdrlib.xdr_tell(self.fh) - frame_size = sum(( &header)[i] for i in range(1, 11)) - - size = file_size // (frame_size + header_size) - offsets = np.empty(size, dtype=np.int64) - n_frames = 1 - offsets[0] = 0 - - while True: - status = xdrlib.xdr_seek(self.fh, frame_size, SEEK_CUR) - if status != 0: - raise RuntimeError("could not seek...") - frame_offset = xdrlib.xdr_tell(self.fh) - if trrlib.do_trnheader(self.fh, 1, &header) != 0: - break - - frame_size = sum(( &header)[i] for i in range(1, 11)) - if n_frames == len(offsets): - offsets = np.resize(offsets, int(len(offsets)*1.2)) - offsets[n_frames] = frame_offset - n_frames += 1 - finally: - xdrlib.xdr_seek(self.fh, old_pos, SEEK_SET) - - return n_frames, offsets[:n_frames] - - def __enter__(self): - "Support the context manager protocol" - return self - - def __exit__(self, *exc_info): - "Support the context manager protocol" - self.close() - - def __len__(self): - "Number of frames in the file" - if str(self.mode) != 'r': - raise NotImplementedError('len() only available in mode="r" currently') - if not self.is_open: - raise ValueError('I/O operation on closed file') - if self.n_frames == -1: - self.offsets - return int(self.n_frames) diff --git a/MDANSE/Extensions/xtc/trrlib.pxd b/MDANSE/Extensions/xtc/trrlib.pxd deleted file mode 100644 index ca3bbe92d8..0000000000 --- a/MDANSE/Extensions/xtc/trrlib.pxd +++ /dev/null @@ -1,30 +0,0 @@ -cdef extern from "include/xdrfile.h": - ctypedef struct XDRFILE: - pass - - XDRFILE* xdrfile_open (char * path, char * mode) - int xdrfile_close (XDRFILE * xfp) - ctypedef float matrix[3][3] - ctypedef float rvec[3] - - -cdef extern from "include/xdrfile_trr.h": - int read_trr_natoms(char *fn, int *natoms) - int read_trr_nframes(char* fn, unsigned long *nframes) - - # Read one frame of an open xtc file. If either of x, v, f, box are - # NULL the arrays will be read from the file but not used. - int read_trr(XDRFILE *xd, int natoms, int *step, float *t, float* lambd, - matrix box, rvec* x, rvec* v, rvec* f) - - # Write a frame to xtc file - int write_trr(XDRFILE *xd, int natoms, int step, float t, float lambd, - matrix box, rvec* x, rvec* v, rvec* f) - -cdef extern from "include/trr_header.h": - ctypedef struct t_trnheader: - int v_size - int f_size - - # header - int do_trnheader(XDRFILE *xd, int bRead, t_trnheader *sh) diff --git a/MDANSE/Extensions/xtc/xdrlib.pxd b/MDANSE/Extensions/xtc/xdrlib.pxd deleted file mode 100644 index a5d71312b4..0000000000 --- a/MDANSE/Extensions/xtc/xdrlib.pxd +++ /dev/null @@ -1,25 +0,0 @@ - -cdef extern from "include/xdrfile.h": - ctypedef struct XDRFILE: - pass - - XDRFILE* xdrfile_open (char * path, char * mode) - ctypedef float matrix[3][3] - ctypedef float rvec[3] - int xdrfile_close (XDRFILE * xfp) - -cdef extern from "include/xdrfile_xtc.h": - int read_xtc_natoms(char* fn, int* natoms) - int read_xtc(XDRFILE *xd, int natoms, int *step, float *time, matrix box, rvec *x, float *prec) - int write_xtc(XDRFILE *xd, int natoms, int step, float time, matrix box, rvec* x, float prec) - int xdrfile_read_int(int * ptr, int ndata, XDRFILE *xfp) - - -cimport numpy as np - -ctypedef np.npy_int64 int64_t - -cdef extern from "include/xdr_seek.h": - int64_t xdr_tell(XDRFILE *xd) - int xdr_seek(XDRFILE *xd, int64_t pos, int whence) - int xdr_flush(XDRFILE* xd) diff --git a/MDANSE/Extensions/xtc/xtc.pyx b/MDANSE/Extensions/xtc/xtc.pyx deleted file mode 100644 index e70020ac06..0000000000 --- a/MDANSE/Extensions/xtc/xtc.pyx +++ /dev/null @@ -1,706 +0,0 @@ -# cython: c_string_type=str, c_string_encoding=ascii -############################################################################## -# MDTraj: A Python Library for Loading, Saving, and Manipulating -# Molecular Dynamics Trajectories. -# Copyright 2012-2013 Stanford University and the Authors -# -# Authors: Robert McGibbon -# Contributors: -# -# MDTraj is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as -# published by the Free Software Foundation, either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with MDTraj. If not, see . -############################################################################## - - -############################################################################### -# Imports -############################################################################### -import os -import warnings - -import numpy as np - -cimport numpy as np - -np.import_array() - -from MDANSE.mdtraj.utils import cast_indices, ensure_type - -cimport xdrlib - -from libc.math cimport ceil -from libc.stdio cimport SEEK_CUR, SEEK_SET - -ctypedef np.npy_int64 int64_t - - -__all__ = ['XTCTrajectoryFile'] - - -############################################################################### -# globals -############################################################################### - -cdef int _EXDROK = 0 # OK -cdef int _EXDRENDOFFILE = 11 # End of file -_EXDR_ERROR_MESSAGES = { - 1: "Header", - 2: "String", - 3: "Double", - 4: "Integer", - 5: "Float", - 6: "Unsigned integer", - 7: "Compressed 3d coordinate", - 8: "Closing file", - 9: " Magic number", - 10: 'Not enough memory', - 12: "File not found" -} - -# Note: following constant depend on int is a 32bit integer! - -# numpy variable types include the specific numpy of bytes of each, but the c -# variables in our interface file don't. this could get bad if we're on a wierd -# machine, so lets make sure first -if sizeof(int) != sizeof(np.int32_t): - raise RuntimeError('Integers on your compiler are not 32 bits. This is not good.') -if sizeof(float) != sizeof(np.float32_t): - raise RuntimeError('Floats on your compiler are not 32 bits. This is not good') - -# constants for short (<= 10 atoms) XTC files: -cdef int DIM = 3 -# header fields (before coords): -# 1. magic (int) -# 2. natoms (int) -# 3. step (int) -# 4. time (float) -cdef int XTC_HDR_SIZE = 3*sizeof(np.int32_t) + sizeof(np.float32_t) -cdef int XTC_SHORT_HEADER_SIZE = XTC_HDR_SIZE + DIM**2 * sizeof(np.float32_t) + 4 -cdef int XTC_SHORT_BYTES_PER_ATOM = DIM*sizeof(np.float32_t) - -# constant for 'regular' XTCs (> 10 atoms): -# 1. magic(int) -# 2. natoms (int) -# 3. step (int) -# 4. time (float) -# 5. DIM*DIM_box_vecs (DIM*DIM floats) -# 6. natoms (int) -# 7. prec (float) -# 8. DIM_min_xyz (int[3]) -# 9. DIM_max_xyz (int[3]) -# 10. smallidx (int) -cdef int XTC_HEADER_SIZE = 11*sizeof(np.int32_t) + 2*sizeof(np.float32_t) + DIM**2 * sizeof(np.float32_t) - - -############################################################################### -# Code -############################################################################### - -def load_xtc(filename, top=None, stride=None, atom_indices=None, frame=None): - """load_xtc(filename, top=None, stride=None, atom_indices=None, frame=None) - - Load a Gromacs XTC file from disk. - - The .xtc format is a cross-platform compressed binary trajectory format - produced by the gromacs software that stores atomic coordinates, box - vectors, and time information. It is lossy (storing coordinates to about - 1e-3 A) and extremely space-efficient. - - Parameters - ---------- - filename : path-like - Filename (string) of xtc trajectory. - top : {str, Trajectory, Topology} - The XTC format does not contain topology information. Pass in either the - path to a RCSB PDB file, a trajectory, or a topology to supply this - information. - stride : int, default=None - Only read every stride-th frame - atom_indices : array_like, optional - If not none, then read only a subset of the atoms coordinates from the - file. This may be slightly slower than the standard read because it - requires an extra copy, but will save memory. - frame : int, optional - Use this option to load only a single frame from a trajectory on disk. - If frame is None, the default, the entire trajectory will be loaded. - If supplied, ``stride`` will be ignored. - - Examples - -------- - >>> import mdtraj as md - >>> traj = md.load_xtc('output.xtc', top='topology.pdb') - >>> print traj - - - Returns - ------- - trajectory : md.Trajectory - The resulting trajectory, as an md.Trajectory object. - - See Also - -------- - mdtraj.XTCTrajectoryFile : Low level interface to XTC files - """ - from mdtraj.core.trajectory import _parse_topology - if top is None: - raise ValueError('"top" argument is required for load_xtc') - - if not isinstance(filename, (str, os.PathLike)): - raise TypeError('filename must be of type path-like for load_xtc. ' - 'you supplied %s' % type(filename)) - - topology = _parse_topology(top) - atom_indices = cast_indices(atom_indices) - - with XTCTrajectoryFile(str(filename), 'r') as f: - if frame is not None: - f.seek(frame) - n_frames = 1 - else: - n_frames = None - - return f.read_as_traj(topology, n_frames=n_frames, stride=stride, - atom_indices=atom_indices) - - -cdef class XTCTrajectoryFile(object): - """XTCTrajectoryFile(filenamee, mode='r', force_overwrite=True, **kwargs) - - Interface for reading and writing to a GROMACS XTC file. - This is a file-like objec that supports both reading and writing. - It also supports the context manager ptorocol, so you can use it - with the python 'with' statement. - - The conventional units in the XTC file are nanometers and picoseconds. - The format only supports saving coordinates, the time, the md step, - and the unit cell parametrs (box vectors) - - Parameters - ---------- - filename : str - The filename to open. A path to a file on disk. - mode : {'r', 'w'} - The mode in which to open the file, either 'r' for read or 'w' for write. - force_overwrite : bool - If opened in write mode, and a file by the name of `filename` already exists on disk, should we overwrite it? - - Other Parameters - ---------------- - min_chunk_size : int, default=100 - In read mode, we need to allocate a buffer in which to store the data - without knowing how many frames are in the file. This parameter is the - minimum size of the buffer to allocate. - chunk_size_multiplier : int, default=1.5 - In read mode, we need to allocate a buffer in which to store the data without knowing how many frames are in - the file. We can *guess* this information based on the size of the file on disk, but it's not perfect. This - parameter inflates the guess by a multiplicative factor. - - Examples - -------- - >>> # read the data from from an XTC file - >>> with XTCTrajectoryFile('traj.xtc') as f: - >>> xyz, time, step, box = f.read() - - >>> # write some random coordinates to an XTC file - >>> with XTCTrajectoryFile('output.xtc', 'w') as f: - >>> f.write(np.random.randn(10,1,3)) - - See Also - -------- - mdtraj.load_xtc : High-level wrapper that returns a ``md.Trajectory`` - """ - cdef xdrlib.XDRFILE* fh - cdef str filename - cdef int n_atoms # number of atoms in the file - cdef int64_t n_frames # number of frames in the file, cached - cdef int64_t frame_counter # current position in the file, in read mode - cdef char is_open # is the file handle currently open? - cdef int64_t approx_n_frames # appriximate number of frames in the file, as guessed based on its size - cdef char* mode # mode in which the file is open, either 'r' or 'w' - cdef int min_chunk_size - cdef float chunk_size_multiplier - cdef char with_unitcell # used in mode='w' to know if we're writing unitcells or nor - cdef readonly char* distance_unit - cdef np.ndarray _offsets - - def __cinit__(self, char* filename, char* mode='r', force_overwrite=True, **kwargs): - """Open a GROMACS XTC file for reading/writing. - """ - self.distance_unit = 'nanometers' - self.is_open = False - self.frame_counter = 0 - self.n_frames = -1 # means unknown - self.filename = filename - self._offsets = None - - if str(mode) == 'r': - self.n_atoms = 0 - if not os.path.exists(filename): - raise IOError("The file '%s' doesn't exist" % filename) - xdrlib.read_xtc_natoms(filename, &self.n_atoms) - if self.n_atoms <= 0: - raise IOError('Malformed XTC file. Number of atoms <= 0. ' - 'Are you sure this is a valid GROMACS xtc file?') - - self.fh = xdrlib.xdrfile_open(filename, b'r') - if self.fh is NULL: - raise IOError('File not found: "%s"' % filename) - self.approx_n_frames = self._estimate_n_frames_from_filesize(os.stat(filename).st_size) - - self.min_chunk_size = max(kwargs.pop('min_chunk_size', 100), 1) - self.chunk_size_multiplier = max(kwargs.pop('chunk_size_multiplier', 1.5), 0.01) - - elif str(mode) == 'w': - if force_overwrite and os.path.exists(filename): - os.unlink(filename) - if not force_overwrite and os.path.exists(filename): - raise IOError('"%s" already exists' % filename) - self.fh = xdrlib.xdrfile_open(str(filename), 'w') - if self.fh is NULL: - raise IOError('Unable to open file "%s"' % filename) - else: - raise ValueError('mode must be one of "r" or "w". ' - 'you supplied %s' % mode) - - for key in kwargs.keys(): - warnings.warn('kwarg "%s" was not recognized or processed' % key) - - self.is_open = True - self.mode = mode - - def _estimate_n_frames_from_filesize(self, filesize): - # model: size(bytes) = coefs_[0] * n_frames + coefs_[1]*n_atoms - # + coefs_[2] * n_frames * n_atoms - # + intercept - # fit on a small training set with a few hundred frames - coefs_ = [9.93733050e+01, -6.49891780e-02, 4.74462831e+00] - intercept_ = 5 - - approx_n_frames = (filesize - intercept_ - - coefs_[1]*self.n_atoms) / (coefs_[2] * self.n_atoms + - coefs_[0]) - - return max(approx_n_frames, 1) - - def __dealloc__(self): - self.close() - - def close(self): - "Close the XTC file handle" - if self.is_open: - xdrlib.xdrfile_close(self.fh) - self.is_open = False - - def read(self, n_frames=None, stride=None, atom_indices=None): - """read(n_frames=None, stride=None, atom_indices=None) - - Read data from an XTC file - - Parameters - ---------- - n_frames : int, None - The number of frames you would like to read from the file. - If None, all of the remaining frames will be loaded. - stride : int, optional - Read only every stride-th frame. - atom_indices : array_like, optional - If not none, then read only a subset of the atoms coordinates from the - file. This may be slightly slower than the standard read because it required - an extra copy, but will save memory. - - Returns - ------- - xyz : np.ndarray, shape=(n_frames, n_atoms, 3), dtype=np.float32 - The cartesian coordinates, in nanometers - time : np.ndarray, shape=(n_frames), dtype=np.float32 - The simulation time, in picoseconds, corresponding to each frame - step : np.ndarray, shape=(n_frames), dtype=np.int32 - The step in the simulation corresponding to each frame - box : np.ndarray, shape=(n_frames, 3, 3), dtype=np.float32 - The box vectors in each frame. - status: int - either _EXDROK or _EXDRENDOFFILE - - See Also - -------- - read_as_traj : Returns a Trajectory object - """ - if not str(self.mode) == 'r': - raise ValueError('read() is only available when file is opened in mode="r"') - if not self.is_open: - raise IOError('file must be open to read from it.') - stride = int(stride) if stride is not None else 1 - if n_frames is not None: - # if they supply the number of frames they want, that's easy - if not int(n_frames) == n_frames: - raise ValueError('n_frames must be an int, you supplied "%s"' % n_frames) - xyz, time, step, box, status = self._read(int(n_frames), atom_indices, stride) - if np.all(np.logical_and(box < 1e-10, box > -1e-10)): - box = None - return xyz, time, step, box - - # if they want ALL of the remaining frames, we need to guess at the - # chunk size, and then check the exit status to make sure we're really - # at the EOF - all_xyz, all_time, all_step, all_box = [], [], [], [] - status = _EXDROK - while status == _EXDROK: - # guess the size of the chunk to read, based on how many frames we - # think are in the file and how many we've currently read - chunk = max(abs(int((self.approx_n_frames - self.frame_counter) * self.chunk_size_multiplier / stride)), - self.min_chunk_size) - xyz, time, step, box, status = self._read(chunk, atom_indices, stride) - - all_xyz.append(xyz) - all_time.append(time) - all_step.append(step) - all_box.append(box) - - if len(all_xyz) == 0: - return np.empty(0), np.empty(0), np.empty(0), np.empty(0) - all_xyz = np.concatenate(all_xyz) - all_time = np.concatenate(all_time) - all_step = np.concatenate(all_step) - all_box = np.concatenate(all_box) - if np.all(np.logical_and(all_box < 1e-10, all_box > -1e-10)): - all_box = None - return all_xyz, all_time, all_step, all_box - - def _read(self, int64_t n_frames, atom_indices, stride): - """Read a specified number of XTC frames from the buffer""" - cdef int64_t n_read_frames = 0 - cdef int status = _EXDROK - cdef int status_seek = _EXDROK - cdef unsigned int n_atoms_to_read - cdef char efficient_striding = stride > 1 and self._offsets is not None - - if atom_indices is None: - n_atoms_to_read = self.n_atoms - elif isinstance(atom_indices, slice): - n_atoms_to_read = len(np.arange(self.n_atoms)[atom_indices]) - else: - atom_indices = np.asarray(atom_indices) - if min(atom_indices) < 0: - raise ValueError('atom_indices should be zero indexed. you gave an index less than zero') - if max(atom_indices) >= self.n_atoms: - raise ValueError('atom indices should be zero indexed. you gave an index bigger than the number of atoms') - n_atoms_to_read = len(atom_indices) - - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] xyz = \ - np.empty((n_frames, n_atoms_to_read, 3), dtype=np.float32) - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] time = \ - np.empty(n_frames, dtype=np.float32) - cdef np.ndarray[ndim=1, dtype=np.int32_t, mode='c'] step = \ - np.empty(n_frames, dtype=np.int32) - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] box = \ - np.empty((n_frames, 3, 3), dtype=np.float32) - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] prec = \ - np.empty(n_frames, dtype=np.float32) - - # only used if atom_indices is given - cdef np.ndarray[dtype=np.float32_t, ndim=2] framebuffer - if atom_indices is not None: - framebuffer = np.empty((self.n_atoms, 3), dtype=np.float32) - - # striding dummy, only used if efficient_striding is false or at the end of the file. - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] xyz_stride - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] time_stride - cdef np.ndarray[ndim=1, dtype=np.int32_t, mode='c'] step_stride - cdef np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] box_stride - cdef np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] prec_stride - - if stride > 1 and not efficient_striding: - xyz_stride = np.empty((1, self.n_atoms, 3), dtype=np.float32) - time_stride = np.empty(1, dtype=np.float32) - step_stride = np.empty(1, dtype=np.int32) - box_stride = np.empty((1, 3, 3), dtype=np.float32) - prec_stride = np.empty(1, dtype=np.float32) - - while (n_read_frames < n_frames) and (status != _EXDRENDOFFILE): - if atom_indices is None: - status = xdrlib.read_xtc(self.fh, self.n_atoms, &step[n_read_frames], - &time[n_read_frames], &box[n_read_frames,0,0], &xyz[n_read_frames,0,0], &prec[n_read_frames]) - else: - status = xdrlib.read_xtc(self.fh, self.n_atoms, &step[n_read_frames], - &time[n_read_frames], &box[n_read_frames,0,0], &framebuffer[0,0], &prec[n_read_frames]) - xyz[n_read_frames, :, :] = framebuffer[atom_indices, :] - - if status != _EXDRENDOFFILE and status != _EXDROK: - raise RuntimeError('XTC read error: %s' % _EXDR_ERROR_MESSAGES.get(status, 'unknown')) - else: - # we have successfully read a frame! - n_read_frames += 1 - - if stride > 1: - # Can we seek within bounds? - if efficient_striding: - if self.frame_counter + stride < len(self): - self.seek(stride, whence=1) - else: - # deliberately dont set eof status as last frame is valid! - status = _EXDRENDOFFILE - n_read_frames += 1 - else: - for _ in range(stride - 1): - seek_status = xdrlib.read_xtc(self.fh, self.n_atoms, &step_stride[0], - &time_stride[0], &box_stride[0,0,0], - &xyz_stride[0,0,0], &prec_stride[0]) - if seek_status != _EXDROK: - break - - if status == _EXDRENDOFFILE: - xyz = xyz[:n_read_frames-1] - box = box[:n_read_frames-1] - time = time[:n_read_frames-1] - step = step[:n_read_frames-1] - - # if we are using seek, the frame_counter already points to the right absolute position, - # otherwise we increment the counter relatively - if not efficient_striding: - self.frame_counter += len(xyz) - - return xyz, time, step, box, status - - def write(self, xyz, time=None, step=None, box=None): - """write(xyz, time=None, step=None, box=None) - - Write data to an XTC file - - Parameters - ---------- - xyz : np.ndarray, dtype=np.float32, shape=(n_frames, n_atoms, 3) - The cartesian coordinates of the atoms, in nanometers - time : np.ndarray, dtype=float32, shape=(n_frames), optional - The simulation time corresponding to each frame, in picoseconds. - If not supplied, the numbers 0..n_frames will be written. - step : np.ndarray, dtype=int32, shape=(n_frames), optional - The simulation timestep corresponding to each frame, in steps. - If not supplied, the numbers 0..n_frames will be written - box : np.ndarray, dtype=float32, shape=(n_frames, 3, 3), optional - The periodic box vectors of the simulation in each frame, in nanometers. - If not supplied, the vectors (1,0,0), (0,1,0) and (0,0,1) will - be written for each frame. - """ - if str(self.mode) != 'w': - raise ValueError('write() is only available when the file is opened in mode="w"') - - # do typechecking, and then dispatch to the c level function - xyz = ensure_type(xyz, dtype=np.float32, ndim=3, name='xyz', can_be_none=False, - add_newaxis_on_deficient_ndim=True, warn_on_cast=False) - n_frames = len(xyz) - time = ensure_type(time, dtype=np.float32, ndim=1, name='time', can_be_none=True, - shape=(n_frames,), add_newaxis_on_deficient_ndim=True, - warn_on_cast=False) - step = ensure_type(step, dtype=np.int32, ndim=1, name='step', can_be_none=True, - shape=(n_frames,), add_newaxis_on_deficient_ndim=True, - warn_on_cast=False) - box = ensure_type(box, dtype=np.float32, ndim=3, name='box', can_be_none=True, - shape=(n_frames, 3, 3), add_newaxis_on_deficient_ndim=True, - warn_on_cast=False) - - if self.frame_counter == 0: - self.n_atoms = xyz.shape[1] - self.with_unitcell = (box is not None) - else: - if not self.n_atoms == xyz.shape[1]: - raise ValueError("This file has %d atoms, but you're now " - "trying to write %d atoms" % (self.n_atoms, xyz.shape[1])) - if self.with_unitcell and (box is None): - raise ValueError("The file that you're saving to expects each frame " - "to contain unitcell information, but you did not supply it.") - if (not self.with_unitcell) and (box is not None): - raise ValueError("The file that you're saving to was created without " - "unitcell information.") - - if time is None: - time = np.arange(0, n_frames, dtype=np.float32) - if step is None: - step = np.arange(0, n_frames, dtype=np.int32) - if box is None: - # make each box[i] be the all zeros, which indicates the lack of - # a unitcell - box = np.zeros((n_frames, 3, 3), dtype=np.float32) - - prec = 1000.0 * np.ones(n_frames, dtype=np.float32) - self._write(xyz, time, step, box, prec) - - def _write(self, np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] xyz not None, - np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] time not None, - np.ndarray[ndim=1, dtype=np.int32_t, mode='c'] step not None, - np.ndarray[ndim=3, dtype=np.float32_t, mode='c'] box not None, - np.ndarray[ndim=1, dtype=np.float32_t, mode='c'] prec not None): - - cdef int n_frames = len(xyz) - cdef int n_atoms = xyz.shape[1] - cdef int status, i - - # all same shape - assert n_frames == len(box) == len(step) == len(time) == len(prec) - for i in range(n_frames): - status = xdrlib.write_xtc(self.fh, n_atoms, step[i], time[i], &box[i, 0, 0], &xyz[i, 0, 0], prec[i]) - if status != _EXDROK: - raise RuntimeError('XTC write error: %s' % status) - - self.frame_counter += n_frames - return status - - def seek(self, int64_t offset, int whence=0): - """seek(offset, whence=0) - - Move to a new file position - - Parameters - ---------- - offset : int - A number of frames. - whence : {0, 1, 2} - 0: offset from start of file, offset should be >=0. - 1: move relative to the current position, positive or negative - 2: move relative to the end of file, offset should be <= 0. - Seeking beyond the end of a file is not supported - """ - cdef int status - cdef int64_t pos, absolute - - if str(self.mode) != 'r': - raise NotImplementedError('seek() only available in mode="r" currently') - if whence == 0 and offset >= 0: - absolute = offset - elif whence == 1: - absolute = offset + self.frame_counter - elif whence == 2 and offset <= 0: - raise NotImplementedError('offsets from the end are not supported yet') - else: - raise IOError('Invalid argument') - - if absolute < 0 or absolute >= len(self.offsets): - raise IOError('XTC Seek out of bounds: given absolute position: {}'.format(absolute)) - - pos = self.offsets[absolute] - status = xdrlib.xdr_seek(self.fh, pos, SEEK_SET) - if status != _EXDROK: - raise RuntimeError('XTC seek error: %s' % status) - self.frame_counter = absolute - - def _calc_len_and_offsets(self): - cdef int byte_offset, status - cdef int64_t n_frames, filesize - cdef np.ndarray[dtype=int64_t, ndim=1] offsets - - # restore old pos when done or in case of error - cdef int64_t old_pos = xdrlib.xdr_tell(self.fh) - - if self.n_atoms <= 9: - filesize = os.stat(self.filename).st_size - byte_offset = XTC_SHORT_HEADER_SIZE + XTC_SHORT_BYTES_PER_ATOM*self.n_atoms - assert filesize % byte_offset == 0, ("filesize(%i) not divideable" - " by bytes per frames(%i)" - % (filesize, byte_offset)) - n_frames = filesize // byte_offset - offsets = np.fromiter((i*byte_offset for i in range(n_frames)), - dtype=np.int64, count=n_frames) - else: - offsets = np.empty(self.approx_n_frames, dtype=np.int64) - assert len(offsets) >= 1 - - try: - # skip header - if xdrlib.xdr_seek(self.fh, XTC_HEADER_SIZE, SEEK_SET) != 0: - raise RuntimeError('could not skip header for file ' + self.filename) - - # init first byte_offset - status = xdrlib.xdrfile_read_int(&byte_offset, 1, self.fh) - if status == 0: - raise RuntimeError("error reading from first frame: %i" % status) - byte_offset += 3 - ((byte_offset + 3) % 0x04) - - n_frames = 1 - offsets[0] = 0 - resize = np.resize - while True: - # relative seek - status = xdrlib.xdr_seek(self.fh, byte_offset + XTC_HEADER_SIZE, SEEK_CUR) - if status != 0: - offset = byte_offset + XTC_HEADER_SIZE - last_pos = xdrlib.xdr_tell(self.fh) - raise RuntimeError("error during seek: status code " - "fseek=%s; offset=%s, last_pos=%s" - % (status, offset, last_pos)) - - # return value == # ints read, so we're finished - if xdrlib.xdrfile_read_int(&byte_offset, 1, self.fh) == 0: - break - - if n_frames == len(offsets): - new_len = int(ceil(len(offsets)*1.2)) - offsets = resize(offsets, new_len) - - offsets[n_frames] = xdrlib.xdr_tell(self.fh) - 4 - XTC_HEADER_SIZE - n_frames += 1 - - # truncate byte offset to next 32-bit boundary - byte_offset += 3 - ((byte_offset + 3) % 0x04) - finally: - xdrlib.xdr_seek(self.fh, old_pos, SEEK_SET) - - return n_frames, offsets[:n_frames] - - def tell(self): - """Current file position - - Returns - ------- - offset : int - The current frame in the file. - """ - if str(self.mode) != 'r': - raise NotImplementedError('tell() only available in mode="r" currently') - return int(self.frame_counter) - - @property - def offsets(self): - "get byte offsets from current xtc file" - if self._offsets is None: - self.n_frames, self._offsets = self._calc_len_and_offsets() - return self._offsets - - @offsets.setter - def offsets(self, offsets): - "set frame offsets" - self._offsets = offsets - - def __enter__(self): - "Support the context manager protocol" - return self - - def __exit__(self, *exc_info): - "Support the context manager protocol" - self.close() - - def __len__(self): - "Number of frames in the file" - if str(self.mode) != 'r': - raise NotImplementedError('len() only available in mode="r" currently') - if not self.is_open: - raise ValueError('I/O operation on closed file') - if self.n_frames == -1: - self.offsets # invokes _calc_len_and_offsets - return int(self.n_frames) - - def flush(self): - if str(self.mode) != 'w': - raise RuntimeError('could not flush file opened only for reading') - cdef int status = xdrlib.xdr_flush(self.fh) - if status != 0: - raise IOError('could not flush xtc file. Status: %s' % status) - diff --git a/MDANSE/MANIFEST.in b/MDANSE/MANIFEST.in index ac9a90105a..b7dee9e970 100644 --- a/MDANSE/MANIFEST.in +++ b/MDANSE/MANIFEST.in @@ -1,7 +1,5 @@ include LICENSE -recursive-include Extensions/qhull_lib *.h *c *.pxd -recursive-include Extensions/xtc *.h *c *.pxd recursive-include Src/MDANSE/Chemistry *.json recursive-include Src/MDANSE/Framework *.json diff --git a/MDANSE/Src/MDANSE/Chemistry/ChemicalEntity.py b/MDANSE/Src/MDANSE/Chemistry/ChemicalEntity.py deleted file mode 100644 index 70330d34d5..0000000000 --- a/MDANSE/Src/MDANSE/Chemistry/ChemicalEntity.py +++ /dev/null @@ -1,2919 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -from __future__ import annotations - -import abc -from ast import literal_eval -import collections -import copy -from typing import Union, TYPE_CHECKING, List, Tuple -import h5py -import numpy as np -from rdkit import Chem -from numpy.typing import NDArray -from MDANSE.Chemistry import ( - ATOMS_DATABASE, - MOLECULES_DATABASE, - NUCLEOTIDES_DATABASE, - RESIDUES_DATABASE, -) -from MDANSE.Mathematics.Geometry import superposition_fit, center_of_mass -from MDANSE.Chemistry.Databases import ResiduesDatabaseError, NucleotidesDatabaseError -from MDANSE.Mathematics.LinearAlgebra import delta, Quaternion, Tensor, Vector -from MDANSE.Mathematics.Transformation import Rotation, RotationTranslation, Translation -from MDANSE.MLogging import LOG - -if TYPE_CHECKING: - from MDANSE.Chemistry.Databases import ( - MoleculesDatabase, - NucleotidesDatabase, - ResiduesDatabase, - ) - from MDANSE.MolecularDynamics.Configuration import _Configuration - - -class UnknownAtomError(Exception): - pass - - -class InvalidMoleculeError(Exception): - def __init__(self, code): - self._message = "The atom {} is unknown".format(code) - - def __str__(self): - return self._message - - -class UnknownMoleculeError(Exception): - def __init__(self, code): - self._message = "The molecule {} is unknown".format(code) - - def __str__(self): - return self._message - - -class InconsistentAtomNamesError(Exception): - pass - - -class InvalidResidueError(Exception): - pass - - -class UnknownResidueError(Exception): - def __init__(self, code): - self._message = "The residue {} is unknown".format(code) - - def __str__(self): - return self._message - - -class InvalidVariantError(Exception): - pass - - -class InvalidNucleotideChainError(Exception): - pass - - -class InvalidPeptideChainError(Exception): - pass - - -class InvalidChemicalEntityError(Exception): - pass - - -class InconsistentChemicalSystemError(Exception): - pass - - -class ChemicalEntityError(Exception): - pass - - -class CorruptedFileError(Exception): - pass - - -class _ChemicalEntity(metaclass=abc.ABCMeta): - """Abstract base class for other chemical entities.""" - - def __init__(self): - self._parent = None - - self._name = "" - - def __getstate__(self): - return self.__dict__ - - def __setstate__(self, state): - self.__dict__ = state - - @property - @abc.abstractmethod - def atom_list(self): - pass - - @abc.abstractmethod - def copy(self): - pass - - @property - def full_name(self) -> str: - """The full name of this chemical entity, which includes the names of all parent chemical entities.""" - full_name = self.name - parent = self._parent - while parent is not None: - full_name = "{}.{}".format(parent.name, full_name) - parent = parent.parent - - return full_name - - @property - @abc.abstractmethod - def number_of_atoms(self): - pass - - @property - def parent(self): - return self._parent - - @parent.setter - def parent(self, parent): - self._parent = parent - - @property - def name(self): - return self._name - - @name.setter - def name(self, name): - self._name = name - - @abc.abstractmethod - def serialize(self, h5_file): - pass - - def group(self, name: str) -> list["Atom"]: - """ - Finds all atoms in this chemical entity that are a part of the provided group. - - :param name: The name of the group whose atoms are being searched for. - :type name: str - - :return: List of atoms that are a part of the provided group. - :rtype: list - """ - selected_atoms = [] - for at in self.atom_list: - if not hasattr(at, "groups"): - continue - - if name in at.groups: - selected_atoms.append(at) - - return selected_atoms - - def center_of_mass(self, configuration: _Configuration) -> NDArray[np.float64]: - """ - Determines the coordinates of the centre of mass of this chemical entity. - - :param configuration: The configuration corresponding to the chemical system whose part this chemical enitity is - :type configuration: any subclass of MDANSE.MolecularDynamics.Configuration._Configuration - - :return: The coordinates of the centre of mass of this chemical entity. - :rtype: numpy.ndarray - """ - coords = configuration["coordinates"] - masses = [ - ATOMS_DATABASE.get_atom_property(at.symbol, "atomic_weight") - for at in self.atom_list - ] - indices = [at.index for at in self.atom_list] - - return center_of_mass(coords[indices], masses) - - def centre_of_mass(self, configuration: _Configuration) -> NDArray[np.float64]: - """Wrapper around the :py:meth: `center_of_mass()` method.""" - return self.center_of_mass(configuration) - - @property - def mass(self) -> float: - """The mass of this chemical entity. This is the sum of all non-ghost atoms in this chemical entity.""" - return sum(self.masses) - - @property - def masses(self) -> list[float]: - """A list of masses of all non-ghost atoms within this chemical entity.""" - return [ - ATOMS_DATABASE.get_atom_property(at.symbol, "atomic_weight") - for at in self.atom_list - ] - - def find_transformation_as_quaternion( - self, conf1: _Configuration, conf2: Union[_Configuration, None] = None - ) -> tuple[Quaternion, Vector, Vector, float]: - """ - Finds a linear transformation that, when applied to this chemical entity with its coordinates defined in - configuration conf1, minimizes the RMS distance to the conformation in conf2. Alternatively, if conf2 is None, - a linear transformation from the current configuration of the ChemicalSystem to conf1 is returned. - - Unlike :py:method: `find_transformation()`, this method returns a Quaternion corresponding to the rotation. - Under the hood, this method calls :func: `MDANSE.Mathematics.Geometry.superposition_fit()`. - - :param conf1: the configuration which is considered the initial configuration for the transformation if conf2 is - set, and the final configuration if it is not - :type conf1: :class: `~MDANSE.MolecularDynamics.Configuration.Configuration` - - :param conf2: the configuration which is considered the target configuration of the transformation, or None - :type conf2: :class: `~MDANSE.MolecularDynamics.Configuration.Configuration` or NoneType - - :return: The quaternion corresponding to the rotation, vectors of the centres of mass of both configurations and - the minimum root-mean-square distance between the configurations. - :rtype: tuple of (:class: `MDANSE.Mathematics.LinearAlgebra.Quaternion`, - :class: `MDANSE.Mathematics.LinearAlgebra.Vector`, - :class: `MDANSE.Mathematics.LinearAlgebra.Vector`, - float) - """ - chemical_system = self.root_chemical_system - if chemical_system is None: - raise ChemicalEntityError( - "Only chemical entities which are a part of a ChemicalSystem can be transformed." - f' The provided chemical entity, "{str(self)}", has its top level parent ' - f"{str(self.top_level_chemical_entity)} which is not a part of any ChemicalSystem" - f".\nFull chemical entity information: {repr(self)}" - ) - - if chemical_system.configuration.is_periodic: - raise ValueError( - "superposition in periodic configurations is not defined, therefore the configuration of " - "the root chemical system of this chemical entity must not be periodic." - ) - - if conf1.chemical_system != chemical_system: - raise ValueError( - "conformations come from different chemical systems: the root chemical system of this " - f'chemical entity is "{chemical_system.name}" but the chemical system registered with the ' - f'provided configuration (conf1) is "{conf1.chemical_system.name}".\nRoot chemical system:' - f" {repr(chemical_system)}\nConfiguration chemical system: {repr(conf1.chemical_system)}" - ) - - if conf2 is None: - conf2 = conf1 - conf1 = chemical_system.configuration - else: - if conf2.chemical_system != chemical_system: - raise ValueError( - "conformations come from different chemical systems: the root chemical system of this" - f' chemical entity is "{chemical_system.name}" but the chemical system registered with' - f' the provided configuration (conf2) is "{conf2.chemical_system.name}".\nRoot ' - f"chemical system: {repr(chemical_system)}\nConfiguration chemical system: " - f"{repr(conf2.chemical_system)}" - ) - - weights = chemical_system.masses - - return superposition_fit( - [ - ( - weights[a.index], - Vector(*conf1["coordinates"][a.index, :]), - Vector(*conf2["coordinates"][a.index, :]), - ) - for a in self.atom_list - ] - ) - - def find_transformation( - self, conf1: _Configuration, conf2: _Configuration = None - ) -> tuple[RotationTranslation, float]: - """ - Finds a linear transformation that, when applied to this chemical entity with its coordinates defined in - configuration conf1, minimizes the RMS distance to the conformation in conf2. Alternatively, if conf2 is None, - a linear transformation from the current configuration of the ChemicalSystem to conf1 is returned. - - It uses the :method: `find_transformation_as_quaternion()` to do this, and then transforms its results to return - an :class: `MDANSE.Mathematics.Transformation.RotationTranslation` object. - - :param conf1: the configuration in which this chemical entity is - :type conf1: :class:`~MDANSE.MolecularDynamics.Configuration.Configuration` - ` - :param conf1: the configuration which is considered the initial configuration for the transformation if conf2 is - set, and the final configuration if it is not - :type conf1: :class: `~MDANSE.MolecularDynamics.Configuration.Configuration` - - :param conf2: the configuration which is considered the target configuration of the transformation, or None - :type conf2: :class: `~MDANSE.MolecularDynamics.Configuration.Configuration` or NoneType - - :returns: the linear transformation corresponding to the transformation from conf1 to conf2, or from current - configuration to conf1, and the minimum root-mean-square distance - :rtype: tuple of (:class: `MDANSE.Mathematics.Transformation.RotationTranslation`, float) - """ - q, cm1, cm2, rms = self.find_transformation_as_quaternion(conf1, conf2) - return Translation(cm2) * q.asRotation() * Translation(-cm1), rms - - def center_and_moment_of_inertia( - self, configuration: _Configuration - ) -> tuple[Vector, Tensor]: - """ - Calculates the centre of masses and the inertia tensor of this chemical entity. - - :param configuration: a configuration that contains coordinates of this chemical entity - :type configuration: :class:`~MDANSE.MolecularDynamics.Configuration.Configuration` - - :returns: the center of mass and the moment of inertia tensor in the given configuration - :rtype: tuple of (:class: `MDANSE.Mathematics.LinearAlgebra.Vector`, - :class: `MDANSE.Mathematics.LinearAlgebra.Tensor`) - """ - - m = 0.0 - mr = Vector(0.0, 0.0, 0.0) - t = Tensor(3 * [3 * [0.0]]) - for atom in self.atom_list: - ma = ATOMS_DATABASE.get_atom_property(atom.symbol, "atomic_weight") - r = Vector(configuration["coordinates"][atom.index, :]) - m += ma - mr += ma * r - t += ma * r.dyadic_product(r) - cm = mr / m - t -= m * cm.dyadic_product(cm) - t = t.trace() * delta - t - return cm, t - - def centre_and_moment_of_inertia( - self, configuration: _Configuration - ) -> tuple[Vector, Tensor]: - """Wrapper around :meth: `center_and_moment_of_inertia()`.""" - return self.center_and_moment_of_inertia(configuration) - - def normalizing_transformation( - self, configuration: _Configuration, representation: str = None - ) -> RotationTranslation: - """ - Calculate a linear transformation that shifts the center of mass of the object to the coordinate origin and - makes its principal axes of inertia parallel to the three coordinate axes. - - :param configuration: a configuration that contains coordinates of this chemical entity - :type configuration: :class:`~MDANSE.MolecularDynamics.Configuration.Configuration` - - :param representation: the specific representation for axis alignment: - Ir : x y z <--> b c a - IIr : x y z <--> c a b - IIIr : x y z <--> a b c - Il : x y z <--> c b a - IIl : x y z <--> a c b - IIIl : x y z <--> b a c - :type representation: str - - :returns: the normalizing transformation - :rtype: :class: `MDANSE.Mathematics.Transformation.RotationTranslation` - """ - - cm, inertia = self.center_and_moment_of_inertia(configuration) - ev, diag = np.linalg.eig(inertia.array) - diag = np.transpose(diag) - if np.linalg.det(diag) < 0: - diag[0] = -diag[0] - - if representation is not None: - seq = np.argsort(ev) - if representation == "Ir": - seq = np.array([seq[1], seq[2], seq[0]]) - elif representation == "IIr": - seq = np.array([seq[2], seq[0], seq[1]]) - elif representation == "IIIr": - pass - elif representation == "Il": - seq = seq[2::-1] - elif representation == "IIl": - seq[1:3] = np.array([seq[2], seq[1]]) - elif representation == "IIIl": - seq[0:2] = np.array([seq[1], seq[0]]) - else: - raise ValueError( - f"invalid input for parameter repr: a value of {repr(representation)} was provided, " - 'but only the following values are accepted: "Ir", "IIr", "IIIr", "Il", "IIl", "IIIl"' - ) - diag = np.take(diag, seq) - - return Rotation(diag) * Translation(-cm) - - @property - def root_chemical_system(self) -> Union["ChemicalSystem", None]: - """ - The :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` of which part this chemical entity is, or None if - it is not a part of one. - """ - if isinstance(self, ChemicalSystem): - return self - else: - try: - return self._parent.root_chemical_system - except AttributeError: - return None - - @property - def top_level_chemical_entity(self) -> "_ChemicalEntity": - """ - The ultimate non-system parent of this chemical entity, i.e. the parent of a parent etc. until an entity that - is directly a child of a :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem`, wherein the child is returned. - If this chemical entity is not a part of a ChemicalSystem, the entity whose parent is None is returned. - """ - if isinstance(self._parent, ChemicalSystem): - return self - else: - try: - return self._parent.top_level_chemical_entity - except AttributeError: - return self - - @property - @abc.abstractmethod - def total_number_of_atoms(self): - pass - - -class Atom(_ChemicalEntity): - """A representation of atom in a trajectory.""" - - def __init__( - self, - symbol: str = "H", - name: str = None, - bonds: list["Atom"] = None, - groups: list[str] = None, - ghost: bool = False, - **kwargs, - ): - """ - :param symbol: The chemical symbol of the Atom. It has to be registered in the ATOMS_DATABASE. - :type symbol: str - - :param name: The name of the Atom. If this is not provided, the symbol is used as the name as well - :type nameL str - - :param bonds: List of Atom objects that this Atom is chemically bonded to. - :type bonds: list - - :param groups: List of groups that this Atom is a part of, e.g. sidechain. - :type groups: list - - :param ghost: - :type ghost: bool - - :param kwargs: Any additional parameters that should be set during instantiation. - """ - - super(Atom, self).__init__() - - self._symbol = symbol - - if self._symbol not in ATOMS_DATABASE: - raise UnknownAtomError("The atom {} is unknown".format(self.symbol)) - - self._name = name if name else symbol - - self._bonds = bonds if bonds else [] - - self._groups = groups if groups else [] - - self._ghost = ghost - - self._index = kwargs.pop("index", None) - - self._parent = None - - self.element = ATOMS_DATABASE.get_atom_property(self._symbol, "element") - - for k, v in kwargs.items(): - try: - setattr(self, k, v) - except AttributeError: - raise AttributeError( - f"Could not set attribute {k} to value {v}, probably because this is a protected " - f"attribute of this class." - ) - - def __hash__(self) -> int: - text = self._symbol - number = self._index - temp = text + "_" + str(number) - return temp.__hash__() - - def copy(self) -> "Atom": - """ - Creates a copy of the current instance of this class. - - :return: A copy of the current instance. - :rtype: MDANSE.Chemistry.ChemicalEntity.Atom - """ - - a = Atom(symbol=self._symbol) - - for k, v in self.__dict__.items(): - setattr(a, k, v) - - a._bonds = [bat.name for bat in self._bonds] - - return a - - def restore_bonds(self, atom_dict: dict[str, Atom]) -> List[Tuple[int, int]]: - """After copying, the Atom._bonds is filled with atom NAMES. - This method uses a dictionary of name: Atom pairs to - replace the names with Atom instances. - Trying to copy the Atom instances directly will result - in infinite recursion. - - Arguments: - atom_dict -- dictionary of str: atom pairs, - where the key is the name of the Atom instance - """ - new_bonds = [atom_dict[atm.name] for atm in self._bonds] - self._bonds = new_bonds - return [(self.index, other.index) for other in self._bonds] - - def __eq__(self, other): - if not isinstance(other, Atom): - return False - if not self._index == other._index: - return False - if not self._symbol == other._symbol: - return False - if not self._name == other._name: - return False - return True - - def __getitem__(self, item): - return getattr(self, item) - - def __getstate__(self): - return self.__dict__ - - def __setstate__(self, state): - self.__dict__ = state - - def __str__(self): - return self.full_name - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if key == "bonds": - bonds = ", ".join( - [ - f'Atom({atom.name if hasattr(atom, "name") else atom})' - for atom in self.bonds - ] - ) - contents += f"bonds=[{bonds}]" - elif isinstance(value, _ChemicalEntity): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}({value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.Chemistry.ChemicalEntity.Atom({contents[:-2]})" - - @property - def atom_list(self) -> list["Atom"]: - return [self] if not self.ghost else [] - - @property - def total_number_of_atoms(self) -> int: - return 1 - - @property - def number_of_atoms(self) -> int: - return int(self.ghost) - - @property - def bonds(self) -> list["Atom"]: - """A list of atoms to which this atom is chemically bonded.""" - return self._bonds - - @bonds.setter - def bonds(self, bonds: list["Atom"]) -> None: - self._bonds = bonds - - @property - def ghost(self) -> bool: - return self._ghost - - @ghost.setter - def ghost(self, ghost: bool) -> None: - self._ghost = ghost - - @property - def groups(self) -> list[str]: - """A list of groups to which this atom belongs, e.g. sidechain.""" - return self._groups - - @property - def index(self) -> int: - """The index of the atom in the trajectory. Once set, it cannot be changed anymore.""" - return self._index - - @index.setter - def index(self, index: int) -> None: - if self._index is not None: - return - self._index = index - - @property - def name(self) -> str: - """The full name of the atom, e.g. Hydrogen.""" - return self._name - - @name.setter - def name(self, name: str) -> None: - self._name = name - - @property - def symbol(self) -> str: - """The chemical symbol of the atom. The symbol must be registered in ATOMS_DATABASE.""" - return self._symbol - - @symbol.setter - def symbol(self, symbol: str) -> None: - if symbol not in ATOMS_DATABASE: - raise UnknownAtomError("The atom {} is unknown".format(symbol)) - - self._symbol = symbol - - @classmethod - def build( - cls, - h5_contents: Union[None, dict[str, list[list[str]]]], - symbol: str, - name: str, - index: str, - ghost: bool, - ) -> Atom: - """ - Creates an instance of the Atom class. This method is meant to be used when loading a trajectory from disk and - so may be called when the :class: `ChemicalSystem`.load() method is called. - - :param h5_contents: A parameter present solely for compatibility with other classes' build() methods. It is not - used. - :type h5_contents: None or dict - - :param symbol: The chemical symbol of the Atom. It has to be registered in the ATOMS_DATABASE. - :type symbol: str - - :param name: The name of the Atom. If this is not provided, the symbol is used as the name as well - :type name: str - - :param index: The unique atom index. Since AtomCluster saves it, Atom must save it too. - :type index: str - - :param ghost: - :type ghost: bool - """ - return cls(symbol=symbol, name=name, index=int(index), ghost=ghost) - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the Atom object into a string in preparation of the object being stored on disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'atoms' and the index of the serialization data of this Atom in the - provided dictionary. - :rtype: tuple - """ - h5_contents.setdefault("atoms", []).append( - [repr(self.symbol), repr(self.name), str(self.index), str(self.ghost)] - ) - - return "atoms", len(h5_contents["atoms"]) - 1 - - -class AtomGroup(_ChemicalEntity): - """ - An arbitrary selection of atoms that belong to the same chemical system. Unlike in Molecule and AtomCluster, the - atoms in an AtomGroup do not have to be related in any way other than that they have to exist in the same system. - Further, AtomGroup does not have the serialize() or the build() method defined, and so it will not be saved on disk - and cannot be loeaded from disk. - """ - - def __init__(self, atoms: list[Atom]): - """ - - :param atoms: The list of atoms that form this AtomGroup - :type atoms: list - """ - super(AtomGroup, self).__init__() - - s = set([at.root_chemical_system for at in atoms]) - if len(s) != 1: - raise ChemicalEntityError("The atoms comes from different chemical systems") - - self._atoms = atoms - - self._chemical_system = list(s)[0] - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, _ChemicalEntity) and not isinstance(value, Atom): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}({value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.MolecularDynamics.ChemicalEntity.AtomGroup({contents[:-2]})" - - def __str__(self): - return f"AtomGroup consisting of {self.total_number_of_atoms} atoms" - - @property - def atom_list(self) -> list[Atom]: - """The list of all non-ghost atoms in the AtomGroup.""" - return list([at for at in self._atoms if not at.ghost]) - - def copy(self) -> None: - """The copy method is not defined for the AtomGroup class; instances of it cannot be copied.""" - pass - - @property - def number_of_atoms(self) -> int: - """The number of all non-ghost atoms in the AtomGroup.""" - return len([at for at in self._atoms if not at.ghost]) - - @property - def total_number_of_atoms(self) -> int: - """The total number of atoms in the AtomGroup, including ghosts.""" - return len(self._atoms) - - @property - def root_chemical_system(self) -> "ChemicalSystem": - """ - :return: The chemical system whose part all the atoms in this AtomGroup are. - :rtype: MDANSE.Chemistry.ChemicalEntity.ChemicalSystem - """ - return self._chemical_system - - def serialize(self, h5_contents: dict) -> None: - """The serialize method is not defined for the AtomGroup class; it cannot be saved to disk.""" - pass - - -class AtomCluster(_ChemicalEntity): - """A cluster of atoms.""" - - def __init__(self, name: str, atoms: list[Atom], parentless: bool = False): - """ - :param name: The name of the AtomCluster. - :type name: str - - :param atoms: List of atoms that this AtomCluster consists of. - :type atoms: list - - :param parentless: Determines whether the AtomCluster is the parent of its constituent Atoms. It is if - parentless is False. - :type parentless: bool - """ - super(AtomCluster, self).__init__() - - self._name = name - - self._parentless = parentless - - self._atoms = [] - for at in atoms: - if not parentless: - at._parent = self - self._atoms.append(at) - - def __getitem__(self, item: int) -> Atom: - return self._atoms[item] - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, _ChemicalEntity) and not isinstance(value, Atom): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}({value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.MolecularDynamics.ChemicalEntity.AtomCluster({contents[:-2]})" - - def __str__(self): - return f"AtomCluster consisting of {self.total_number_of_atoms} atoms" - - @property - def atom_list(self) -> list[Atom]: - """A list of all non-ghost atoms in the AtomCluster.""" - return list([at for at in self._atoms if not at.ghost]) - - def copy(self) -> "AtomCluster": - """ - Copies the instance of AtomCluster into a new, identical instance. - :return: An identical copy of the AtomCluster instance. - :rtype: MDANSE.Chemistry.ChemicalEntity.AtomCluster - """ - atoms = [atom.copy() for atom in self._atoms] - - ac = AtomCluster(self._name, atoms, self._parentless) - - if not self._parentless: - for at in ac._atoms: - at._parent = ac - - return ac - - @property - def number_of_atoms(self) -> int: - """The number of non-ghost atoms in the cluster.""" - return len([at for at in self._atoms if not at.ghost]) - - @property - def total_number_of_atoms(self) -> int: - """The total number of atoms in the cluster, including ghosts.""" - return len(self._atoms) - - def reorder_atoms(self, atoms: list[str]) -> None: - """ - Change the order in which the atoms in this cluster are stored. - - :param atoms: A list of atoms. The atoms in the AtomCluster will be reordered to be in the same order as provided. - :type atoms: list of strings - """ - if set(atoms) != set([at.name for at in self._atoms]) or len(atoms) != len( - self._atoms - ): - raise InconsistentAtomNamesError( - "The set of atoms to reorder is inconsistent with molecular contents: " - f"the provided atoms ({atoms}) are different from the atoms in the " - f"AtomCluster ({[at.name for at in self._atoms]})" - ) - - reordered_atoms = [] - for at in atoms: - for i, registered_atom in enumerate(self._atoms): - if at == registered_atom.name: - reordered_atoms.append(registered_atom) - self._atoms.pop(i) - break - - self._atoms = reordered_atoms - - @classmethod - def build( - cls, h5_contents: dict[str, list[list[str]]], atom_indexes: list[int], name: str - ) -> AtomCluster: - """ - Creates an instance of the AtomCluster class. This method is meant to be used when loading a trajectory from - disk and so may be called when the :class: `ChemicalSystem`.load() method is called. Please note that an - AtomCluster made with this method has parentless set to False. - - :param h5_contents: The contents of an MDANSE HDF5 Trajectory describing a chemical system. Obtained by calling - the :class: `ChemicalSystem`.load() method. - :type h5_contents: dict - - :param atom_indexes: List of indexes of atoms that compose this AtomGroup, pointing to `h5_contents['atoms']` - :type atom_indexes: list - - :param name: The name of the AtomCluster. - :type name: str - - :param atoms: List of atoms that this AtomCluster consists of. - :type atoms: list - """ - contents = h5_contents["atoms"] - atoms = [] - for index in atom_indexes: - args = [literal_eval(arg.decode("utf8")) for arg in contents[index]] - atoms.append(Atom.build(None, *args)) - - return cls(name, atoms) - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the AtomCluster object and its contents into strings in preparation of the object being stored on - disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'atom_clusters' and the index of the serialization data of this - AtomCluster in the provided dictionary. - :rtype: tuple - """ - if "atoms" in h5_contents: - at_indexes = list( - range( - len(h5_contents["atoms"]), - len(h5_contents["atoms"]) + len(self._atoms), - ) - ) - else: - at_indexes = list(range(len(self._atoms))) - - h5_contents.setdefault("atom_clusters", []).append( - [str(at_indexes), repr(self._name)] - ) - - for at in self._atoms: - at.serialize(h5_contents) - - return "atom_clusters", len(h5_contents["atom_clusters"]) - 1 - - -class Molecule(_ChemicalEntity): - """A group of atoms that form a molecule.""" - - def __init__(self, code: str, name: str): - """ - :param code: A code representing a molecule in the :class: `MoleculesDatabase` - :type code: str - - :param name: The name of this Molecule. - :type name: str - """ - - super(Molecule, self).__init__() - - self._atoms = collections.OrderedDict() - - self._code = code - - self._name = name - - self._build(code) - - def _build(self, code: str) -> None: - for molname, molinfo in MOLECULES_DATABASE.items(): - if code == molname or code in molinfo["alternatives"]: - for at, atinfo in molinfo["atoms"].items(): - info = copy.deepcopy(atinfo) - atom = Atom(name=at, **info) - atom.parent = self - self._atoms[at] = atom - - self._set_bonds() - break - else: - raise UnknownMoleculeError(code) - - def __getitem__(self, item: str) -> Atom: - return self._atoms[item] - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, _ChemicalEntity) and not isinstance(value, Atom): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}({value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.MolecularDynamics.ChemicalEntity.Molecule({contents[:-2]})" - - def __str__(self): - return f'Molecule of {self.name} (database code "{self.code}")' - - def _set_bonds(self) -> None: - for atom in self._atoms.values(): - for i in range(len(atom.bonds)): - try: - atom.bonds[i] = self._atoms[atom.bonds[i]] - except KeyError: - continue - - @property - def atom_list(self) -> list[Atom]: - """The list of non-ghost atoms in the molecule.""" - return list([at for at in self._atoms.values() if not at.ghost]) - - @property - def code(self) -> str: - """The code corresponding to the name of the molecule, e.g. WAT for water.""" - return self._code - - def copy(self) -> "Molecule": - """Copies the instance of Molecule into a new, identical instance.""" - - m = Molecule(self._code, self._name) - - atom_names = [at for at in self._atoms] - - m.reorder_atoms(atom_names) - - for atname, at in m._atoms.items(): - at._parent = m - at._index = self._atoms[atname].index - - return m - - @property - def number_of_atoms(self) -> int: - """The number of non-ghost atoms in the molecule.""" - return len([at for at in self._atoms.values() if not at.ghost]) - - @property - def total_number_of_atoms(self) -> int: - """The total number of atoms in the molecule, including ghosts.""" - return len(self._atoms) - - def reorder_atoms(self, atoms: list[str]) -> None: - """ - Change the order in which the atoms in this molecule are stored. - - :param atoms: A list of atoms. The atoms in the Molecule will be reordered to be in the same order as provided. - :type atoms: list of strings - """ - - if set(atoms) != set(self._atoms.keys()): - raise InconsistentAtomNamesError( - "The set of atoms to reorder is inconsistent with molecular contents" - ) - - reordered_atoms = collections.OrderedDict() - for at in atoms: - reordered_atoms[at] = self._atoms[at] - - self._atoms = reordered_atoms - - @classmethod - def build( - cls, - h5_contents: dict[str, list[list[str]]], - atom_indexes: list[int], - code: str, - name: str, - ) -> Molecule: - """ - Creates an instance of the Molecule class. This method is meant to be used when loading a trajectory from - disk and so may be called when the :class: `ChemicalSystem`.load() method is called. - - :param h5_contents: The contents of an MDANSE HDF5 Trajectory describing a chemical system. Obtained by calling - the :class: `ChemicalSystem`.load() method. - :type h5_contents: dict - - :param atom_indexes: List of indexes of atoms that compose this Molecule, pointing to `h5_contents['atoms']` - :type atom_indexes: list - - :param code: A code representing a molecule in the :class: `MoleculesDatabase` - :type code: str - - :param name: The name of the Molecule. - :type name: str - """ - mol = cls(code, name) - contents = h5_contents["atoms"] - - names = [ - literal_eval(contents[index][1].decode("utf8")) for index in atom_indexes - ] - - mol.reorder_atoms(names) - - for at, index in zip(mol.atom_list, atom_indexes): - at.ghost = literal_eval(contents[index][3].decode("utf8")) - - return mol - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the Molecule object and its contents into strings in preparation of the object being stored on - disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'molecules' and the index of the serialization data of this Molecule - in the provided dictionary. - :rtype: tuple - """ - if "atoms" in h5_contents: - at_indexes = list( - range( - len(h5_contents["atoms"]), - len(h5_contents["atoms"]) + len(self._atoms), - ) - ) - else: - at_indexes = list(range(len(self._atoms))) - - h5_contents.setdefault("molecules", []).append( - [str(at_indexes), repr(self._code), repr(self._name)] - ) - - for at in self._atoms.values(): - at.serialize(h5_contents) - - return "molecules", len(h5_contents["molecules"]) - 1 - - -def is_molecule(name: str) -> bool: - """ - Checks if a given molecule is in the molecules database. - - :param name: A code or an alternative code (e.g. 'WAT' for water) corresponding to a molecule - :type name: str - - :return: Whether the provided molecule is in MOLECULES_DATABASE - :rtype: bool - """ - return name in MOLECULES_DATABASE - - -class Residue(_ChemicalEntity): - """A group of atoms that make up an amino acid.""" - - def __init__(self, code: str, name: str, variant: Union[str, None] = None): - """ - - :param code: A code corresponding to a residue in the residue database. - :type code: str - - :param name: A name for the residue. - :type name: str - - :param variant: - :type variant: str - """ - - super(Residue, self).__init__() - - for resname, resinfo in RESIDUES_DATABASE.items(): - if code == resname or code in resinfo["alternatives"]: - break - else: - raise UnknownResidueError(code) - - self._code = code - - self._name = name - - self._variant = variant - - if self._variant is not None: - try: - self._selected_variant = RESIDUES_DATABASE[self._variant] - except (KeyError, ResiduesDatabaseError): - raise InvalidVariantError( - "The variant {} is unknown".format(self._variant) - ) - else: - if ( - not self._selected_variant["is_n_terminus"] - and not self._selected_variant["is_c_terminus"] - ): - raise InvalidVariantError( - "The variant {} is not valid".format(self._variant) - ) - else: - self._selected_variant = None - - self._atoms = collections.OrderedDict() - - def __getitem__(self, item: str) -> Atom: - return self._atoms[item] - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, _ChemicalEntity) and not isinstance(value, Atom): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}({value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.MolecularDynamics.ChemicalEntity.Residue({contents[:-2]})" - - def __str__(self): - return f'Amino acid Residue {self.name} (database code "{self.code}")' - - def set_atoms(self, atoms: list[str]) -> None: - """ - Populates the Residue with the provided atoms using the data from RESIDUES_DATABASE. The input must correspond - to the data in RESIDUES_DATABSE for this residue. - - :param atoms: A list of atoms to populate the residue with - :type atoms: list of strings - - :return: None - """ - - replaced_atoms = set() - if self._selected_variant is not None: - for v in self._selected_variant["atoms"].values(): - replaced_atoms.update(v["replaces"]) - - res_atoms = set(RESIDUES_DATABASE[self._code]["atoms"]) - res_atoms.difference_update(replaced_atoms) - if self._selected_variant is not None: - res_atoms.update(self._selected_variant["atoms"]) - - if res_atoms != set(atoms): - raise InconsistentAtomNamesError( - "The set of atoms to reorder is inconsistent with residue contents" - ) - - if res_atoms != set(atoms): - raise InconsistentAtomNamesError( - "The set of atoms to reorder is inconsistent with residue contents" - ) - - d = copy.deepcopy(RESIDUES_DATABASE[self._code]["atoms"]) - if self._selected_variant is not None: - d.update(self._selected_variant["atoms"]) - - self._atoms.clear() - for at in atoms: - self._atoms[at] = Atom(name=at, **d[at]) - self._atoms[at].parent = self - - for at, atom in self._atoms.items(): - for i in range(len(atom.bonds) - 1, -1, -1): - if atom.bonds[i] in replaced_atoms: - del atom.bonds[i] - - try: - atom.bonds[i] = self._atoms[atom.bonds[i]] - except KeyError: - continue - - @property - def atom_list(self) -> list[Atom]: - """List of atoms in the Residue.""" - return list([at for at in self._atoms.values() if not at.ghost]) - - @property - def number_of_atoms(self) -> int: - """The number of non-ghost atoms in the residue.""" - return len([at for at in self._atoms.values() if not at.ghost]) - - @property - def total_number_of_atoms(self) -> int: - """The total number of atoms in the residue, including ghosts.""" - return len(self._atoms) - - @property - def code(self) -> str: - return self._code - - def copy(self) -> "Residue": - """Copies the instance of Residue into a new, identical instance.""" - r = Residue(self._code, self._name, self._variant) - atoms = [at for at in self._atoms] - r.set_atoms(atoms) - - for atname, at in r._atoms.items(): - at._parent = r - at._index = self._atoms[atname]._index - - return r - - @property - def variant(self): - return self._variant - - @classmethod - def build( - cls, - h5_contents: dict[str, list[list[str]]], - atom_indexes: list[int], - code: str, - name: str, - variant: Union[str, None], - ) -> Residue: - """ - Creates an instance of the Residue class. This method is meant to be used when loading a trajectory from - disk and so may be called when the :class: `ChemicalSystem`.load() method is called. - - :param h5_contents: The contents of an MDANSE HDF5 Trajectory describing a chemical system. Obtained by calling - the :class: `ChemicalSystem`.load() method. - :type h5_contents: dict - - :param atom_indexes: List of indexes of atoms that compose this Residue, pointing to `h5_contents['atoms']` - :type atom_indexes: list - - :param code: A code corresponding to a residue in the residue database. - :type code: str - - :param name: A name for the residue. - :type name: str - - :param variant: - :type variant: str - - :return: An instance of the Residue class. - :rtype: Residue - """ - res = cls(code, name, variant) - - names = [ - literal_eval(h5_contents["atoms"][index][1].decode("utf8")) - for index in atom_indexes - ] - res.set_atoms(names) - - return res - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the Residue object and its contents into strings in preparation of the object being stored on - disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'residues' and the index of the serialization data of this Residue - in the provided dictionary. - :rtype: tuple - """ - if "atoms" in h5_contents: - at_indexes = list( - range( - len(h5_contents["atoms"]), - len(h5_contents["atoms"]) + len(self._atoms), - ) - ) - else: - at_indexes = list(range(len(self._atoms))) - - h5_contents.setdefault("residues", []).append( - [str(at_indexes), repr(self._code), repr(self._name), repr(self._variant)] - ) - - for at in self._atoms.values(): - at.serialize(h5_contents) - - return "residues", len(h5_contents["residues"]) - 1 - - -class Nucleotide(_ChemicalEntity): - """A group of atoms that make up a nucleotide.""" - - def __init__(self, code: str, name: str, variant: Union[str, None] = None): - """ - :param code: A code corresponding to a nucleotide in the nucleotide database. - :type code: str - - :param name: A name for the residue. - :type name: str - - :param variant: - :type variant: str - """ - super(Nucleotide, self).__init__() - - for resname, resinfo in NUCLEOTIDES_DATABASE.items(): - if code == resname or code in resinfo["alternatives"]: - self._resname = resname - break - else: - raise UnknownResidueError(code) - - self._code = code - - self._name = name - - self._variant = variant - - if self._variant is not None: - try: - self._selected_variant = NUCLEOTIDES_DATABASE[self._variant] - except (KeyError, NucleotidesDatabaseError): - raise InvalidVariantError( - "The variant {} is unknown".format(self._variant) - ) - else: - if ( - not self._selected_variant["is_5ter_terminus"] - and not self._selected_variant["is_3ter_terminus"] - ): - raise InvalidVariantError( - "The variant {} is not valid".format(self._variant) - ) - else: - self._selected_variant = None - - self._atoms = collections.OrderedDict() - - def __getitem__(self, item: str) -> Atom: - return self._atoms[item] - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, _ChemicalEntity) and not isinstance(value, Atom): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}(name={value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.MolecularDynamics.ChemicalEntity.Nucleotide({contents[:-2]})" - - def __str__(self): - return f'Nucleotide {self.name} (database code "{self.code}")' - - def copy(self) -> "Nucleotide": - """Copies the instance of Nucleotide into a new, identical instance.""" - n = Nucleotide(self._code, self._name, self._variant) - atoms = [at for at in self._atoms] - n.set_atoms(atoms) - - for atname, at in n._atoms.items(): - at._parent = n - at._index = self._atoms[atname].index - - return n - - def set_atoms(self, atoms: list[str]) -> None: - """ - Populates the Nucleotide with the provided atoms using the data from NUCLEOTIDES_DATABASE. The input must - correspond to the data in NUCLEOTIDES_DATABASE for this nucleotide. - - :param atoms: A list of atoms to populate the nucleotide with - :type atoms: list of strings - - :return: None - """ - replaced_atoms = set() - if self._selected_variant is not None: - for v in self._selected_variant["atoms"].values(): - replaced_atoms.update(v["replaces"]) - - res_atoms = set(NUCLEOTIDES_DATABASE[self._resname]["atoms"]) - res_atoms.difference_update(replaced_atoms) - if self._selected_variant is not None: - res_atoms.update(self._selected_variant["atoms"]) - - if res_atoms != set(atoms): - difference = ( - res_atoms.difference(set(atoms)) - if len(res_atoms) > len(atoms) - else set(atoms).difference(res_atoms) - ) - raise InconsistentAtomNamesError( - "The set of atoms to reorder is inconsistent with residue contents.\n" - f"Expected: {res_atoms}\nActual: {set(atoms)}\nDifference: {difference}" - ) - - d = copy.deepcopy(NUCLEOTIDES_DATABASE[self._resname]["atoms"]) - if self._selected_variant is not None: - d.update(self._selected_variant["atoms"]) - - self._atoms.clear() - for at in atoms: - self._atoms[at] = Atom(name=at, **d[at]) - self._atoms[at].parent = self - - for at, atom in self._atoms.items(): - for i in range(len(atom.bonds) - 1, -1, -1): - if atom.bonds[i] in replaced_atoms: - del atom.bonds[i] - continue - else: - try: - atom.bonds[i] = self._atoms[atom.bonds[i]] - except KeyError: - continue - - @property - def atom_list(self) -> list[Atom]: - """List of all non-ghost atoms in the Nucleotide.""" - return list([at for at in self._atoms.values() if not at.ghost]) - - @property - def number_of_atoms(self) -> int: - """The number of non-ghost atoms in the residue.""" - return len([at for at in self._atoms.values() if not at.ghost]) - - @property - def total_number_of_atoms(self): - """The total number of atoms in the molecule, including ghosts.""" - return len(self._atoms) - - @property - def code(self): - return self._code - - @property - def variant(self): - return self._variant - - @classmethod - def build( - cls, - h5_contents: dict[str, list[list[str]]], - atom_indexes: list[int], - code: str, - name: str, - variant: Union[str, None], - ) -> Nucleotide: - """ - Creates an instance of the Nucleotide class. This method is meant to be used when loading a trajectory from - disk and so may be called when the :class: `ChemicalSystem`.load() method is called. - - :param h5_contents: The contents of an MDANSE HDF5 Trajectory describing a chemical system. Obtained by calling - the :class: `ChemicalSystem`.load() method. - :type h5_contents: dict - - :param atom_indexes: List of indexes of atoms that compose this Nucleotide, pointing to `h5_contents['atoms']` - :type atom_indexes: list - - :param code: A code corresponding to a residue in the residue database. - :type code: str - - :param name: A name for the residue. - :type name: str - - :param variant: - :type variant: str - - :return: An instance of the Nucleotide class. - :rtype: Nucleotide - """ - nucl = cls(code, name, variant) - - names = [ - literal_eval(h5_contents["atoms"][index][1].decode("utf8")) - for index in atom_indexes - ] - nucl.set_atoms(names) - - return nucl - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the Nucleotide object and its contents into strings in preparation of the object being stored on - disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'nucleotides' and the index of the serialization data of this Nucleotide - in the provided dictionary. - :rtype: tuple - """ - if "atoms" in h5_contents: - at_indexes = list( - range( - len(h5_contents["atoms"]), - len(h5_contents["atoms"]) + len(self._atoms), - ) - ) - else: - at_indexes = list(range(len(self._atoms))) - - h5_contents.setdefault("nucleotides", []).append( - [str(at_indexes), repr(self._code), repr(self._name), repr(self._variant)] - ) - - for at in self._atoms.values(): - at.serialize(h5_contents) - - return "nucleotides", len(h5_contents["nucleotides"]) - 1 - - -class NucleotideChain(_ChemicalEntity): - """A group of Nucleotide objects connected by bonds in a chain.""" - - def __init__(self, name: str): - """ - - :param name: A name for the chain. - :type name: str - """ - - super(NucleotideChain, self).__init__() - - self._name = name - - self._nucleotides = [] - - def __getitem__(self, item: int): - return self._nucleotides[item] - - def __str__(self): - return "NucleotideChain of {} nucleotides".format(len(self._nucleotides)) - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, _ChemicalEntity) and not isinstance(value, Nucleotide): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}(name={value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return ( - f"MDANSE.MolecularDynamics.ChemicalEntity.NucleotideChain({contents[:-2]})" - ) - - def _connect_nucleotides(self): - ratoms_in_first_residue = [ - at - for at in self._nucleotides[0].atom_list - if getattr(at, "o5prime_connected", False) - ] - if len(ratoms_in_first_residue) == 0: - raise InvalidNucleotideChainError( - "The first nucleotide in the chain must contain an atom that is connected" - " to the 5' terminal oxygen (O5'). This is signified by an atom having" - 'the "o5prime_connected" property set to True, and happens automatically' - 'if the nucleotide was created with variant="5T1", and the set_atoms()' - " method was called with the correct atoms.\nThe provided first " - f"nucleotide is {self._nucleotides[0].name}, its variant is " - f"{self._nucleotides[0].variant} and it contains the following atoms: " - f"{[at.name for at in self._nucleotides[0].atom_list]}. The full info" - f" on the first nucleotide is {repr(self._nucleotides[0])}." - ) - - try: - n_atom_in_first_residue = self._nucleotides[0]["O5'"] - except KeyError: - raise InvalidNucleotideChainError( - "The first nucleotide in the chain must contain 5' terminal oxygen atom" - " (O5'). The first nucleotide that was provided is " - f"{self._nucleotides[0].name} and contains only the following atoms: " - f"{[at.name for at in self._nucleotides[0].atom_list]}.\nThe full info" - f" on the first nucleotide is {repr(self._nucleotides[0])}." - ) - n_atom_in_first_residue.bonds.extend(ratoms_in_first_residue) - - ratoms_in_last_residue = [ - at - for at in self._nucleotides[-1].atom_list - if getattr(at, "o3prime_connected", False) - ] - if len(ratoms_in_last_residue) == 0: - raise InvalidNucleotideChainError( - "The last nucleotide in the chain must contain an atom that is connected" - " to the 3' terminal oxygen (O3'). This is signified by an atom having" - 'the "o3prime_connected" property set to True, and happens automatically' - 'if the nucleotide was created with variant="3T1", and the set_atoms()' - " method was called with the correct atoms.\nThe provided first " - f"nucleotide is {self._nucleotides[-1].name}, its variant is " - f"{self._nucleotides[0].variant} and it contains the following atoms: " - f"{[at.name for at in self._nucleotides[-1].atom_list]}. The full info" - f" on the first nucleotide is {repr(self._nucleotides[-1])}." - ) - try: - c_atom_in_last_residue = self._nucleotides[-1]["O3'"] - except KeyError: - raise InvalidNucleotideChainError( - "The last nucleotide in the chain must contain 3' terminal oxygen atom" - " (O3'). The first nucleotide that was provided is " - f"{self._nucleotides[0].name} and contains only the following atoms: " - f"{[at.name for at in self._nucleotides[0].atom_list]}.\nThe full info" - f" on the first nucleotide is {repr(self._nucleotides[0])}." - ) - idx = c_atom_in_last_residue.bonds.index("+R") - del c_atom_in_last_residue.bonds[idx] - c_atom_in_last_residue.bonds.extend(ratoms_in_last_residue) - - for i, current_residue in enumerate(self._nucleotides[:-1]): - next_residue = self._nucleotides[i + 1] - - for atom in current_residue.atom_list: - if "+R" in atom.bonds: - current_idx = atom.bonds.index("+R") - current_atom = atom - break - else: - raise InvalidResidueError( - f"The provided nucleotide with index {i}, {current_residue.name}, is invalid " - f'because it does not contain an atom bonded to "R+", i.e. another nucleo' - f"tide.\nThe full info on this nucleotide is {repr(current_residue)}" - ) - - for atom in next_residue.atom_list: - if "-R" in atom.bonds: - next_idx = atom.bonds.index("-R") - next_atom = atom - break - else: - raise InvalidResidueError( - f"The provided nucleotide with index {i + 1}, {next_residue.name}, is invalid " - f'because it does not contain an atom bonded to "R+", i.e. another nucleo' - f"tide.\nThe full info on this nucleotide is {repr(next_residue)}" - ) - - current_atom.bonds[current_idx] = next_atom - next_atom.bonds[next_idx] = current_atom - - @property - def atom_list(self) -> list[Atom]: - """List of all non-ghost atoms in all the nucleotides in the chain.""" - atoms = [] - for res in self._nucleotides: - atoms.extend(res.atom_list) - return atoms - - @property - def bases(self) -> list[Atom]: - """A list of atoms that are part of a nucleotide base.""" - atoms = [] - for at in self.atom_list: - if "base" in at.groups: - atoms.append(at) - return atoms - - def copy(self) -> "NucleotideChain": - """Copies the instance of NucleotideChain into a new, identical instance.""" - nc = NucleotideChain(self._name) - - nucleotides = [nucl.copy() for nucl in self._nucleotides] - - nc._nucleotides = nucleotides - - for nucl in nc._nucleotides: - nucl._parent = nc - - nc._connect_nucleotides() - - return nc - - @property - def residues(self) -> list[Nucleotide]: - """List of nucleotides in the chain.""" - return self._nucleotides - - @property - def number_of_atoms(self) -> int: - """Number of non-ghost atoms in the nucleotide chain.""" - number_of_atoms = 0 - for nucleotide in self._nucleotides: - number_of_atoms += nucleotide.number_of_atoms - return number_of_atoms - - @property - def total_number_of_atoms(self) -> int: - """Total number of atoms in the nucleotide chain, including ghost atoms.""" - number_of_atoms = 0 - for nucleotide in self._nucleotides: - number_of_atoms += nucleotide.total_number_of_atoms - return number_of_atoms - - @classmethod - def build( - cls, h5_contents: dict[str, list[list[str]]], name: str, nucl_indexes: list[int] - ) -> NucleotideChain: - """ - Creates an instance of the NucleotideChain class, one that contains its constituent :class: `Nucleotide`s. - This method is meant to be used when loading a trajectory from disk and so may be called when the - :class: `ChemicalSystem`.load() method is called. - - :param h5_contents: The contents of an MDANSE HDF5 Trajectory describing a chemical system. Obtained by calling - the :class: `ChemicalSystem`.load() method. - :type h5_contents: dict - - :param name: A name for the chain. - :type name: str - - :param nucl_indexes: List of indexes of :class: `Nucleotide`s that compose this NucleotideChain, pointing to - `h5_contents['nucleotides']` - :type nucl_indexes: list - - :return: An instance of the NucleotideChain class. - :rtype: NucleotideChain - """ - nc = cls(name) - - contents = h5_contents["nucleotides"] - nucleotides = [] - for index in nucl_indexes: - args = [literal_eval(arg.decode("utf8")) for arg in contents[index]] - nucl = Nucleotide.build(h5_contents, *args) - nucl.parent = nc - nucleotides.append(nucl) - - try: - nc.set_nucleotides(nucleotides) - except (InvalidNucleotideChainError, InvalidResidueError) as e: - raise CorruptedFileError( - f"Could not reconstruct NucleotideChain with name {name} from the HDF5 trajectory " - f"due to an issue with the terminal nucleotides. The NucleotideChain that could " - f"not be reconstructed is located in the trajectory at /chemical_system/" - f"nucleotide_chains at index INDEX. The original error is:\n{str(e)}" - ) - - return nc - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the NucleotideChain object and its contents into strings in preparation of the object being stored on - disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'nucleotide_chains' and the index of the serialization data of this - NucleotideChain in the provided dictionary. - :rtype: tuple - """ - if "nucleotides" in h5_contents: - res_indexes = list( - range( - len(h5_contents["nucleotides"]), - len(h5_contents["nucleotides"]) + len(self._nucleotides), - ) - ) - else: - res_indexes = list(range(len(self._nucleotides))) - - for nucl in self._nucleotides: - nucl.serialize(h5_contents) - - h5_contents.setdefault("nucleotide_chains", []).append( - [repr(self._name), str(res_indexes)] - ) - - return "nucleotide_chains", len(h5_contents["nucleotide_chains"]) - 1 - - @property - def nucleotides(self): - return self._nucleotides - - def set_nucleotides(self, nucleotides: list[Nucleotide]) -> None: - """ - Sets the provided Nucleotide objects as the nucleotides making up this chain, and creates bonds between them. - They are connected in the order that they are passed in to this method, so the first nucleotide must the 5' - terminus and the last one must be the 3' terminus. - - :param nucleotides: A list of nucleotides that will make up this chain. - :type nucleotides: list - - :return: None - """ - self._nucleotides = [] - for nucleotide in nucleotides: - nucleotide.parent = self - self._nucleotides.append(nucleotide) - - self._connect_nucleotides() - - @property - def sugars(self) -> list[Atom]: - """A list of atoms in the nucleotide chain that are a part of the sugar part of a nucleotide.""" - atoms = [] - for at in self.atom_list: - if "sugar" in at.groups: - atoms.append(at) - return atoms - - -class PeptideChain(_ChemicalEntity): - """A peptide chain composed of bonded amino acid residues.""" - - def __init__(self, name: str): - """ - The PeptideChain is instantiated empty. To set its component Residues, the set_residues() method must be called. - - :param name: The name of this PeptideChain - :type name: str - """ - - super(PeptideChain, self).__init__() - - self._name = name - - self._residues = [] - - def _connect_residues(self) -> None: - ratoms_in_first_residue = [ - at - for at in self._residues[0].atom_list - if getattr(at, "nter_connected", False) - ] - if not ratoms_in_first_residue: - raise InvalidPeptideChainError( - "The first residue in the chain must contain an atom that is connected to " - "the terminal nitrogen. This is signified by an atom having the " - '"nter_connected" property set to True, and happens automatically if the ' - 'residue was created with variant="NT1", and the set_atoms() method was ' - "called with the correct atoms.\nThe provided first residue is " - f"{self._residues[0].name}, its variant is {self._residues[0].variant} and " - f"it contains the following atoms: " - f"{[at.name for at in self._residues[0].atom_list]}. The full info on the " - f"first residue is {repr(self._residues[0])}." - ) - try: - n_atom_in_first_residue = self._residues[0]["N"] - except KeyError: - raise InvalidPeptideChainError( - "The first residue in the chain must contain the terminal nitrogen atom. " - f"However, the first residue that was provided is {self._residues[0].name} " - f"and contains only the following atoms: " - f"{[at.name for at in self._residues[0].atom_list]}.\nThe full info" - f" on the first nucleotide is {repr(self._residues[0])}." - ) - idx = n_atom_in_first_residue.bonds.index("-R") - del n_atom_in_first_residue.bonds[idx] - n_atom_in_first_residue.bonds.extend(ratoms_in_first_residue) - - # Process the last atom - ratoms_in_last_residue = [ - at - for at in self._residues[-1].atom_list - if getattr(at, "cter_connected", False) - ] - if not ratoms_in_last_residue: - raise InvalidPeptideChainError( - "The last residue in the chain must contain an atom that is connected to " - "the terminal carbon. This is signified by an atom having the " - '"cter_connected" property set to True, and happens automatically if the ' - 'residue was created with variant="CT1", and the set_atoms() method was ' - "called with the correct atoms.\nThe provided last residue is " - f"{self._residues[-1].name}, its variant is {self._residues[-1].variant} and" - f" it contains the following atoms: " - f"{[at.name for at in self._residues[-1].atom_list]}. The full info on the" - f" last residue is {repr(self._residues[-1])}." - ) - try: - c_atom_in_last_residue = self._residues[-1]["C"] - except KeyError: - raise InvalidPeptideChainError( - "The last residue in the chain must contain the terminal carbon atom. " - f"However, the last residue that was provided is {self._residues[-1].name} " - f"and contains only the following atoms: " - f"{[at.name for at in self._residues[-1].atom_list]}.\nThe full info" - f" on the last nucleotide is {repr(self._residues[-1])}." - ) - idx = c_atom_in_last_residue.bonds.index("+R") - del c_atom_in_last_residue.bonds[idx] - c_atom_in_last_residue.bonds.extend(ratoms_in_last_residue) - - # Create bonds - for i, current_residue in enumerate(self._residues[:-1]): - next_residue = self._residues[i + 1] - - for atom in current_residue.atom_list: - if "+R" in atom.bonds: - current_atom = atom - current_index = atom.bonds.index("+R") - break - else: - raise InvalidResidueError( - f"The provided residue with index {i}, {current_residue.name}, is invalid " - f'because it does not contain an atom bonded to "R+", i.e. another residue.' - f"\nThe full info on this residue is {repr(current_residue)}" - ) - - for atom in next_residue.atom_list: - if "-R" in atom.bonds: - next_atom = atom - next_index = atom.bonds.index("-R") - break - else: - raise InvalidResidueError( - f"The provided residue with index {i+1}, {next_residue.name}, is invalid " - f'because it does not contain an atom bonded to "R+", i.e. another residue.' - f"\nThe full info on this residue is {repr(next_residue)}" - ) - - current_atom.bonds[current_index] = next_atom - next_atom.bonds[next_index] = current_atom - - def __getitem__(self, item: int): - return self._residues[item] - - def __str__(self): - return "PeptideChain of {} residues".format(len(self._residues)) - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, _ChemicalEntity) and not isinstance(value, Residue): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}(name={value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.MolecularDynamics.ChemicalEntity.PeptideChain({contents[:-2]})" - - @property - def atom_list(self) -> list[Atom]: - """A list of all non-ghost atoms in the PeptideChain.""" - atoms = [] - for res in self._residues: - atoms.extend(res.atom_list) - return atoms - - @property - def backbone(self) -> list[Atom]: - """A list of all atoms in the PeptideChain that are a part of the backbone.""" - atoms = [] - for at in self.atom_list: - if "backbone" in at.groups: - atoms.append(at) - return atoms - - def copy(self) -> "PeptideChain": - """Copies the instance of NucleotideChain into a new, identical instance.""" - pc = PeptideChain(self._name) - - residues = [res.copy() for res in self._residues] - - pc._residues = residues - - for res in pc._residues: - res._parent = pc - - pc._connect_residues() - - return pc - - @property - def number_of_atoms(self) -> int: - """The number of non-ghost in the PeptideChain.""" - number_of_atoms = 0 - for residue in self._residues: - number_of_atoms += residue.number_of_atoms - return number_of_atoms - - @property - def total_number_of_atoms(self) -> int: - """The total number of atoms in the PeptideChain, including ghosts.""" - number_of_atoms = 0 - for residue in self._residues: - number_of_atoms += residue.total_number_of_atoms - return number_of_atoms - - @property - def peptide_chains(self) -> list["PeptideChain"]: - """The list of PeptideChains in this PeptideChain""" - return [self] - - @property - def peptides(self) -> list[Atom]: - """The list of atoms in the PeptideChain that are a part of a peptide.""" - atoms = [] - for at in self.atom_list: - if "peptide" in at.groups: - atoms.append(at) - return atoms - - @property - def residues(self) -> list[Residue]: - """The list of amino acid Residues that make up this PeptideChain.""" - return self._residues - - @classmethod - def build( - cls, h5_contents: dict[str, list[list[str]]], name: str, res_indexes: list[int] - ) -> PeptideChain: - """ - Creates an instance of the PeptideChain class, one that contains its constituent :class: `Residue`s. - This method is meant to be used when loading a trajectory from disk and so may be called when the - :class: `ChemicalSystem`.load() method is called. - - :param h5_contents: The contents of an MDANSE HDF5 Trajectory describing a chemical system. Obtained by calling - the :class: `ChemicalSystem`.load() method. - :type h5_contents: dict - - :param name: A name for the chain. - :type name: str - - :param res_indexes: List of indexes of :class: `Residue`s that compose this PeptideChain, pointing to - `h5_contents['residues']` - :type res_indexes: list - - :return: An instance of the PeptideChain class. - :rtype: PeptideChain - """ - pc = cls(name) - - contents = h5_contents["residues"] - residues = [] - for index in res_indexes: - args = [literal_eval(arg.decode("utf8")) for arg in contents[index]] - res = Residue.build(h5_contents, *args) - res.parent = pc - residues.append(res) - - try: - pc.set_residues(residues) - except (InvalidPeptideChainError, InvalidResidueError) as e: - raise CorruptedFileError( - f"Could not reconstruct PeptideChain with name {name} from the HDF5 trajectory " - f"due to an issue with the terminal residues. The PeptideChain that could " - f"not be reconstructed is located in the trajectory at /chemical_system/" - f"peptide_chains at index INDEX. The original error is:\n{str(e)}" - ) - - return pc - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the PeptideChain object and its contents into strings in preparation of the object being stored on - disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'peptide_chains' and the index of the serialization data of this - PeptideChain in the provided dictionary. - :rtype: tuple - """ - if "residues" in h5_contents: - res_indexes = list( - range( - len(h5_contents["residues"]), - len(h5_contents["residues"]) + len(self._residues), - ) - ) - else: - res_indexes = list(range(len(self._residues))) - - for res in self._residues: - res.serialize(h5_contents) - - h5_contents.setdefault("peptide_chains", []).append( - [repr(self._name), str(res_indexes)] - ) - - return "peptide_chains", len(h5_contents["peptide_chains"]) - 1 - - def set_residues(self, residues: list[Residue]) -> None: - """ - Populates the PeptideChain with amino acid Residues. - - :param residues: List of residues that the PeptideChain consists of. - :type residues: list - - :return: None - """ - self._residues = [] - for residue in residues: - residue.parent = self - self._residues.append(residue) - - self._connect_residues() - - @property - def sidechains(self) -> list[Atom]: - """A list of atoms in the PeptideChain that are part of an amino acid side-chain.""" - atoms = [] - for at in self.atom_list: - if "sidechain" in at.groups: - atoms.append(at) - return atoms - - -class Protein(_ChemicalEntity): - """A Protein consisting of one or more peptide chains.""" - - def __init__(self, name: str = ""): - """ - The Peptide class is instantiated empty; for it to contain peptide chains, the set_peptide_chains() method must - be called first. - - :param name: The name of the protein - :type name: str - """ - super().__init__() - - self._name = name - - self._peptide_chains = [] - - def __getitem__(self, item: int) -> PeptideChain: - return self._peptide_chains[item] - - def __repr__(self): - contents = "" - for key, value in self.__dict__.items(): - key = key[1:] if key[0] == "_" else key - if isinstance(value, ChemicalSystem): - class_name = str(type(value)).replace("", "") - contents += f"{key}={class_name}(name={value.name})" - else: - contents += f"{key}={repr(value)}" - contents += ", " - - return f"MDANSE.MolecularDynamics.ChemicalEntity.Protein({contents[:-2]})" - - def __str__(self): - return f"Protein {self.name} consisting of {len(self.peptide_chains)} peptide chains" - - @property - def atom_list(self) -> list[Atom]: - """List of all non-ghost atoms in the Protein.""" - atom_list = [] - for c in self._peptide_chains: - atom_list.extend(c.atom_list) - - return atom_list - - @property - def backbone(self) -> list[Atom]: - """A list of all atoms in the Protein that are a part of the backbone.""" - atoms = [] - for at in self.atom_list: - if "backbone" in at.groups: - atoms.append(at) - return atoms - - def copy(self) -> "Protein": - """Copies the instance of Protein into a new, identical instance.""" - p = Protein(self._name) - - peptide_chains = [pc.copy() for pc in self._peptide_chains] - - p._peptide_chains = peptide_chains - - for pc in p._peptide_chains: - pc._parent = p - - return p - - @property - def number_of_atoms(self) -> int: - """The number of non-ghost atoms in the Protein.""" - number_of_atoms = 0 - for peptide_chain in self._peptide_chains: - number_of_atoms += peptide_chain.number_of_atoms - return number_of_atoms - - @property - def total_number_of_atoms(self) -> int: - """The total number of atoms in the protein, including ghosts.""" - number_of_atoms = 0 - for peptide_chain in self._peptide_chains: - number_of_atoms += peptide_chain.total_number_of_atoms - return number_of_atoms - - def set_peptide_chains(self, peptide_chains: list[PeptideChain]) -> None: - """ - Sets the provided peptide chains as the ones that make up this Protein. - - :param peptide_chains: A list of peptide chains that make up this Protein - :type peptide_chains: list - - :return: None - """ - self._peptide_chains = peptide_chains - for pc in self._peptide_chains: - pc.parent = self - - @property - def peptide_chains(self) -> list[PeptideChain]: - """A list of peptide chains that make up this Protein.""" - return self._peptide_chains - - @property - def peptides(self) -> list[Atom]: - """A list of all atoms in the Protein that are a part of a peptide.""" - atoms = [] - for at in self.atom_list: - if "peptide" in at.groups: - atoms.append(at) - return atoms - - @property - def residues(self) -> list[Residue]: - """A list of all amino acid Residues in the Protein.""" - residues = [] - for pc in self._peptide_chains: - residues.extend(pc.residues) - - return residues - - @classmethod - def build( - cls, - h5_contents: dict[str, list[list[str]]], - name: str, - peptide_chain_indexes: list[int], - ) -> Protein: - """ - Creates an instance of the Protein class, one that contains its constituent :class: `PeptideChain`s. - This method is meant to be used when loading a trajectory from disk and so may be called when the - :class: `ChemicalSystem`.load() method is called. - - :param h5_contents: The contents of an MDANSE HDF5 Trajectory describing a chemical system. Obtained by calling - the :class: `ChemicalSystem`.load() method. - :type h5_contents: dict - - :param name: A name for the chain. - :type name: str - - :param peptide_chain_indexes: List of indexes of :class: `PeptideChain`s that compose this Protein, pointing to - `h5_contents['peptide_chains']` - :type peptide_chain_indexes: list - - :return: An instance of the Protein class. - :rtype: Protein - """ - p = cls(name) - - contents = h5_contents["peptide_chains"] - peptide_chains = [] - for index in peptide_chain_indexes: - args = [literal_eval(arg.decode("utf8")) for arg in contents[index]] - pc = PeptideChain.build(h5_contents, *args) - pc.parent = p - peptide_chains.append(pc) - - p.set_peptide_chains(peptide_chains) - - return p - - def serialize(self, h5_contents: dict[str, list[list[str]]]) -> tuple[str, int]: - """ - Serializes the Protein object and its contents into strings in preparation of the object being stored on disk. - - :param h5_contents: A dictionary that stores all serialized information for the whole ChemicalSystem. - :type h5_contents: dict - - :return: A tuple containing the string 'proteins' and the index of the serialization data of this Protein in the - provided dictionary. - :rtype: tuple - """ - if "peptide_chains" in h5_contents: - pc_indexes = list( - range( - len(h5_contents["peptide_chains"]), - len(h5_contents["peptide_chains"]) + len(self._peptide_chains), - ) - ) - else: - pc_indexes = list(range(len(self._peptide_chains))) - - h5_contents.setdefault("proteins", []).append( - [repr(self._name), str(pc_indexes)] - ) - - for pc in self._peptide_chains: - pc.serialize(h5_contents) - - return "proteins", len(h5_contents["proteins"]) - 1 - - @property - def sidechains(self) -> list[Atom]: - """A list of all atoms in the Protein that are a part of a sidechain.""" - atoms = [] - for at in self.atom_list: - if "sidechain" in at.groups: - atoms.append(at) - return atoms - - -def translate_atom_names( - database: Union[MoleculesDatabase, ResiduesDatabase, NucleotidesDatabase], - molname: str, - atoms: list[str], -) -> list[str]: - """ - Changes the names of all atoms in a given compound to the default names used in MDANSE. The names provided to this - function must be registered in the database provided in the database parameter, either as the default name or in the - 'alternatives'. - - :param database: Database of compounds in which the compound and its constituent atoms are registered. - :type database: one of MOLECULES_DATABASE, RESIDUES_DATABASE, or NUCLEOTIDES_DATABASE - - :param molname: The name of the chemical compound registered in the provided database whose atoms are to be renamed. - :type molname: str - - :param atoms: A list of atom names to be renamed. All of them must be part of the molname chemical compound. - :type atoms: list - - :return: The list of renamed atoms. - :rtype: list - """ - if not molname in database: - raise UnknownMoleculeError("The molecule {} is unknown".format(molname)) - - renamed_atoms = [] - for atom in atoms: - for dbat, dbinfo in database[molname]["atoms"].items(): - if dbat == atom or atom in dbinfo["alternatives"]: - renamed_atoms.append(dbat) - break - else: - raise UnknownAtomError("The atom {} is unknown".format(atom)) - - return renamed_atoms - - -class ChemicalSystem(_ChemicalEntity): - """A collection of all chemical compounds in a trajectory.""" - - def __init__(self, name: str = ""): - """ - - :param name: The name of the ChemicalSystem - :type name: str - """ - super(ChemicalSystem, self).__init__() - - self._chemical_entities = [] - - self._configuration = None - - self._number_of_atoms = 0 - - self._total_number_of_atoms = 0 - - self._name = name - - self._bonds = [] - - self._atoms = None - - self.rdkit_mol = Chem.RWMol() - - def __repr__(self): - contents = [] - for key, value in self.__dict__.items(): - if key == "rdkit_mol": - continue - contents.append(f'{key[1:] if key[0] == "_" else key}={repr(value)}') - - contents = ", ".join(contents) - return f"MDANSE.MolecularDynamics.ChemicalEntity.ChemicalSystem({contents})" - - def __str__(self): - return f"ChemicalSystem {self.name} consisting of {len(self._chemical_entities)} chemical entities" - - def add_chemical_entity(self, chemical_entity: _ChemicalEntity) -> None: - """ - Adds the provided ChemicalEntity to the ChemicalSystem. - - :param chemical_entity: The ChemicalEntity to be added. - :type chemical_entity: Any subclass of _ChemicalEntity - - :return: None - """ - if not isinstance(chemical_entity, _ChemicalEntity): - raise InvalidChemicalEntityError("Invalid type") - - for at in chemical_entity.atom_list: - at.index = self._number_of_atoms - self._number_of_atoms += 1 - - # add the atoms to the rdkit molecule, ghost atoms are - # never added to the rdkit molecule object - atm_num = ATOMS_DATABASE.get_atom_property(at.symbol, "atomic_number") - rdkit_atm = Chem.Atom(atm_num) - - # makes sure that rdkit doesn't add extra hydrogens - rdkit_atm.SetNumExplicitHs(0) - rdkit_atm.SetNoImplicit(True) - - self.rdkit_mol.AddAtom(rdkit_atm) - - self._total_number_of_atoms += chemical_entity.total_number_of_atoms - - chemical_entity.parent = self - - self._chemical_entities.append(chemical_entity) - - if hasattr(chemical_entity, "_bonds") and hasattr(chemical_entity, "index"): - for bond in chemical_entity._bonds: - number_bond = [chemical_entity.index, bond.index] - if number_bond not in self._bonds: - self._bonds.append(number_bond) - - # add the bonds between the rdkit atoms, the atom index in - # this chemical system needs to be unique and fixed otherwise - # there could be issues - bonds_added = [] - for at_i in chemical_entity.atom_list: - i = at_i.index - for at_j in at_i.bonds: - j = at_j.index - if i == j: - continue - bond_idxs = sorted([i, j]) - if bond_idxs not in bonds_added: - # there is currently no bonding information in - # MDANSE, we will have to default to the UNSPECIFIED - # bond type. - single = Chem.rdchem.BondType.UNSPECIFIED - self.rdkit_mol.AddBond(i, j, single) - bonds_added.append(bond_idxs) - - self._configuration = None - - self._atoms = None - - def has_substructure_match(self, smarts: str) -> bool: - """Check if there is a substructure match. - - Parameters - ---------- - smarts : str - SMARTS string. - - Returns - ------- - bool - True if the there is a substructure match. - """ - return self.rdkit_mol.HasSubstructMatch(Chem.MolFromSmarts(smarts)) - - def get_substructure_matches( - self, smarts: str, maxmatches: int = 1000000 - ) -> set[int]: - """Get the indexes which match the smarts string. Note that - the default bond type in MDANSE is - Chem.rdchem.BondType.UNSPECIFIED. - - Parameters - ---------- - smarts : str - SMARTS string. - maxmatches : int - Maximum number of matches used in the GetSubstructMatches - rdkit method. - - Returns - ------- - set[int] - An set of matched atom indices. - """ - substruct_set = set() - matches = self.rdkit_mol.GetSubstructMatches( - Chem.MolFromSmarts(smarts), maxMatches=maxmatches - ) - for match in matches: - substruct_set.update(match) - return substruct_set - - @property - def atom_list(self) -> list[Atom]: - """List of all non-ghost atoms in the ChemicalSystem.""" - atom_list = [] - for ce in self._chemical_entities: - atom_list.extend(ce.atom_list) - - return atom_list - - @property - def atoms(self) -> list[Atom]: - """A list of all non-ghost atoms in the ChemicalSystem, sorted by their index.""" - from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms - - if self._atoms is None: - self._atoms = sorted_atoms(self.atom_list) - - return self._atoms - - @property - def chemical_entities(self) -> list[_ChemicalEntity]: - """ - A list of all chemical entities in this ChemicalSystem. Only the entities registered in the ChemicalSystem will - be returned; their children will not. I.e., if a Molecule object is added to this ChemicalSystem via the - add_chemical_entity() method, this property will only return the Molecule object, not its consituent Atom - objects. - """ - return self._chemical_entities - - @property - def configuration(self) -> _Configuration: - """The Configuration that this ChemicalSystem is associated with.""" - return self._configuration - - @configuration.setter - def configuration(self, configuration: _Configuration): - if configuration.chemical_system != self: - raise InconsistentChemicalSystemError("Mismatch between chemical systems") - - self._configuration = configuration - - def copy(self) -> "ChemicalSystem": - """ - Copies the instance of ChemicalSystem into a new, identical instance. - - :return: Copy of the ChemicalSystem instance - :rtype: MDANSE.Chemistry.ChemicalEntity.ChemicalSystem - """ - cs = ChemicalSystem(self._name) - - cs._parent = self._parent - - cs._chemical_entities = [ce.copy() for ce in self._chemical_entities] - # for ce in self._chemical_entities: - # cs.add_chemical_entity(ce.copy()) - - new_atoms = {atom.name: atom for atom in cs.atoms} - - for atom in cs.atoms: - cs._bonds += atom.restore_bonds(new_atoms) - - cs._number_of_atoms = self._number_of_atoms - - cs._total_number_of_atoms = self._total_number_of_atoms - - for ce in cs._chemical_entities: - ce._parent = cs - - if self._configuration is not None: - conf = self._configuration.clone(cs) - - cs._configuration = conf - - if self._atoms is not None: - _ = cs.atoms - else: - cs._atoms = None - - return cs - - def rebuild(self, cluster_list: List[Tuple[int]], selection: List[int] = None): - """ - Copies the instance of ChemicalSystem into a new, identical instance. - - :param cluster_list: list of tuples of atom indices, one per cluster - :type List[Tuple[int]]: each element is a tuple of atom indices (int) - """ - - atom_names_before = [atom.name for atom in self.atoms] - clusters = [] - - if selection is not None: - for cluster_number, index_list in enumerate(cluster_list): - temp = AtomCluster( - "cluster_" + str(cluster_number + 1), - [ - self.atom_list[index] - for index in index_list - if index in selection - ], - ) - if temp.number_of_atoms > 0: - clusters.append(temp) - else: - for cluster_number, index_list in enumerate(cluster_list): - temp = AtomCluster( - "cluster_" + str(cluster_number + 1), - [self.atom_list[index] for index in index_list], - ) - clusters.append(temp) - - self._chemical_entities = [] - - self._number_of_atoms = 0 - - self._total_number_of_atoms = 0 - - configuration_before = self.configuration - atom_names_after = [atom.name for atom in self.atoms] - - for cluster in clusters[::-1]: - self.add_chemical_entity(cluster) - - if atom_names_before == atom_names_after: - self._configuration = configuration_before - else: - LOG.error(f"Atoms before: {atom_names_before}") - LOG.error(f"Atoms after: {atom_names_after}") - raise RuntimeError( - "ChemicalSystem.rebuild() changed the order of atoms. This needs to be handled!" - ) - - def unique_molecules(self) -> List[str]: - """Returns the list of unique names in the chemical system""" - result = np.unique( - [ce.name for ce in self.chemical_entities if ce.number_of_atoms > 1] - ) - return list(result) - - def number_of_molecules(self, molecule_name: str) -> int: - """Returns the number of molecules with the given name in the system""" - result = [1 for ce in self.chemical_entities if ce.name == molecule_name] - return len(result) - - def from_element_list(self, elements: List[str]): - for element in elements: - ce = Atom(element) - self.add_chemical_entity(ce) - - def load(self, h5_filename: Union[str, h5py.File]) -> None: - """ - Loads a ChemicalSystem from an HDF5 file. The HDF5 file must be organised in such a way that it can parsed by - MDANSE. - - :param h5_filename: The HDF5 file that contains the information about the chemical system. This is usually the - trajectory file. - :type h5_filename: str or h5py.File - - :return: None - """ - h5_classes = { - "atoms": Atom, - "atom_clusters": AtomCluster, - "molecules": Molecule, - "nucleotides": Nucleotide, - "nucleotide_chain": NucleotideChain, - "residue": Residue, - "peptide_chains": PeptideChain, - "proteins": Protein, - } - - try: - h5_file = h5py.File(h5_filename, "r", libver="latest") - close_file = True - except TypeError: - h5_file = h5_filename - close_file = False - - grp = h5_file["/chemical_system"] - self._chemical_entities = [] - - skeleton = h5_file["/chemical_system/contents"][:] - - try: - bonds = np.unique(h5_file["/chemical_system/bonds"], axis=0) - except KeyError: - bonds = [] - - self._name = grp.attrs["name"] - - h5_contents = {} - for entity_type, v in grp.items(): - if entity_type == "contents" or entity_type == "bonds": - continue - h5_contents[entity_type] = v[:] - - for i, (entity_type, entity_index) in enumerate(skeleton): - entity_index = int(entity_index) - entity_type = entity_type.decode("utf-8") - - try: - entity_class = h5_classes[entity_type] - except KeyError: - h5_file.close() - raise CorruptedFileError( - f"Could not create a chemical entity of type {entity_type}. The entity listed" - f" in the chemical system contents (located at /chemical_system/contents in " - f"the HDF5 file) at index {i} is not recognised as valid entity; {entity_type}" - f" should be one of: atoms, atom_clusters, molecules, nucleotides, nucleotide" - f"_chains, residues, residue_chains, or proteins." - ) - try: - arguments = [ - literal_eval(arg.decode("utf8")) - for arg in h5_contents[entity_type][entity_index] - ] - # except AttributeError: - # print(f"Wrong argument in entity_type: {entity_type} at index {entity_index} ") - # print(f"The entry was: {h5_contents[entity_type][entity_index]}") - except KeyError: - raise CorruptedFileError( - f"Could not find chemical entity {entity_type}, listed in chemical system " - f"contents (/chemical_system/contents) at index {i}, in the chemical system " - f"itself (/chemical_system). The chemical_system group in the HDF5 file " - f"contains only the following datasets: {h5_contents.keys()}." - ) - except IndexError: - raise CorruptedFileError( - f"The chemical entity {entity_type}, listed in chemical system contents " - f"(/chemical_system/contents) at index {i}, could not be found in the " - f"{entity_type} dataset (/chemical_system/{entity_type}) because the " - f"index registered in contents, {entity_index}, is out of range of the dataset" - f", which contains only {len(h5_contents[entity_type])} elements." - ) - except (ValueError, SyntaxError, RuntimeError) as e: - raise CorruptedFileError( - f"The data used for reconstructing the chemical system could not be parsed " - f"from the HDF5 file. The data located at /chemical_system/{entity_type}[" - f"{entity_index}] is corrupted.\nThe provided data is: " - f"{h5_contents[entity_type][entity_index]}\nThe original error is: {e}" - ) - finally: - h5_file.close() - - try: - ce = entity_class.build(h5_contents, *arguments) - except CorruptedFileError as e: - raise CorruptedFileError(str(e).replace("INDEX", str(entity_index))) - except InconsistentAtomNamesError as e: - raise CorruptedFileError( - f"Could not reconstruct {entity_class} from the HDF5 Trajectory because its " - f"constituent atoms recorded in the trajectory are different from those " - f"expected of this entity with this code ({arguments[1]}). The entity that " - f"raised this error is located in the trajectory at /chemical_system/" - f"{entity_type} at index {entity_index} while its constituent atoms are at " - f"/chemical_system/atoms at indices {arguments[0]}.\nThe full data in the " - f"trajectory of the entity that raised the error is " - f"{h5_contents[entity_type][entity_index]}\nThe original error is {e}" - ) - except IndexError as e: - raise CorruptedFileError( - f"Could not reconstruct {entity_class} from the HDF5 Trajectory because one " - f"or more of its constituent atoms are missing from the trajectory. This " - f"entity is located in the trajectory at /chemical_system/{entity_type} at " - f"index {entity_index}. Its constituent atoms are located at /chemical_system/" - f'atoms at indices {arguments[0]}, but only {len(h5_contents["atoms"])} atoms ' - f"are present in the trajectory.\nThe full data in the trajectory of the " - f"entity that raised the error is {h5_contents[entity_type][entity_index]}" - f"\nThe original error is {e}" - ) - except KeyError as e: - raise CorruptedFileError( - f"Could not reconstruct {entity_class} from the HDF5 Trajectory because one of" - f" its constituent parts could not be found in the trajectory. This entity is " - f"located in the trajectory at /chemical_system/{entity_type} at index " - f"{entity_index}.\nThe full data in the trajectory of the entity that raised " - f"the error is {h5_contents[entity_type][entity_index]}\nThe original error is" - f" {e}" - ) - except TypeError as e: - raise CorruptedFileError( - f"Could not reconstruct {entity_class} from the HDF5 Trajectory because the " - f"data associated with it does not match the expected arguments required for" - f" reconstructing this entity. This entity is located in the trajectory at " - f"/chemical_system/{entity_type} at index {entity_index} and the associated " - f"data is {arguments}.\nThe full data in the trajectory of the entity that " - f"raised the error is {h5_contents[entity_type][entity_index]}\nThe original " - f"error is {e}." - ) - except ValueError as e: - raise CorruptedFileError( - f"Could not reconstruct {entity_class} from the HDF5 Trajectory because the " - f"data associated with it is in an incorrect format. This entity is located " - f"in the trajectory at /chemical_system/{entity_type} at index {entity_index} " - f"and the associated data is {arguments}.\nThe full data in the trajectory of " - f"the entity that raised the error is {h5_contents[entity_type][entity_index]}" - f"\nThe original error is {e}." - ) - - self.add_chemical_entity(ce) - - self._bonds = list(bonds) - - if close_file: - h5_file.close() - - self._h5_file = None - - @property - def number_of_atoms(self) -> int: - """The number of non-ghost atoms in the ChemicalSystem.""" - return self._number_of_atoms - - @property - def total_number_of_atoms(self) -> int: - """The number of all atoms in the ChemicalSystem, including ghost ones.""" - return self._total_number_of_atoms - - def serialize(self, h5_file: h5py.File) -> None: - """ - Serializes the contents of the ChemicalSystem object and stores all the data necessary to reconstruct it into - the provided HDF5 file. - - :param h5_file: The file into which the ChemicalSystem is saved - :type h5_file: h5py.File - - :return: None - """ - string_dt = h5py.special_dtype(vlen=str) - - grp = h5_file.create_group("/chemical_system") - - grp.attrs["name"] = self._name - - h5_contents = {} - - contents = [] - for ce in self._chemical_entities: - entity_type, entity_index = ce.serialize(h5_contents) - contents.append((entity_type, str(entity_index))) - - for k, v in h5_contents.items(): - grp.create_dataset(k, data=v, dtype=string_dt) - grp.create_dataset("contents", data=contents, dtype=string_dt) - - h5_bonds = np.array(self._bonds).astype(np.int32) - grp.create_dataset("bonds", data=h5_bonds, dtype=np.int32) diff --git a/MDANSE/Src/MDANSE/Chemistry/ChemicalSystem.py b/MDANSE/Src/MDANSE/Chemistry/ChemicalSystem.py new file mode 100644 index 0000000000..d411393640 --- /dev/null +++ b/MDANSE/Src/MDANSE/Chemistry/ChemicalSystem.py @@ -0,0 +1,350 @@ +# This file is part of MDANSE. +# +# MDANSE is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from __future__ import annotations +from typing import List, Tuple, Dict, Any +import copy +from functools import reduce + +import h5py +import numpy as np +from rdkit import Chem +import networkx as nx +from MDANSE.MLogging import LOG +from MDANSE.Chemistry import ATOMS_DATABASE + + +class ChemicalSystem: + + def __init__(self, name: str = "", trajectory=None): + """ + + :param name: The name of the ChemicalSystem + :type name: str + """ + + self._name = name + self._database = ATOMS_DATABASE + if trajectory is not None: + self._database = trajectory + + self._total_number_of_atoms = 0 + self._atom_types = [] + self._atom_names = None + self._atom_indices = [] + self._labels = {} # arbitrary tag attached to atoms (e.g. residue name) + + self._bonds = [] + + self._clusters = {} + + self.rdkit_mol = Chem.RWMol() + self._unique_elements = set() + + def __str__(self): + return f"ChemicalSystem {self._name} consisting of {len(self._atom_types)} atoms in {len(self._clusters)} molecules" + + def initialise_atoms(self, element_list: List[str], name_list: List[str] = None): + self._atom_indices = [ + self.add_atom(self._database.get_atom_property(symbol, "atomic_number")) + for symbol in element_list + ] + self._atom_types = [str(x) for x in element_list] + self._total_number_of_atoms = len(self._atom_indices) + self._unique_elements.update(set(element_list)) + if name_list is not None: + self._atom_names = [str(x) for x in name_list] + + def add_atom(self, atm_num: int) -> int: + if atm_num is not None: + rdkit_atm = Chem.Atom(atm_num) + else: + rdkit_atm = Chem.Atom(0) + rdkit_atm.SetNumExplicitHs(0) + rdkit_atm.SetNoImplicit(True) + return self.rdkit_mol.AddAtom(rdkit_atm) + + def add_bonds(self, pair_list: List[Tuple[int]]): + self._bonds += list(pair_list) + for pair in pair_list: + self.rdkit_mol.AddBond( + int(pair[0]), int(pair[1]), Chem.rdchem.BondType.UNSPECIFIED + ) + + def add_labels(self, label_dict: Dict[str, List[int]]): + for key, item in label_dict.items(): + if key in self._labels.keys(): + self._labels[key] += item + else: + self._labels[key] = item + + def add_clusters(self, group_list: List[List[int]]): + for group in group_list: + sorted_group = list(sorted(set(group))) + if len(sorted_group) < 2: + continue + atom_list = [self._atom_types[index] for index in group] + unique_atoms, counts = np.unique(atom_list, return_counts=True) + name = "_".join( + [str(unique_atoms[n]) + str(counts[n]) for n in range(len(counts))] + ) + if name not in self._clusters: + self._clusters[name] = [sorted_group] + else: + if sorted_group not in self._clusters[name]: + self._clusters[name].append(group) + + def has_substructure_match(self, smarts: str) -> bool: + """Check if there is a substructure match. + + Parameters + ---------- + smarts : str + SMARTS string. + + Returns + ------- + bool + True if the there is a substructure match. + """ + return self.rdkit_mol.HasSubstructMatch(Chem.MolFromSmarts(smarts)) + + def get_substructure_matches( + self, smarts: str, maxmatches: int = 1000000 + ) -> set[int]: + """Get the indices which match the smarts string. Note that + the default bond type in MDANSE is + Chem.rdchem.BondType.UNSPECIFIED. + + Parameters + ---------- + smarts : str + SMARTS string. + maxmatches : int + Maximum number of matches used in the GetSubstructMatches + rdkit method. + + Returns + ------- + set[int] + An set of matched atom indices. + """ + substruct_set = set() + matches = self.rdkit_mol.GetSubstructMatches( + Chem.MolFromSmarts(smarts), maxMatches=maxmatches + ) + for match in matches: + substruct_set.update(match) + return substruct_set + + @property + def atom_list(self) -> list[str]: + """List of all non-ghost atoms in the ChemicalSystem.""" + return self._atom_types + + @property + def name_list(self) -> list[str]: + """List of all non-ghost atoms in the ChemicalSystem.""" + if self._atom_names is not None: + return self._atom_names + return self._atom_types + + def atom_property(self, property: str) -> list[Any]: + """List of a specific property, for all atoms in the system""" + lookup = {} + for atom in self.atom_list: + lookup[atom] = self._database.get_atom_property(atom, property) + return [lookup[atom] for atom in self.atom_list] + + def grouping_level(self, index: int) -> int: + """Temporarily, there is no grouping test. + + Parameters + ---------- + index : int + atom index + + Returns + ------- + int + grouping level for the GroupingLevelConfigurator + """ + return 0 + + def copy(self) -> "ChemicalSystem": + """ + Copies the instance of ChemicalSystem into a new, identical instance. + + :return: Copy of the ChemicalSystem instance + :rtype: MDANSE.Chemistry.ChemicalSystem.ChemicalSystem + """ + cs = ChemicalSystem(self._name) + + for attribute_name, attribute_value in self.__dict__.items(): + if attribute_name in ["rdkit_mol", "_configuration"]: + continue + setattr(cs, attribute_name, copy.deepcopy(attribute_value)) + + return cs + + def find_clusters_from_bonds(self): + molecules = [] + atom_pool = list(self._atom_indices) + + total_graph = nx.Graph() + total_graph.add_nodes_from(atom_pool) + total_graph.add_edges_from(self._bonds) + while len(atom_pool) > 0: + last_atom = atom_pool.pop() + temp_dict = nx.dfs_successors(total_graph, last_atom) + others = reduce(list.__add__, temp_dict.values(), []) + for atom in others: + atom_pool.pop(atom_pool.index(atom)) + molecule = [last_atom] + others + molecules.append(sorted(molecule)) + self.add_clusters(molecules) + + def unique_molecules(self) -> List[str]: + """Returns the list of unique names in the chemical system""" + return list([str(x) for x in self._clusters.keys()]) + + def number_of_molecules(self, molecule_name: str) -> int: + """Returns the number of molecules with the given name in the system""" + return len(self._clusters[molecule_name]) + + @property + def number_of_atoms(self) -> int: + """The number of non-ghost atoms in the ChemicalSystem.""" + return self._total_number_of_atoms + + @property + def total_number_of_atoms(self) -> int: + """The number of all atoms in the ChemicalSystem, including ghost ones.""" + return self._total_number_of_atoms + + def serialize(self, h5_file: h5py.File) -> None: + """ + Serializes the contents of the ChemicalSystem object and stores all the data necessary to reconstruct it into + the provided HDF5 file. + + :param h5_file: The file into which the ChemicalSystem is saved + :type h5_file: h5py.File + + :return: None + """ + string_dt = h5py.special_dtype(vlen=str) + + grp = h5_file.create_group("/composition") + grp.attrs["name"] = self._name + + try: + grp.create_dataset("atom_types", data=self._atom_types, dtype=string_dt) + except TypeError: + print(f"Bad array: {self._atom_types}") + import sys + + sys.exit(1) + if self._atom_names is not None: + try: + grp.create_dataset("atom_names", data=self._atom_names, dtype=string_dt) + except TypeError: + print(f"Bad array: {self._atom_names}") + import sys + + sys.exit(1) + grp.create_dataset("atom_indices", data=self._atom_indices) + + grp.create_dataset("bonds", data=np.array(self._bonds)) + + label_group = grp.create_group("labels") + for key, value in self._labels.items(): + label_group.create_dataset(key, data=value) + clusters_group = grp.create_group("clusters") + for key, value in self._clusters.items(): + clusters_group.create_dataset(key, data=value) + + def load(self, trajectory: str): + close_on_end = False + if hasattr(trajectory, "keys"): + source = trajectory + else: + close_on_end = True + source = h5py.File(trajectory) + if "composition" not in source.keys(): + if close_on_end: + source.close() + self.legacy_load(trajectory) + return + + self.rdkit_mol = Chem.RWMol() + + grp = source["/composition"] + self._name = grp.attrs["name"] + + atom_types = [binary.decode("utf-8") for binary in grp["atom_types"][:]] + atom_names = None + if "atom_names" in grp.keys(): + atom_names = [binary.decode("utf-8") for binary in grp["atom_names"][:]] + self.initialise_atoms(atom_types, atom_names) + old_indices = [int(tag) for tag in grp["atom_indices"][:]] + if not np.allclose(old_indices, self._atom_indices): + LOG.error("Atoms got re-indexed on loading the trajectory") + + self.add_bonds([[int(pair[0]), int(pair[1])] for pair in grp["bonds"][:]]) + + self._labels = {} + for label in grp["labels"].keys(): + self._labels[str(label)] = [int(tag) for tag in grp[f"labels/{str(label)}"]] + + for cluster in grp["clusters"].keys(): + self._clusters[str(cluster)] = [ + [int(x) for x in line] for line in grp[f"clusters/{cluster}"] + ] + if close_on_end: + source.close() + + def legacy_load(self, trajectory: str): + close_on_end = False + if hasattr(trajectory, "keys"): + source = trajectory + else: + close_on_end = True + source = h5py.File(trajectory) + self.rdkit_mol = Chem.RWMol() + + grp = source["/chemical_system"] + self._name = grp.attrs["name"] + atoms = grp["atoms"] + element_list = [line[0].decode("utf-8").strip("'") for line in atoms] + self.initialise_atoms(element_list) + + bonds = grp["bonds"] + bond_list = bonds[:] + self.add_bonds([[int(pair[0]), int(pair[1])] for pair in bond_list]) + + if "atom_clusters" in grp.keys(): + cluster_list = [] + for line in grp["atom_clusters"]: + indices_string = line[0].decode("utf-8") + indices_list = [int(x) for x in indices_string.strip("[]").split(",")] + if indices_list: + cluster_list.append(indices_list) + if cluster_list: + self.add_clusters(cluster_list) + if close_on_end: + source.close() + + self.find_clusters_from_bonds() diff --git a/MDANSE/Src/MDANSE/Chemistry/Databases.py b/MDANSE/Src/MDANSE/Chemistry/Databases.py index b5d89846f1..df6f51204c 100644 --- a/MDANSE/Src/MDANSE/Chemistry/Databases.py +++ b/MDANSE/Src/MDANSE/Chemistry/Databases.py @@ -16,7 +16,7 @@ import copy import os -from typing import Union, ItemsView +from typing import Union, ItemsView, Dict, Any import json @@ -606,290 +606,38 @@ def get_atom_property(self, symbol: str, property: str) -> Union[int, float, str Union[int, float, str] The atom property. """ - return self._data[symbol][property] - - -class MoleculesDatabaseError(Error): - """This class handles the exceptions related to MoleculesDatabase""" - - pass - - -class MoleculesDatabase(_Database): - _DEFAULT_DATABASE = os.path.join(os.path.dirname(__file__), "molecules.json") - - # The user path - _USER_DATABASE = os.path.join(PLATFORM.application_directory(), "molecules.json") - - def __getitem__(self, item: str): - """ - Return an entry of the database. - - :param item: the item to get from the database - :type item: str or tuple - """ - - if item in self._residue_map: - return copy.deepcopy(self._data[self._residue_map[item]]) - else: - raise MoleculesDatabaseError( - "The molecule {} is not registered in the database.".format(item) - ) - - def _load(self, user_database: str = None, default_database: str = None) -> None: - """ - Load the molecule database. This method should never be called elsewhere than __init__ or unit testing. - - :param user_database: The path to the user-defined database. The default path is used by default. - :type user_database: str or None - - :param default_database: The path to the default MDANSE molecule database. The default path is used by default. - :type default_database: str or None - """ - super()._load(user_database, default_database) - self._build_residue_map() - - def add_molecule(self, molecule: str) -> None: - """ - Add a new molecule in the molecule database. The data for this molecule will be empty (not completely, its entry - will consist of an empty list 'alternatives' and empty dict 'atoms') and will not be saved until the - :meth: `save()` method is called. If the atom already exists, an exception is raised. - - :param molecule: the name of the molecule to add - :type molecule: str - """ - - if molecule in self._data: - raise MoleculesDatabaseError( - "The element {} is already stored in the database".format(molecule) - ) - - self._data[molecule] = {"alternatives": [], "atoms": {}} - self._residue_map[molecule] = molecule - - @property - def molecules(self) -> list[str]: - """ - Returns the names of all molecules in the database. - - :return: the name of the molecule stored in the database - :rtype: list - """ - - return list(self._data.keys()) - - @property - def n_molecules(self) -> int: - """ - Return the number of molecules stored in the molecule database. - - :return: the number of molecules stored in the molecule database - :rtype: int - """ - - return len(self._data) - - -class NucleotidesDatabaseError(Error): - """This class handles the exceptions related to NucleotidesDatabase""" - - pass - - -class NucleotidesDatabase(_Database): - _DEFAULT_DATABASE = os.path.join(os.path.dirname(__file__), "nucleotides.json") - - # The user path - _USER_DATABASE = os.path.join(PLATFORM.application_directory(), "nucleotides.json") - - def __getitem__( - self, item: str - ) -> dict[str, Union[bool, list[str], dict[str, Union[bool, list[str], str]]]]: - """ - Return an entry of the database. - - :param item: the item to get from the database - :type item: str or tuple - """ - - if item in self._residue_map: - return copy.deepcopy(self._data[self._residue_map[item]]) - else: - raise NucleotidesDatabaseError( - "The nucleotide {} is not registered in the database.".format(item) - ) - - def _load(self, user_database: str = None, default_database: str = None) -> None: - """ - Load the nucleotide database. This method should never be called elsewhere than __init__ or unit testing. - - :param user_database: The path to the user-defined database. The default path is used by default. - :type user_database: str or None - - :param default_database: The path to the default MDANSE nucleotide database. The default path is used by default. - :type default_database: str or None - """ - super()._load(user_database, default_database) - self._build_residue_map() - - def add_nucleotide( - self, - nucleotide: str, - is_5ter_terminus: bool = False, - is_3ter_terminus: bool = False, - ) -> None: - """ - Add a new nucleotide in the nucleotide database. The data for this nucleotide will be empty (not completely, - its entry will consist of an empty list 'alternatives', an empty dict 'atoms', and two boolean values determined - by the parameters) and will not be saved until the :meth: `save()` method is called. If the atom already exists, - an exception is raised. - - :param nucleotide: the name of the nucleotide to add - :type nucleotide: str - - :param is_5ter_terminus: boolean value representing whether this nucleotide acts as a 5' terminus - :type is_5ter_terminus: bool - - :param is_3ter_terminus: boolean value representing whether this nucleotide acts as a 3' terminus - :type is_3ter_terminus: bool - """ - - if nucleotide in self._residue_map: - raise NucleotidesDatabaseError( - "The nucleotide {} is already stored in the database".format(nucleotide) - ) - - self._data[nucleotide] = { - "alternatives": [], - "atoms": {}, - "is_5ter_terminus": is_5ter_terminus, - "is_3ter_terminus": is_3ter_terminus, - } - - self._residue_map[nucleotide] = nucleotide - - @property - def nucleotides(self) -> list[str]: - """ - Returns the names of all nucleotides in the database. - - :return: the name of the nucleotides stored in the database - :rtype: list - """ - - return list(self._data.keys()) - - @property - def n_nucleotides(self) -> int: - """ - Return the number of nucleotides stored in the nucleotides database. - - :return: the number of nucleotides stored in the nucleotides database - :rtype: int - """ - - return len(self._data) - - -class ResiduesDatabaseError(Error): - """This class handles the exceptions related to ResiduesDatabase""" - - pass - - -class ResiduesDatabase(_Database): - _DEFAULT_DATABASE = os.path.join(os.path.dirname(__file__), "residues.json") - - # The user path - _USER_DATABASE = os.path.join(PLATFORM.application_directory(), "residues.json") - - def __getitem__( - self, item: str - ) -> dict[str, Union[bool, list[str], dict[str, dict[str, Union[str, list[str]]]]]]: - """ - Return an entry of the database. - - :param item: the item to get from the database - :type item: str or tuple - """ - - if item in self._residue_map: - return copy.deepcopy(self._data[self._residue_map[item]]) - else: - raise ResiduesDatabaseError( - "The residue {} is not registered in the database.".format(item) - ) - - def _load(self, user_database: str = None, default_database: str = None) -> None: - """ - Load the residue database. This method should never be called elsewhere than __init__ or unit testing. - - :param user_database: The path to the user-defined database. The default path is used by default. - :type user_database: str or None - - :param default_database: The path to the default MDANSE residue database. The default path is used by default. - :type default_database: str or None - """ - super()._load(user_database, default_database) - self._build_residue_map() - - def add_residue( - self, residue: str, is_c_terminus: bool = False, is_n_terminus: bool = False - ) -> None: - """ - Add a new residue to the residue database. The data for this residue will be empty (not completely, its entry - will consist of an empty list 'alternatives', an empty dict 'atoms', and two booleans determined by the - arguments) and will not be saved until the :meth: `save()` method is called. If the residue already exists, an - exception is raised. - - :param residue: the name of the residue to add - :type residue: str + try: + return self._data[symbol][property] + except KeyError: + if property == "dummy": + if symbol == "Du": + return 1 + if self._data[symbol]["element"] == "dummy": + return 1 + return 0 + return None + + def get_property_dict(self, symbol: str) -> Dict[str, Any]: + """Faster access to the atom property as it avoids the deepcopy + in __getitem__. - :param is_c_terminus: boolean representation of whether this residue is the C-terminus of proteins. False by - default. - :type is_c_terminus: bool + Parameters + ---------- + symbol : str + Symbol of the atom. + property : str + Property of the atoms to get. - :param is_n_terminus: boolean representation of whether this residue is the N-terminus of proteins. False by - default. - :type is_n_terminus: bool + Returns + ------- + Union[int, float, str] + The atom property. """ - - if residue in self._residue_map: - raise ResiduesDatabaseError( - "The element {} is already stored in the database".format(residue) - ) - - self._data[residue] = { - "alternatives": [], - "atoms": {}, - "is_n_terminus": is_n_terminus, - "is_c_terminus": is_c_terminus, + return { + property_name: self.get_value(symbol, property_name) + for property_name in self.properties } - self._residue_map[residue] = residue - - @property - def residues(self) -> list[str]: - """ - Returns the names of all residues in the database. - - :return: the names of all residues in the database. - :rtype: list - """ - - return list(self._data.keys()) - - @property - def n_residues(self) -> int: - """ - Return the number of residues stored in the residues database. - - :return: the number of residues stored in the residues database - :rtype: int - """ - - return len(self._data) - if __name__ == "__main__": from MDANSE.Chemistry import ATOMS_DATABASE diff --git a/MDANSE/Src/MDANSE/Chemistry/Structrures.py b/MDANSE/Src/MDANSE/Chemistry/Structrures.py deleted file mode 100644 index ca83930600..0000000000 --- a/MDANSE/Src/MDANSE/Chemistry/Structrures.py +++ /dev/null @@ -1,130 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -from io import StringIO - -from rdkit.Chem.inchi import MolToInchi -from rdkit.Chem.rdmolfiles import MolFromPDBBlock -from rdkit.Chem import rdDetermineBonds - - -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem -from MDANSE.MolecularDynamics.Trajectory import Trajectory -from MDANSE.IO.PDB import PDBFile -from MDANSE.MLogging import LOG - - -class DummyStringIO(StringIO): - def close(self): - pass - - def _close(self): - super(DummyStringIO, self).close() - - -class MoleculeTester: - """This is a wrapper that can be attached to an MDANSE ChemicalEntity - in order to convert it into a PDB string buffer and feed it into - a library that can identify molecules. - """ - - def __init__(self, chemical_entity, coordinates): - self.chemical_entity = chemical_entity - self.coordinates = coordinates - self.molecule_object = None - self.molecule_string = None - - def identify_molecule(self, frame_num: int = 0) -> str: - positions = self.coordinates - buffer = DummyStringIO() - temp_pdb = PDBFile(buffer, mode="w") - for natom, atom in enumerate(self.chemical_entity.atom_list): - coords = positions[natom] * 10.0 - atom_data = { - "position": coords, - "serial_number": atom.index, - "name": atom.name, - "occupancy": 1.0, - "element": atom.symbol, - "charge": "", # putting a number here makes it fail! - } - try: - temp_pdb.writeLine("ATOM", atom_data) - except: - LOG.warning(atom_data) - temp_pdb.close() - mol_object = MolFromPDBBlock(buffer.getvalue()) - buffer._close() - if mol_object is None: - return "" - try: - rdDetermineBonds.DetermineBonds(mol_object, charge=0) - except ValueError: - self.molecule_string = "" - else: - self.molecule_string = MolToInchi(mol_object) - self.molecule_object = mol_object - return self.molecule_string - - -class Topology: - def __init__( - self, trajectory: Trajectory = None, chem_system: ChemicalSystem = None - ) -> None: - self._trajectory = trajectory - self._chem_system = chem_system - - def scan_trajectory_frame(self, frame_num: int = 0): - unit_cell = self._chem_system.configuration.unit_cell - temp = unit_cell.abc_and_angles - abc = temp[0:3] # lattice constants in nm - angles = temp[3:6] # lattice angles in degrees - lattice_data = { - "a": round(abc[0] * 10.0, 3), - "b": round(abc[1] * 10.0, 3), - "c": round(abc[2] * 10.0, 3), - "alpha": round(angles[0], 3), - "beta": round(angles[1], 3), - "gamma": round(angles[2], 3), - "space_group": "", - "z": "", - } - buffer = DummyStringIO() - temp_pdb = PDBFile(buffer, mode="w") - # temp_pdb.writeLine('TITLE') - # temp_pdb.writeLine('AUTHOR') - temp_pdb.writeLine("CRYST1", lattice_data) - positions = ( - self._trajectory.trajectory.coordinates(frame_num) * 10.0 - ) # Angstroms, not nm - atoms = self._chem_system.atom_list - for natom in range(self._chem_system._total_number_of_atoms): - atom = atoms[natom] - atom_data = { - "position": positions[natom], - "serial_number": atom.index, - "name": atom.name, - "occupancy": 1.0, - "element": atom.symbol, - "charge": "", # putting a number here makes it fail! - } - try: - temp_pdb.writeLine("ATOM", atom_data) - except: - LOG.warning(atom_data) - temp_pdb.close() - mol_object = MolFromPDBBlock(buffer.getvalue()) - buffer._close() - return mol_object diff --git a/MDANSE/Src/MDANSE/Chemistry/__init__.py b/MDANSE/Src/MDANSE/Chemistry/__init__.py index fc4e9ef8e0..de19f487be 100644 --- a/MDANSE/Src/MDANSE/Chemistry/__init__.py +++ b/MDANSE/Src/MDANSE/Chemistry/__init__.py @@ -13,23 +13,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -import os -import json from MDANSE.Chemistry.Databases import ( AtomsDatabase, - MoleculesDatabase, - NucleotidesDatabase, - ResiduesDatabase, ) ATOMS_DATABASE = AtomsDatabase() - -MOLECULES_DATABASE = MoleculesDatabase() - -RESIDUES_DATABASE = ResiduesDatabase() - -NUCLEOTIDES_DATABASE = NucleotidesDatabase() - -with open(os.path.join(os.path.dirname(__file__), "residues_alt_names.json"), "r") as f: - RESIDUE_ALT_NAMES = json.load(f) diff --git a/MDANSE/Src/MDANSE/Chemistry/molecules.json b/MDANSE/Src/MDANSE/Chemistry/molecules.json deleted file mode 100644 index 9628e7f694..0000000000 --- a/MDANSE/Src/MDANSE/Chemistry/molecules.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "WAT": { - "alternatives": [ - "H2O", - "water", - "TIP3" - ], - "atoms": { - "OW": { - "symbol": "O", - "alternatives": [ - "O", - "OH2" - ], - "groups": [], - "bonds": [ - "HW1", - "HW2" - ] - }, - "HW2": { - "symbol": "H", - "alternatives": [ - "H2" - ], - "groups": [], - "bonds": [ - "OW" - ] - }, - "HW1": { - "symbol": "H", - "alternatives": [ - "H1" - ], - "groups": [], - "bonds": [ - "OW" - ] - } - } - } -} \ No newline at end of file diff --git a/MDANSE/Src/MDANSE/Chemistry/nucleotides.json b/MDANSE/Src/MDANSE/Chemistry/nucleotides.json deleted file mode 100644 index 95abcc94c8..0000000000 --- a/MDANSE/Src/MDANSE/Chemistry/nucleotides.json +++ /dev/null @@ -1,3655 +0,0 @@ -{ - "A": { - "is_3ter_terminus": false, - "atoms": { - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N9", - "O4'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "C8": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "H8", - "N7", - "N9" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "H2" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N1", - "N6" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "N7" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "N9" - ] - }, - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "HO2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "O2'" - ] - }, - "N9": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C4", - "C8" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "O2'" - ] - }, - "O2'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "HO2'" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C6" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4" - ] - }, - "N6": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6", - "H61", - "H62" - ] - }, - "N7": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "C8" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H8": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C8" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "H2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "H61": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N6" - ] - }, - "H62": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N6" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - } - }, - "alternatives": [ - "RA" - ], - "is_5ter_terminus": false - }, - "C": { - "is_3ter_terminus": false, - "atoms": { - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "H6": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N1", - "O4'" - ] - }, - "HO2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "O2'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "O2'" - ] - }, - "O2'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "HO2'" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C2", - "C6" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4" - ] - }, - "N4": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "H41", - "H42" - ] - }, - "O2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "H5": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5" - ] - }, - "H41": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N4" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "O2" - ] - }, - "H42": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N4" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H6", - "N1" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "H5" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "N4" - ] - } - }, - "alternatives": [ - "RC" - ], - "is_5ter_terminus": false - }, - "G": { - "is_3ter_terminus": false, - "atoms": { - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "H21": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N2" - ] - }, - "H22": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N2" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "O6": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "C8": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "H8", - "N7", - "N9" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N2", - "N3" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N1", - "O6" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "N7" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "N9" - ] - }, - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "HO2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "O2'" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N9", - "O4'" - ] - }, - "N9": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C4", - "C8" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "O2'" - ] - }, - "O2'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "HO2'" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C6", - "H1" - ] - }, - "N2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "H21", - "H22" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4" - ] - }, - "N7": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "C8" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H8": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C8" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "H1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - } - }, - "alternatives": [ - "RG" - ], - "is_5ter_terminus": false - }, - "DG": { - "is_3ter_terminus": false, - "atoms": { - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "H21": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N2" - ] - }, - "H22": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N2" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "O6": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "H2''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "C8": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "H8", - "N7", - "N9" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N2", - "N3" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N1", - "O6" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "N7" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "N9" - ] - }, - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N9", - "O4'" - ] - }, - "N9": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C4", - "C8" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "H2''" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C6", - "H1" - ] - }, - "N2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "H21", - "H22" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4" - ] - }, - "N7": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "C8" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H8": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C8" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "H1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - } - }, - "alternatives": [], - "is_5ter_terminus": false - }, - "DC": { - "is_3ter_terminus": false, - "atoms": { - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "H6": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N1", - "O4'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "H2''" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C2", - "C6" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4" - ] - }, - "N4": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "H41", - "H42" - ] - }, - "O2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "H2''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "H5": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5" - ] - }, - "H41": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N4" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "O2" - ] - }, - "H42": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N4" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H6", - "N1" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "H5" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "N4" - ] - } - }, - "alternatives": [], - "is_5ter_terminus": false - }, - "DA": { - "is_3ter_terminus": false, - "atoms": { - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N9", - "O4'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "H2''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "C8": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "H8", - "N7", - "N9" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "H2" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N1", - "N6" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "N7" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "N9" - ] - }, - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "N9": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C4", - "C8" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "H2''" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C6" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4" - ] - }, - "N6": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6", - "H61", - "H62" - ] - }, - "N7": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "C8" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H8": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C8" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "H2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "H61": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N6" - ] - }, - "H62": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N6" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - } - }, - "alternatives": [], - "is_5ter_terminus": false - }, - "5T1": { - "is_3ter_terminus": false, - "atoms": { - "HO5'": { - "replaces": [ - "OP1", - "OP2", - "P" - ], - "o5prime_connected": true, - "groups": [], - "bonds": [ - "O5'" - ], - "symbol": "H", - "alternatives": [] - } - }, - "alternatives": [], - "is_5ter_terminus": true - }, - "U": { - "is_3ter_terminus": false, - "atoms": { - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "H6": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N1", - "O4'" - ] - }, - "HO2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "O2'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "O2'" - ] - }, - "O2'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "HO2'" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C2", - "C6" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O4": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4" - ] - }, - "O2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "H3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N3" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4", - "H3" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "H5": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "O2" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H6", - "N1" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "H5" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "O4" - ] - } - }, - "alternatives": [ - "RU" - ], - "is_5ter_terminus": false - }, - "3T1": { - "is_3ter_terminus": true, - "atoms": { - "HO3'": { - "o3prime_connected": true, - "replaces": [], - "groups": [], - "bonds": [ - "O3'" - ], - "symbol": "H", - "alternatives": [] - } - }, - "alternatives": [], - "is_5ter_terminus": false - }, - "DT": { - "is_3ter_terminus": false, - "atoms": { - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "H6": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N1", - "O4'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O4": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4" - ] - }, - "O2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "H2''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "O2" - ] - }, - "C7": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H71", - "H72", - "H73" - ] - }, - "H73": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C7" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "C7" - ] - }, - "H71": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C7" - ] - }, - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "H2''" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C2", - "C6" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4", - "H3" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H72": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C7" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H6", - "N1" - ] - }, - "H3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N3" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "O4" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - } - }, - "alternatives": [], - "is_5ter_terminus": false - }, - "DU": { - "is_3ter_terminus": false, - "atoms": { - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "H6": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N1", - "O4'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "H2''" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C2", - "C6" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O4": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4" - ] - }, - "O2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "H2''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "H3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N3" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4", - "H3" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "H5": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "O2" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H6", - "N1" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "H5" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "O4" - ] - } - }, - "alternatives": [], - "is_5ter_terminus": false - }, - "T": { - "is_3ter_terminus": false, - "atoms": { - "OP1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "OP2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "P" - ] - }, - "H6": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C6" - ] - }, - "C3'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "C4'", - "H3'", - "O3'" - ] - }, - "C1'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "H1'", - "N1", - "O4'" - ] - }, - "C5'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'", - "H5'", - "H5''", - "O5'" - ] - }, - "H2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'" - ] - }, - "O4": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4" - ] - }, - "O2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2" - ] - }, - "H5'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "H3'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'" - ] - }, - "O4'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'" - ] - }, - "C2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N1", - "N3", - "O2" - ] - }, - "C7": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H71", - "H72", - "H73" - ] - }, - "H73": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C7" - ] - }, - "C5": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C4", - "C6", - "C7" - ] - }, - "H71": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C7" - ] - }, - "H5''": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C5'" - ] - }, - "HO2'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "O2'" - ] - }, - "P": { - "symbol": "P", - "alternatives": [], - "groups": [ - "phopshate" - ], - "bonds": [ - "O5'", - "OP1", - "OP2", - "-R" - ] - }, - "C4'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C3'", - "C5'", - "H4'", - "O4'" - ] - }, - "C2'": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'", - "C3'", - "H2'", - "O2'" - ] - }, - "O2'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C2'", - "HO2'" - ] - }, - "N1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C1'", - "C2", - "C6" - ] - }, - "N3": { - "symbol": "N", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C2", - "C4", - "H3" - ] - }, - "H1'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C1'" - ] - }, - "H4'": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sugar" - ], - "bonds": [ - "C4'" - ] - }, - "H72": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C7" - ] - }, - "O5'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C5'", - "P" - ] - }, - "C6": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "H6", - "N1" - ] - }, - "H3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "N3" - ] - }, - "C4": { - "symbol": "C", - "alternatives": [], - "groups": [ - "base" - ], - "bonds": [ - "C5", - "N3", - "O4" - ] - }, - "O3'": { - "symbol": "O", - "alternatives": [], - "groups": [ - "phosphate" - ], - "bonds": [ - "C3'", - "+R" - ] - } - }, - "alternatives": [ - "RT" - ], - "is_5ter_terminus": false - } -} \ No newline at end of file diff --git a/MDANSE/Src/MDANSE/Chemistry/residues.json b/MDANSE/Src/MDANSE/Chemistry/residues.json deleted file mode 100644 index e8912644d7..0000000000 --- a/MDANSE/Src/MDANSE/Chemistry/residues.json +++ /dev/null @@ -1,4636 +0,0 @@ -{ - "HIP": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CE1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "ND1", - "NE2", - "HE1" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD2", - "ND1" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CD2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "HD2", - "NE2" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HE1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1" - ] - }, - "HD1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "ND1" - ] - }, - "HE2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NE2" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "ND1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1", - "CG", - "HD1" - ] - }, - "NE2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2", - "CE1", - "HE2" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "ILE": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "1HD1": { - "symbol": "H", - "alternatives": [ - "HD11", - "HD1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "3HD1": { - "symbol": "H", - "alternatives": [ - "HD13", - "HD3" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "CG1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD1", - "2HG1", - "3HG1" - ] - }, - "CD1": { - "symbol": "C", - "alternatives": [ - "CD" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG1", - "1HD1", - "2HD1", - "3HD1" - ] - }, - "HB": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "2HG2": { - "symbol": "H", - "alternatives": [ - "HG22" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "2HG1": { - "symbol": "H", - "alternatives": [ - "HG12" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG1" - ] - }, - "CG2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "1HG2", - "2HG2", - "3HG2" - ] - }, - "3HG1": { - "symbol": "H", - "alternatives": [ - "HG13", - "HG11", - "1HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG1" - ] - }, - "3HG2": { - "symbol": "H", - "alternatives": [ - "HG23" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "2HD1": { - "symbol": "H", - "alternatives": [ - "HD12", - "HD2" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "1HG2": { - "symbol": "H", - "alternatives": [ - "HG21" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG1", - "CG2", - "HB" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "GLN": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "1HE2": { - "symbol": "H", - "alternatives": [ - "HE21" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "NE2" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD", - "HG2", - "HG3" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "CD": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "NE2", - "OE1" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "2HE2": { - "symbol": "H", - "alternatives": [ - "HE22" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "NE2" - ] - }, - "HG2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "HG3": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "NE2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD", - "1HE2", - "2HE2" - ] - }, - "OE1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "NT1": { - "is_n_terminus": true, - "atoms": { - "HT3": { - "replaces": [ - "H" - ], - "groups": [ - "backbone" - ], - "bonds": [ - "N" - ], - "nter_connected": true, - "symbol": "H", - "alternatives": [ - "3HT", - "H3", - "3H" - ] - }, - "HT2": { - "replaces": [ - "H" - ], - "groups": [ - "backbone" - ], - "bonds": [ - "N" - ], - "nter_connected": true, - "symbol": "H", - "alternatives": [ - "2HT", - "H2", - "2H" - ] - }, - "HT1": { - "replaces": [ - "H" - ], - "groups": [ - "backbone" - ], - "bonds": [ - "N" - ], - "nter_connected": true, - "symbol": "H", - "alternatives": [ - "1HT", - "H1", - "1H" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "THR": { - "is_n_terminus": false, - "atoms": { - "2HG2": { - "symbol": "H", - "alternatives": [ - "HG22" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CG2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "1HG2", - "2HG2", - "3HG2" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG2", - "HB", - "OG1" - ] - }, - "3HG2": { - "symbol": "H", - "alternatives": [ - "HG23" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "OG1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "HG1" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "HG1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "OG1" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "1HG2": { - "symbol": "H", - "alternatives": [ - "HG21" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HB": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "GLY": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "HA2", - "HA3", - "N" - ] - }, - "HA3": { - "symbol": "H", - "alternatives": [ - "HA1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA" - ] - }, - "HA2": { - "symbol": "H", - "alternatives": [ - "HA" - ], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "TRP": { - "is_n_terminus": false, - "atoms": { - "HH2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CH2" - ] - }, - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CZ2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE2", - "CH2", - "HZ2" - ] - }, - "CZ3": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE3", - "CH2", - "HZ3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "HZ2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CZ2" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD1", - "CD2" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CE3": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2", - "CZ3", - "HE3" - ] - }, - "CD1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "HD1", - "NE1" - ] - }, - "CD2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE2", - "CE3", - "CG" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HE1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NE1" - ] - }, - "HD1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "HE3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE3" - ] - }, - "CH2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CZ2", - "CZ3", - "HH2" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HZ3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CZ3" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "NE1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1", - "CE2", - "HE1" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CE2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2", - "CZ2", - "NE1" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "GLU": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "HG2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "HG3": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "CD": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "OE1", - "OE2" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD", - "HG2", - "HG3" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "OE2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "OE1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "CT1": { - "is_n_terminus": false, - "atoms": { - "OXT": { - "cter_connected": true, - "replaces": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C" - ], - "symbol": "O", - "alternatives": [ - "OT", - "OT2" - ] - } - }, - "alternatives": [], - "is_c_terminus": true - }, - "CYS": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "SG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "SG": { - "symbol": "S", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "HG" - ] - }, - "HG": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "SG" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "ASP": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "OD1", - "OD2" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "OD1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "OD2": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "SER": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "OG": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "HG" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "HB2", - "HB3", - "OG" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HG": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "OG" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "LYS": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD", - "HG2", - "HG3" - ] - }, - "CE": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD", - "NZ", - "HE2", - "HE3" - ] - }, - "CD": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "CE", - "HD2", - "HD3" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HD3": { - "symbol": "H", - "alternatives": [ - "HD1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "HE2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE" - ] - }, - "HE3": { - "symbol": "H", - "alternatives": [ - "HE1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone" - ], - "bonds": [ - "N" - ] - }, - "HG2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "HG3": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "NZ": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE", - "HZ1", - "HZ2", - "HZ3" - ] - }, - "HZ1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NZ" - ] - }, - "HZ3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NZ" - ] - }, - "HZ2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NZ" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "PRO": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HD3": { - "symbol": "H", - "alternatives": [ - "HD1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "HG2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "HG3": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "CD": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "HD2", - "HD3", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CG", - "HG2", - "HG3" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "CD", - "-R" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "CYX": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "SG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "SG": { - "symbol": "S", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "PHE": { - "is_n_terminus": false, - "atoms": { - "HZ": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CZ" - ] - }, - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CE1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1", - "CZ", - "HE1" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD1", - "CD2" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CZ": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1", - "CE2", - "HZ" - ] - }, - "CE2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2", - "CZ", - "HE2" - ] - }, - "CD1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1", - "CG", - "HD1" - ] - }, - "CD2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE2", - "CG", - "HD2" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HE1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1" - ] - }, - "HD1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "HE2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE2" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "ALA": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "HB1", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HB1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "MET": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "HG2", - "HG3", - "SD" - ] - }, - "CE": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "HE1", - "HE2", - "HE3", - "SD" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HE1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE" - ] - }, - "HE2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE" - ] - }, - "HE3": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE" - ] - }, - "HG2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "HG3": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "SD": { - "symbol": "S", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE", - "CG" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "HID": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CE1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "ND1", - "NE2", - "HE1" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD2", - "ND1" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CD2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "HD2", - "NE2" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "HE1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1" - ] - }, - "HD1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "ND1" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "ND1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1", - "CG", - "HD1" - ] - }, - "NE2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2", - "CE1" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [ - "HSD" - ], - "is_c_terminus": false - }, - "HIE": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CE1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "ND1", - "NE2", - "HE1" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD2", - "ND1" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CD2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "HD2", - "NE2" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "HE1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1" - ] - }, - "HE2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NE2" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "ND1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1", - "CG" - ] - }, - "NE2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2", - "CE1", - "HE2" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [ - "HSE" - ], - "is_c_terminus": false - }, - "ARG": { - "is_n_terminus": false, - "atoms": { - "2HH2": { - "symbol": "H", - "alternatives": [ - "HH22" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "NH2" - ] - }, - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "2HH1": { - "symbol": "H", - "alternatives": [ - "HH12" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "NH1" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD", - "HG2", - "HG3" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "HG3": { - "symbol": "H", - "alternatives": [ - "HG1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "CZ": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NE", - "NH1", - "NH2" - ] - }, - "NH1": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "1HH1", - "2HH1" - ] - }, - "NH2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "1HH2", - "2HH2" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HE": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "NE" - ] - }, - "HD3": { - "symbol": "H", - "alternatives": [ - "HD1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD" - ] - }, - "HG2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "NE": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD", - "CZ", - "HE" - ] - }, - "CD": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "NE", - "HD2", - "HD3" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "1HH2": { - "symbol": "H", - "alternatives": [ - "HH21" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "NH2" - ] - }, - "1HH1": { - "symbol": "H", - "alternatives": [ - "HH11" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "NH1" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "VAL": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CG1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "1HG1", - "2HG1", - "3HG1" - ] - }, - "HB": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "2HG2": { - "symbol": "H", - "alternatives": [ - "HG22" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "2HG1": { - "symbol": "H", - "alternatives": [ - "HG12" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG1" - ] - }, - "CG2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "1HG2", - "2HG2", - "3HG2" - ] - }, - "3HG1": { - "symbol": "H", - "alternatives": [ - "HG13" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG1" - ] - }, - "3HG2": { - "symbol": "H", - "alternatives": [ - "HG23" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "1HG2": { - "symbol": "H", - "alternatives": [ - "HG21" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG2" - ] - }, - "1HG1": { - "symbol": "H", - "alternatives": [ - "HG11" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG1" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG1", - "CG2", - "HB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "ASN": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "1HD2": { - "symbol": "H", - "alternatives": [ - "HD21" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "ND2" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "ND2", - "OD1" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "2HD2": { - "symbol": "H", - "alternatives": [ - "HD22" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "ND2" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "OD1": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "ND2": { - "symbol": "N", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "1HD2", - "2HD2" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "TYR": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "CE1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1", - "CZ", - "HE1" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD1", - "CD2" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "CZ": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1", - "CE2", - "OH" - ] - }, - "HH": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "OH" - ] - }, - "CD1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1", - "CG", - "HD1" - ] - }, - "CD2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE2", - "CG", - "HD2" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HE1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE1" - ] - }, - "HD1": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "HE2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CE2" - ] - }, - "OH": { - "symbol": "O", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CZ", - "HH" - ] - }, - "CE2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2", - "CZ", - "HE2" - ] - }, - "HD2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - }, - "LEU": { - "is_n_terminus": false, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "O", - "+R" - ] - }, - "1HD2": { - "symbol": "H", - "alternatives": [ - "HD21" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "1HD1": { - "symbol": "H", - "alternatives": [ - "HD11" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "CG": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB", - "CD1", - "CD2", - "HG" - ] - }, - "O": { - "symbol": "O", - "alternatives": [ - "OT1" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "C" - ] - }, - "N": { - "symbol": "N", - "alternatives": [], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "CA", - "H", - "-R" - ] - }, - "3HD1": { - "symbol": "H", - "alternatives": [ - "HD13" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "3HD2": { - "symbol": "H", - "alternatives": [ - "HD23" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "CD1": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "1HD1", - "2HD1", - "3HD1" - ] - }, - "CD2": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG", - "1HD2", - "2HD2", - "3HD2" - ] - }, - "HA": { - "symbol": "H", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "CA" - ] - }, - "HG": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CG" - ] - }, - "2HD2": { - "symbol": "H", - "alternatives": [ - "HD22" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD2" - ] - }, - "2HD1": { - "symbol": "H", - "alternatives": [ - "HD12" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CD1" - ] - }, - "H": { - "symbol": "H", - "alternatives": [ - "HN" - ], - "groups": [ - "backbone", - "peptide" - ], - "bonds": [ - "N" - ] - }, - "CB": { - "symbol": "C", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CA", - "CG", - "HB2", - "HB3" - ] - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": [ - "backbone" - ], - "bonds": [ - "C", - "CB", - "HA", - "N" - ] - }, - "HB3": { - "symbol": "H", - "alternatives": [ - "HB1" - ], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - }, - "HB2": { - "symbol": "H", - "alternatives": [], - "groups": [ - "sidechain" - ], - "bonds": [ - "CB" - ] - } - }, - "alternatives": [], - "is_c_terminus": false - } -} \ No newline at end of file diff --git a/MDANSE/Src/MDANSE/Chemistry/residues_alt_names.json b/MDANSE/Src/MDANSE/Chemistry/residues_alt_names.json deleted file mode 100644 index e9a7bdda80..0000000000 --- a/MDANSE/Src/MDANSE/Chemistry/residues_alt_names.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "HIS": ["HID", "HIE", "HIP"], - "CYS": ["CYS", "CYX"] -} \ No newline at end of file diff --git a/MDANSE/Src/MDANSE/Extensions/.gitignore b/MDANSE/Src/MDANSE/Extensions/.gitignore deleted file mode 100644 index 584ae1596f..0000000000 --- a/MDANSE/Src/MDANSE/Extensions/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.o -*.so -*.pyd \ No newline at end of file diff --git a/MDANSE/Src/MDANSE/Extensions/__init__.py b/MDANSE/Src/MDANSE/Extensions/__init__.py deleted file mode 100644 index 268c9cc94f..0000000000 --- a/MDANSE/Src/MDANSE/Extensions/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# diff --git a/MDANSE/Src/MDANSE/Framework/AtomSelector/all_selector.py b/MDANSE/Src/MDANSE/Framework/AtomSelector/all_selector.py index 46e327b071..8b1813e10c 100644 --- a/MDANSE/Src/MDANSE/Framework/AtomSelector/all_selector.py +++ b/MDANSE/Src/MDANSE/Framework/AtomSelector/all_selector.py @@ -14,11 +14,11 @@ # along with this program. If not, see . # from typing import Union -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.MolecularDynamics.Trajectory import Trajectory def select_all( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects all atoms in the chemical system except for the dummy atoms. @@ -36,7 +36,19 @@ def select_all( All atom indices except for dummy atoms or a bool if checking match. """ + system = trajectory.chemical_system if check_exists: return True else: - return set([at.index for at in system.atom_list if at.element != "dummy"]) + dummy_list = [] + atom_list = system.atom_list + for atm in system._unique_elements: + if trajectory.get_atom_property(atm, "dummy"): + dummy_list.append(atm) + return set( + [ + index + for index in system._atom_indices + if atom_list[index] not in dummy_list + ] + ) diff --git a/MDANSE/Src/MDANSE/Framework/AtomSelector/atom_selectors.py b/MDANSE/Src/MDANSE/Framework/AtomSelector/atom_selectors.py index 9c8c8653be..1b7a054818 100644 --- a/MDANSE/Src/MDANSE/Framework/AtomSelector/atom_selectors.py +++ b/MDANSE/Src/MDANSE/Framework/AtomSelector/atom_selectors.py @@ -14,8 +14,7 @@ # along with this program. If not, see . # from typing import Union -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem -from MDANSE.Chemistry import ATOMS_DATABASE +from MDANSE.MolecularDynamics.Trajectory import Trajectory __all__ = [ @@ -30,7 +29,7 @@ def select_element( - system: ChemicalSystem, symbol: str, check_exists: bool = False + trajectory: Trajectory, symbol: str, check_exists: bool = False ) -> Union[set[int], bool]: """Selects all atoms for the input element. @@ -48,7 +47,8 @@ def select_element( Union[set[int], bool] The atom indices of the matched atoms. """ - pattern = f"[#{ATOMS_DATABASE.get_atom_property(symbol, 'atomic_number')}]" + system = trajectory.chemical_system + pattern = f"[#{trajectory.get_atom_property(symbol, 'atomic_number')}]" if check_exists: return system.has_substructure_match(pattern) else: @@ -56,7 +56,7 @@ def select_element( def select_dummy( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects all dummy atoms in the chemical system. @@ -72,17 +72,30 @@ def select_dummy( Union[set[int], bool] All dummy atom indices or a bool if checking match. """ + system = trajectory.chemical_system + dummy_list = ["Du", "dummy"] if check_exists: for atm in system.atom_list: - if atm.element == "dummy": + if atm in dummy_list: + return True + elif trajectory.get_atom_property(atm, "dummy"): return True return False else: - return set([at.index for at in system.atom_list if at.element == "dummy"]) + for atm in system._unique_elements: + if trajectory.get_atom_property(atm, "dummy"): + dummy_list.append(atm) + return set( + [ + index + for index, element in enumerate(system.atom_list) + if element in dummy_list + ] + ) def select_atom_name( - system: ChemicalSystem, name: str, check_exists: bool = False + trajectory: Trajectory, name: str, check_exists: bool = False ) -> Union[set[int], bool]: """Selects all atoms with the input name in the chemical system. @@ -100,17 +113,19 @@ def select_atom_name( Union[set[int], bool] All atom indices or a bool if checking match. """ + system = trajectory.chemical_system if check_exists: - for atm in system.atom_list: - if atm.name == name: - return True + if name in system.atom_list: + return True return False else: - return set([at.index for at in system.atom_list if at.name == name]) + return set( + [index for index, element in enumerate(system.atom_list) if element == name] + ) def select_atom_fullname( - system: ChemicalSystem, fullname: str, check_exists: bool = False + trajectory: Trajectory, fullname: str, check_exists: bool = False ) -> Union[set[int], bool]: """Selects all atoms with the input fullname in the chemical system. @@ -128,17 +143,19 @@ def select_atom_fullname( Union[set[int], bool] All atom indices or a bool if checking match. """ + system = trajectory.chemical_system if check_exists: - for atm in system.atom_list: - if atm.full_name == fullname: - return True + if fullname in system.name_list: + return True return False else: - return set([at.index for at in system.atom_list if at.full_name == fullname]) + return set( + [index for index, name in enumerate(system.name_list) if name == fullname] + ) def select_hs_on_element( - system: ChemicalSystem, symbol: str, check_exists: bool = False + trajectory: Trajectory, symbol: str, check_exists: bool = False ) -> Union[set[int], bool]: """Selects all H atoms bonded to the input element. @@ -156,7 +173,8 @@ def select_hs_on_element( Union[set[int], bool] The atom indices of the matched atoms. """ - num = ATOMS_DATABASE.get_atom_property(symbol, "atomic_number") + system = trajectory.chemical_system + num = trajectory.get_atom_property(symbol, "atomic_number") if check_exists: return system.has_substructure_match(f"[#{num}]~[H]") else: @@ -166,7 +184,7 @@ def select_hs_on_element( def select_hs_on_heteroatom( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects all H atoms bonded to any atom except carbon and hydrogen. @@ -183,6 +201,7 @@ def select_hs_on_heteroatom( Union[set[int], bool] The atom indices of the matched atoms. """ + system = trajectory.chemical_system if check_exists: return system.has_substructure_match("[!#6&!#1]~[H]") else: @@ -192,7 +211,7 @@ def select_hs_on_heteroatom( def select_index( - system: ChemicalSystem, index: Union[int, str], check_exists: bool = False + trajectory: Trajectory, index: Union[int, str], check_exists: bool = False ) -> Union[set[int], bool]: """Selects atom with index - just returns the set with the index in it. diff --git a/MDANSE/Src/MDANSE/Framework/AtomSelector/group_selectors.py b/MDANSE/Src/MDANSE/Framework/AtomSelector/group_selectors.py index dfddde02a2..0bc1e85672 100644 --- a/MDANSE/Src/MDANSE/Framework/AtomSelector/group_selectors.py +++ b/MDANSE/Src/MDANSE/Framework/AtomSelector/group_selectors.py @@ -14,13 +14,13 @@ # along with this program. If not, see . # from typing import Union -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.MolecularDynamics.Trajectory import Trajectory __all__ = [ "select_primary_amine", "select_hydroxy", - "select_methly", + "select_methyl", "select_phosphate", "select_sulphate", "select_thiol", @@ -28,7 +28,7 @@ def select_primary_amine( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects the N and H atoms of all primary amines. @@ -44,6 +44,7 @@ def select_primary_amine( Union[set[int], bool] The atom indices of the matched atoms or a bool if checking match. """ + system = trajectory.chemical_system pattern = "[#7X3;H2;!$([#7][#6X3][!#6]);!$([#7][#6X2][!#6])](~[H])~[H]" if check_exists: return system.has_substructure_match(pattern) @@ -52,7 +53,7 @@ def select_primary_amine( def select_hydroxy( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects the O and H atoms of all hydroxy groups including water. @@ -68,6 +69,7 @@ def select_hydroxy( Union[set[int], bool] The atom indices of the matched atoms or a bool if checking match. """ + system = trajectory.chemical_system pattern = "[#8;H1,H2]~[H]" if check_exists: return system.has_substructure_match(pattern) @@ -75,8 +77,8 @@ def select_hydroxy( return system.get_substructure_matches(pattern) -def select_methly( - system: ChemicalSystem, check_exists: bool = False +def select_methyl( + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects the C and H atoms of all methyl groups. @@ -92,6 +94,7 @@ def select_methly( Union[set[int], bool] The atom indices of the matched atoms or a bool if checking match. """ + system = trajectory.chemical_system pattern = "[#6;H3](~[H])(~[H])~[H]" if check_exists: return system.has_substructure_match(pattern) @@ -100,7 +103,7 @@ def select_methly( def select_phosphate( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects the P and O atoms of all phosphate groups. @@ -116,6 +119,7 @@ def select_phosphate( set[int] The atom indices of the matched atoms or a bool if checking match. """ + system = trajectory.chemical_system pattern = "[#15X4](~[#8])(~[#8])(~[#8])~[#8]" if check_exists: return system.has_substructure_match(pattern) @@ -124,7 +128,7 @@ def select_phosphate( def select_sulphate( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects the S and O atoms of all sulphate groups. @@ -140,6 +144,7 @@ def select_sulphate( Union[set[int], bool] The atom indices of the matched atoms or a bool if checking match. """ + system = trajectory.chemical_system pattern = "[#16X4](~[#8])(~[#8])(~[#8])~[#8]" if check_exists: return system.has_substructure_match(pattern) @@ -148,7 +153,7 @@ def select_sulphate( def select_thiol( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects the S and H atoms of all thiol groups. @@ -164,6 +169,7 @@ def select_thiol( Union[set[int], bool] The atom indices of the matched atoms or a bool if checking match. """ + system = trajectory.chemical_system pattern = "[#16X2;H1]~[H]" if check_exists: return system.has_substructure_match(pattern) diff --git a/MDANSE/Src/MDANSE/Framework/AtomSelector/molecule_selectors.py b/MDANSE/Src/MDANSE/Framework/AtomSelector/molecule_selectors.py index eeab32e155..366f599833 100644 --- a/MDANSE/Src/MDANSE/Framework/AtomSelector/molecule_selectors.py +++ b/MDANSE/Src/MDANSE/Framework/AtomSelector/molecule_selectors.py @@ -14,7 +14,7 @@ # along with this program. If not, see . # from typing import Union -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.MolecularDynamics.Trajectory import Trajectory __all__ = [ @@ -23,7 +23,7 @@ def select_water( - system: ChemicalSystem, check_exists: bool = False + trajectory: Trajectory, check_exists: bool = False ) -> Union[set[int], bool]: """Selects the O and H atoms of all water molecules. @@ -39,6 +39,7 @@ def select_water( Union[set[int], bool] The atom indices of the matched atoms or a bool if checking match. """ + system = trajectory.chemical_system pattern = "[#8X2;H2](~[H])~[H]" if check_exists: return system.has_substructure_match(pattern) diff --git a/MDANSE/Src/MDANSE/Framework/AtomSelector/selector.py b/MDANSE/Src/MDANSE/Framework/AtomSelector/selector.py index 6dac3ca492..f98d9aafdc 100644 --- a/MDANSE/Src/MDANSE/Framework/AtomSelector/selector.py +++ b/MDANSE/Src/MDANSE/Framework/AtomSelector/selector.py @@ -16,7 +16,8 @@ import json import copy from typing import Union -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem +from MDANSE.MolecularDynamics.Trajectory import Trajectory from MDANSE.Framework.AtomSelector.all_selector import select_all from MDANSE.Framework.AtomSelector.atom_selectors import * from MDANSE.Framework.AtomSelector.group_selectors import * @@ -24,7 +25,7 @@ class Selector: - """Used to get the indexes of a subset of atoms of a chemical system. + """Used to get the indices of a subset of atoms of a chemical system. Attributes ---------- @@ -62,7 +63,7 @@ class Selector: "hs_on_heteroatom": select_hs_on_heteroatom, "primary_amine": select_primary_amine, "hydroxy": select_hydroxy, - "methyl": select_methly, + "methyl": select_methyl, "phosphate": select_phosphate, "sulphate": select_sulphate, "thiol": select_thiol, @@ -82,18 +83,20 @@ class Selector: "index": "index", } - def __init__(self, system: ChemicalSystem) -> None: + def __init__(self, trajectory: Trajectory) -> None: """ Parameters ---------- - system: ChemicalSystem + trajectory: Trajectory The chemical system to apply the selection to. """ + system = trajectory.chemical_system self.system = system - self.all_idxs = set([at.index for at in system.atom_list]) + self.trajectory = trajectory + self.all_idxs = set(system._atom_indices) self.settings = copy.deepcopy(self._default) - symbols = set([at.symbol for at in system.atom_list]) + symbols = set(system.atom_list) # all possible values for the system self._kwarg_vals = { "element": symbols, @@ -101,11 +104,11 @@ def __init__(self, system: ChemicalSystem) -> None: [ symbol for symbol in symbols - if select_hs_on_element(system, symbol, check_exists=True) + if select_hs_on_element(trajectory, symbol, check_exists=True) ] ), - "name": set([at.name for at in system.atom_list]), - "fullname": set([at.full_name for at in system.atom_list]), + "name": set(system.atom_list), + "fullname": set(system.name_list), "index": self.all_idxs, } @@ -116,7 +119,9 @@ def __init__(self, system: ChemicalSystem) -> None: for k1 in v0.keys(): self.match_exists[k0][k1] = True else: - self.match_exists[k0] = self._funcs[k0](self.system, check_exists=True) + self.match_exists[k0] = self._funcs[k0]( + self.trajectory, check_exists=True + ) self.settings = self.create_default_settings() @@ -171,12 +176,12 @@ def update_settings( self.settings[k0] = v0 def get_idxs(self) -> set[int]: - """The atom indexes after applying the selection to the system. + """The atom indices after applying the selection to the system. Returns ------- set[int] - The atoms indexes. + The atoms indices. """ idxs = set([]) @@ -193,7 +198,7 @@ def get_idxs(self) -> set[int]: if not switch: continue - idxs.update(self._funcs[k](self.system, **arg)) + idxs.update(self._funcs[k](self.trajectory, **arg)) return idxs @@ -206,7 +211,7 @@ def update_with_idxs(self, idxs: set[int]) -> None: Parameters ---------- idxs : set[int] - With the indexes of the atom selection. + With the indices of the atom selection. """ new_settings = self.create_default_settings() new_settings["all"] = False @@ -228,7 +233,7 @@ def update_with_idxs(self, idxs: set[int]) -> None: if not switch: continue - selection = self._funcs[k](self.system, **arg) + selection = self._funcs[k](self.trajectory, **arg) if not idxs.issuperset(selection): continue diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/AtomSelectionConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/AtomSelectionConfigurator.py index 679d904f45..5d7be86ca9 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/AtomSelectionConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/AtomSelectionConfigurator.py @@ -13,8 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -import operator -from MDANSE.Chemistry import ATOMS_DATABASE + from MDANSE.Framework.Configurators.IConfigurator import IConfigurator from MDANSE.Framework.AtomSelector import Selector @@ -49,7 +48,7 @@ def configure(self, value: str) -> None: self.error_status = "Invalid input value." return - selector = Selector(trajConfig["instance"].chemical_system) + selector = Selector(trajConfig["instance"]) if not selector.check_valid_json_settings(value): self.error_status = "Invalid JSON string." return @@ -57,26 +56,23 @@ def configure(self, value: str) -> None: self["value"] = value selector.load_from_json(value) - indexes = selector.get_idxs() + indices = selector.get_idxs() - self["flatten_indexes"] = sorted(list(indexes)) + self["flatten_indices"] = sorted(list(indices)) trajConfig = self._configurable[self._dependencies["trajectory"]] - atoms = sorted( - trajConfig["instance"].chemical_system.atom_list, - key=operator.attrgetter("index"), - ) - selectedAtoms = [atoms[idx] for idx in self["flatten_indexes"]] + atoms = trajConfig["instance"].chemical_system.atom_list + selectedAtoms = [atoms[idx] for idx in self["flatten_indices"]] - self["selection_length"] = len(self["flatten_indexes"]) - self["indexes"] = [[idx] for idx in self["flatten_indexes"]] + self["selection_length"] = len(self["flatten_indices"]) + self["indices"] = [[idx] for idx in self["flatten_indices"]] - self["elements"] = [[at.symbol] for at in selectedAtoms] - self["names"] = [at.symbol for at in selectedAtoms] + self["elements"] = [[at] for at in selectedAtoms] + self["names"] = [at for at in selectedAtoms] self["unique_names"] = sorted(set(self["names"])) self["masses"] = [ - [ATOMS_DATABASE.get_atom_property(n, "atomic_weight")] + [trajConfig["instance"].get_atom_property(n, "atomic_weight")] for n in self["names"] ] if self["selection_length"] == 0: @@ -110,15 +106,15 @@ def get_total_natoms(self) -> int: """ return len(self["names"]) - def get_indexes(self): - indexesPerElement = {} + def get_indices(self): + indicesPerElement = {} for i, v in enumerate(self["names"]): - if v in indexesPerElement: - indexesPerElement[v].extend(self["indexes"][i]) + if v in indicesPerElement: + indicesPerElement[v].extend(self["indices"][i]) else: - indexesPerElement[v] = self["indexes"][i][:] + indicesPerElement[v] = self["indices"][i][:] - return indexesPerElement + return indicesPerElement def get_information(self) -> str: """ @@ -145,5 +141,5 @@ def get_selector(self) -> Selector: chemical system. """ traj_config = self._configurable[self._dependencies["trajectory"]] - selector = Selector(traj_config["instance"].chemical_system) + selector = Selector(traj_config["instance"]) return selector diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/AtomTransmutationConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/AtomTransmutationConfigurator.py index dad0cebcd6..6ec3f36fee 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/AtomTransmutationConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/AtomTransmutationConfigurator.py @@ -18,7 +18,7 @@ from MDANSE.Framework.Configurators.IConfigurator import IConfigurator from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.MolecularDynamics.Trajectory import Trajectory from MDANSE.Framework.AtomSelector import Selector @@ -27,17 +27,17 @@ class AtomTransmuter: transmutation setting with applications of the apply_transmutation method with a selection setting and symbol.""" - def __init__(self, system: ChemicalSystem) -> None: + def __init__(self, trajectory: Trajectory) -> None: """ Parameters ---------- system : ChemicalSystem The chemical system object. """ - self.selector = Selector(system) + self.selector = Selector(trajectory) self._original_map = {} - for at in system.atom_list: - self._original_map[at.index] = at.symbol + for number, element in enumerate(trajectory.chemical_system.atom_list): + self._original_map[number] = element self._new_map = {} def apply_transmutation( @@ -49,7 +49,7 @@ def apply_transmutation( Parameters ---------- selection_dict: dict[str, Union[bool, dict]] - The selection setting to get the indexes to map the inputted + The selection setting to get the indices to map the inputted symbol. symbol: str The element to map the selected atoms to. @@ -138,7 +138,7 @@ def configure(self, value: str): traj_config = self._configurable[self._dependencies["trajectory"]] system = traj_config["instance"].chemical_system - idxs = [at.index for at in system.atom_list] + idxs = system._atom_indices self._nTransmutedAtoms = 0 for idx, element in value.items(): @@ -153,7 +153,7 @@ def configure(self, value: str): self.error_status = "Inputted setting not valid - atom index not found in the current system." return - if element not in ATOMS_DATABASE: + if element not in traj_config["instance"].atoms_in_database: self.error_status = ( f"the element {element} is not registered in the database" ) @@ -164,7 +164,7 @@ def configure(self, value: str): atomSelConfigurator = self._configurable[self._dependencies["atom_selection"]] atomSelConfigurator["unique_names"] = sorted(set(atomSelConfigurator["names"])) atomSelConfigurator["masses"] = [ - [ATOMS_DATABASE.get_atom_property(n, "atomic_weight")] + [traj_config["instance"].get_atom_property(n, "atomic_weight")] for n in atomSelConfigurator["names"] ] self.error_status = "OK" @@ -182,7 +182,7 @@ def transmute(self, idx: int, element: str) -> None: atomSelConfigurator = self._configurable[self._dependencies["atom_selection"]] try: - idxInSelection = atomSelConfigurator["flatten_indexes"].index(idx) + idxInSelection = atomSelConfigurator["flatten_indices"].index(idx) except ValueError: pass else: @@ -214,5 +214,5 @@ def get_transmuter(self) -> AtomTransmuter: chemical system. """ traj_config = self._configurable[self._dependencies["trajectory"]] - transmuter = AtomTransmuter(traj_config["instance"].chemical_system) + transmuter = AtomTransmuter(traj_config["instance"]) return transmuter diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/AtomsListConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/AtomsListConfigurator.py index eacfe2c306..d465ef6dd5 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/AtomsListConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/AtomsListConfigurator.py @@ -84,7 +84,7 @@ def configure(self, value): + str(natoms) + " were found." ) - atoms = tempdict["indexes"] + atoms = tempdict["indices"] self["value"] = value self["atoms"] = atoms self["n_values"] = len(self["atoms"]) diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py index 49c30772b5..c49ee605a5 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py @@ -60,13 +60,13 @@ def configure(self, value): self.update(value) e1 = find_atoms_in_molecule( - trajConfig["instance"]._chemical_system, + trajConfig["instance"].chemical_system, self["molecule"], self["endpoint1"], True, ) e2 = find_atoms_in_molecule( - trajConfig["instance"]._chemical_system, + trajConfig["instance"].chemical_system, self["molecule"], self["endpoint2"], True, diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/FieldFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/FieldFileConfigurator.py index de7308373e..233660a5dd 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/FieldFileConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/FieldFileConfigurator.py @@ -18,10 +18,6 @@ import numpy as np -from MDANSE.Chemistry.ChemicalEntity import ( - Atom, - AtomCluster, -) from MDANSE.Core.Error import Error from MDANSE.Framework.AtomMapping import get_element_from_mapping, AtomLabel from .FileWithAtomDataConfigurator import FileWithAtomDataConfigurator @@ -138,28 +134,28 @@ def get_atom_charges(self) -> np.ndarray: charge_groups.append(np.array(num_molecules * list(charges))) return np.concatenate(charge_groups) - def build_chemical_system(self, chemicalSystem, aliases): - chemicalEntities = [] + def build_chemical_system(self, chemical_system, aliases): + element_list = [] + name_list = [] for db_name, nMolecules, atomic_contents, masses, _ in self["molecules"]: # Loops over the number of molecules of the current type. - for i in range(nMolecules): + clusters = [] + index = 0 + for _ in range(nMolecules): # This list will contains the instances of the atoms of the molecule. - atoms = [] # Loops over the atom of the molecule. - for j, (name, mass) in enumerate(zip(atomic_contents, masses)): + cluster = [] + for _, (name, mass) in enumerate(zip(atomic_contents, masses)): # The atom is created. element = get_element_from_mapping( aliases, name, molecule=db_name, mass=mass ) - a = Atom(symbol=element, name="%s_%s_%s" % (db_name, name, j)) - atoms.append(a) - - if len(atoms) > 1: - ac = AtomCluster("{:s}".format(db_name), atoms) - chemicalEntities.append(ac) - else: - chemicalEntities.append(atoms[0]) - - for ce in chemicalEntities: - chemicalSystem.add_chemical_entity(ce) + element_list.append(element) + name_list.append(name) + cluster.append(index) + index += 1 + if len(cluster) > 1: + clusters.append(cluster) + chemical_system.initialise_atoms(element_list, name_list) + chemical_system.add_clusters(clusters) diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/FramesConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/FramesConfigurator.py index 3f9122c9a0..21233a23b5 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/FramesConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/FramesConfigurator.py @@ -22,7 +22,7 @@ class FramesConfigurator(RangeConfigurator): The frame selection can be input as: - #. a 3-tuple where the 1st, 2nd will correspond respectively to the indexes of the first and \ + #. a 3-tuple where the 1st, 2nd will correspond respectively to the indices of the first and \ last (excluded) frames to be selected while the 3rd element will correspond to the step number between two frames. For example (1,11,3) will give 1,4,7,10 #. *'all'* keyword, in such case, all the frames of the trajectory are selected #. ``None`` keyword, in such case, all the frames of the trajectory are selected diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/GroupingLevelConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/GroupingLevelConfigurator.py index 7437419a81..a9144d5a0d 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/GroupingLevelConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/GroupingLevelConfigurator.py @@ -20,41 +20,6 @@ from MDANSE.Framework.Configurators.SingleChoiceConfigurator import ( SingleChoiceConfigurator, ) -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms - -LEVELS = collections.OrderedDict() -LEVELS["atom"] = { - "atom": 0, - "atomcluster": 0, - "molecule": 0, - "nucleotidechain": 0, - "peptidechain": 0, - "protein": 0, -} -LEVELS["residue"] = { - "atom": 0, - "atomcluster": 1, - "molecule": 1, - "nucleotidechain": 1, - "peptidechain": 1, - "protein": 1, -} -LEVELS["chain"] = { - "atom": 0, - "atomcluster": 1, - "molecule": 1, - "nucleotidechain": 2, - "peptidechain": 2, - "protein": 2, -} -LEVELS["molecule"] = { - "atom": 0, - "atomcluster": 1, - "molecule": 1, - "nucleotidechain": 2, - "peptidechain": 2, - "protein": 2, -} class GroupingLevelConfigurator(SingleChoiceConfigurator): @@ -84,11 +49,12 @@ def __init__(self, name, choices=None, **kwargs): :param choices: the level of granularities allowed for the input value. If None all levels are allowed. :type choices: one of ['atom','group','residue','chain','molecule'] or None """ + usual_choices = ["atom", "molecule", "group"] if choices is None: - choices = list(LEVELS.keys()) + choices = usual_choices else: - choices = list(set(LEVELS.keys()).intersection(choices)) + choices += [x for x in usual_choices if x not in choices] SingleChoiceConfigurator.__init__(self, name, choices=choices, **kwargs) @@ -108,38 +74,41 @@ def configure(self, value): if value == "atom": return - trajConfig = self._configurable[self._dependencies["trajectory"]] atomSelectionConfig = self._configurable[self._dependencies["atom_selection"]] - - allAtoms = sorted_atoms(trajConfig["instance"].chemical_system.atom_list) - - groups = collections.OrderedDict() - for i in range(atomSelectionConfig["selection_length"]): - idx = atomSelectionConfig["indexes"][i][0] - el = atomSelectionConfig["elements"][i][0] - mass = atomSelectionConfig["masses"][i][0] - at = allAtoms[idx] - lvl = LEVELS[value][at.top_level_chemical_entity.__class__.__name__.lower()] - parent = self.find_parent(at, lvl) - d = groups.setdefault(parent, {}) - d.setdefault("indexes", []).append(idx) - d.setdefault("elements", []).append(el) - d.setdefault("masses", []).append(mass) - - indexes = [] + chemical_system = trajConfig["instance"].chemical_system + indices = [] elements = [] - masses = [] names = [] - group_indices = [] - for i, v in enumerate(groups.values()): - names.append("group_%d" % i) - elements.append(v["elements"]) - indexes.append(v["indexes"]) - masses.append(v["masses"]) - group_indices.append(i) - - atomSelectionConfig["indexes"] = indexes + masses = [] + mass_lookup = chemical_system.atom_property("atomic_weight") + + if value == "molecule": + for mol_name in chemical_system._clusters.keys(): + for mol_number, cluster in enumerate( + chemical_system._clusters[mol_name] + ): + indices.append(cluster) + elements.append([chemical_system.atom_list[x] for x in cluster]) + names.append(f"{mol_name}_mol{mol_number+1}") + masses.append([mass_lookup[x] for x in cluster]) + elif value == "group": + for group_name, group_indices in chemical_system._labels.items(): + residue = set(group_indices) + counter = 1 + for clustername, clusterlist in chemical_system._clusters.items(): + for cluster in clusterlist: + molecule = set(cluster) + if molecule.issubset(residue) or residue.issubset(molecule): + indices.append(list(molecule.intersection(residue))) + elements.append( + [chemical_system.atom_list[x] for x in cluster] + ) + names.append(f"{group_name}_num{counter}_in_{clustername}") + masses.append([mass_lookup[x] for x in cluster]) + counter += 1 + + atomSelectionConfig["indices"] = indices atomSelectionConfig["elements"] = elements atomSelectionConfig["masses"] = masses atomSelectionConfig["names"] = names @@ -147,7 +116,9 @@ def configure(self, value): atomSelectionConfig["unique_names"] = sorted(set(atomSelectionConfig["names"])) self["level"] = value - self["group_indices"] = group_indices + self["group_indices"] = list(range(len(names))) + if atomSelectionConfig["selection_length"] == 0: + self.error_status = "This option resulted in nothing being selected in the current trajectory" @staticmethod def find_parent(atom, level): diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTopologyFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTopologyFileConfigurator.py index af4f3ce03b..a67b240138 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTopologyFileConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTopologyFileConfigurator.py @@ -68,7 +68,7 @@ def configure(self, value: Optional[str]): extension = "".join(Path(value).suffixes)[1:] supported = list(i[1:] for i in _TOPOLOGY_EXTS) if extension not in supported: - self.error_status = f"File '{extension}' not support should be one of the following: {supported}" + self.error_status = f"File '{extension}' not supported. Should be one of the following: {supported}" return super().configure(value) diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTrajectoryFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTrajectoryFileConfigurator.py index 06632d17ff..a44cac00ba 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTrajectoryFileConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/MDTrajTrajectoryFileConfigurator.py @@ -33,5 +33,5 @@ def configure(self, value): supported = list(i[1:] for i in FormatRegistry.loaders.keys()) if self.extension not in supported: - self.error_status = f"File '{self.extension}' not support should be one of the following: {supported}" + self.error_status = f"File '{self.extension}' not supported. Should be one of the following: {supported}" return diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/MultipleChoicesConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/MultipleChoicesConfigurator.py index 39e34fb013..640a9479b3 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/MultipleChoicesConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/MultipleChoicesConfigurator.py @@ -61,20 +61,20 @@ def configure(self, value): self.error_status = f"invalid number of choices." return - indexes = [] + indices = [] for v in value: try: - indexes.append(self._choices.index(v)) + indices.append(self._choices.index(v)) except ValueError: self.error_status = f"{v} item is not a valid choice" return - if not indexes: + if not indices: self.error_status = "Empty choices selection." return - self["indexes"] = indexes - self["choices"] = [self._choices[i] for i in indexes] + self["indices"] = indices + self["choices"] = [self._choices[i] for i in indices] self["value"] = self["choices"] self.error_status = "OK" diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/PartialChargeConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/PartialChargeConfigurator.py index b1b29048e1..5222728afb 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/PartialChargeConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/PartialChargeConfigurator.py @@ -18,7 +18,6 @@ from MDANSE.Framework.Configurators.IConfigurator import IConfigurator from MDANSE.Framework.AtomSelector import Selector -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem from MDANSE.MolecularDynamics.Trajectory import Trajectory @@ -36,13 +35,13 @@ def __init__(self, trajectory: Trajectory) -> None: """ system = trajectory.chemical_system charges = trajectory.charges(0) - self.selector = Selector(system) + self.selector = Selector(trajectory) self._original_map = {} for at_num, at in enumerate(system.atom_list): try: - self._original_map[at.index] = charges[at_num] + self._original_map[at_num] = charges[at_num] except: - self._original_map[at.index] = 0.0 + self._original_map[at_num] = 0.0 self._new_map = {} def update_charges( @@ -54,7 +53,7 @@ def update_charges( Parameters ---------- selection_dict: dict[str, Union[bool, dict]] - The selection setting to get the indexes to map the inputted + The selection setting to get the indices to map the inputted partial charge. charge: float The partial charge to map the selected atoms to. @@ -146,7 +145,7 @@ def configure(self, value): traj_config = self._configurable[self._dependencies["trajectory"]] system = traj_config["instance"].chemical_system - idxs = [at.index for at in system.atom_list] + idxs = system._atom_indices if any([int(i) not in idxs for i in value.keys()]): self.error_status = "Inputted setting not valid - atom index not found in the current system." diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/QVectorsConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/QVectorsConfigurator.py index 5d4c468a69..cd8a7c8ef0 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/QVectorsConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/QVectorsConfigurator.py @@ -63,7 +63,7 @@ def configure(self, value): self.error_status = f"Invalid q vectors settings {value}" return generator = IQVectors.create( - generator, trajConfig["instance"].chemical_system + generator, trajConfig["instance"].configuration(0) ) try: generator.setup(parameters) diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/TrajectoryVariableConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/TrajectoryVariableConfigurator.py index 74b283da44..78f17da577 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/TrajectoryVariableConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/TrajectoryVariableConfigurator.py @@ -40,7 +40,7 @@ def configure(self, value): trajConfig = self._configurable[self._dependencies["trajectory"]] - if not value in trajConfig["instance"].chemical_system.configuration: + if value not in trajConfig["instance"].configuration(): self.error_status = f"{value} is not registered as a trajectory variable." return diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/UnitCellConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/UnitCellConfigurator.py index bddac2a591..99b4ed13c2 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/UnitCellConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/UnitCellConfigurator.py @@ -92,23 +92,25 @@ def configure(self, value): :type value: (np.ndarray, bool) tuple """ self._original_input = value - - self.update_trajectory_information() - - try: - input_array = np.array(value[0], dtype=float) - except: - self.error_status = ( - "Could not convert the inputs into a floating point array" - ) - return - else: - if input_array.shape != (3, 3): - self.error_status = "Input shape must be 3x3" + self["apply"] = value[1] + if self["apply"]: + self.update_trajectory_information() + + try: + input_array = np.array(value[0], dtype=float) + except: + self.error_status = ( + "Could not convert the inputs into a floating point array" + ) return + else: + if input_array.shape != (3, 3): + self.error_status = "Input shape must be 3x3" + return - self["value"] = input_array - self["apply"] = value[1] + self["value"] = input_array + else: + self["value"] = np.eye(3) self.error_status = "OK" def get_information(self): diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/WeightsConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/WeightsConfigurator.py index 370be33cdd..ffa7853fb7 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/WeightsConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/WeightsConfigurator.py @@ -86,6 +86,9 @@ def configure(self, value): :type value: one of the numeric properties of MDANSE.Data.ElementsDatabase.ElementsDatabase """ self._original_input = value + self._trajectory = self._configurable[self._dependencies["trajectory"]][ + "instance" + ] if not isinstance(value, str): self.error_status = "Invalid type for weight. Must be a string." @@ -96,7 +99,7 @@ def configure(self, value): if value in self._aliases.keys(): value = self._aliases[value] - if not value in ATOMS_DATABASE.numeric_properties: + if value not in self._trajectory.properties_in_database: self.error_status = ( f"weight {value} is not registered as a valid numeric property." ) @@ -112,7 +115,7 @@ def get_weights(self): for i in range(ascfg["selection_length"]): name = ascfg["names"][i] for el in ascfg["elements"][i]: - p = ATOMS_DATABASE.get_atom_property(el, self["property"]) + p = self._trajectory.get_atom_property(el, self["property"]) if name in weights: weights[name] += p else: diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/XTDFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/XTDFileConfigurator.py index 7ddc7b18bc..eb46ff7ab9 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/XTDFileConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/XTDFileConfigurator.py @@ -19,14 +19,13 @@ import numpy as np -from MDANSE.Chemistry.ChemicalEntity import Atom, AtomCluster, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Framework.Units import measure from MDANSE.MolecularDynamics.Configuration import ( PeriodicBoxConfiguration, RealConfiguration, ) from MDANSE.MolecularDynamics.UnitCell import UnitCell -from MDANSE.Mathematics.Graph import Graph from MDANSE.Framework.Configurators.FileWithAtomDataConfigurator import ( FileWithAtomDataConfigurator, ) @@ -39,19 +38,21 @@ def __init__(self, name, **kwargs): super().__init__(name, **kwargs) self._atoms = None - self._chemicalSystem = None + self._chemical_system = None self._pbc = False self._cell = None + self._configuration = None + @property def clusters(self): return self._clusters @property def chemicalSystem(self): - return self._chemicalSystem + return self._chemical_system @property def pbc(self): @@ -127,6 +128,7 @@ def parse(self): self._nAtoms = len(self._atoms) bondsMapping = {} + self._bonds = [] comp = 0 for node in ROOT.iter("Bond"): @@ -139,53 +141,43 @@ def parse(self): atomsMapping[int(v)] for v in node.attrib["Connects"].split(",") ] idx1, idx2 = bondsMapping[idx] + self._bonds.append( + (self._atoms[idx1]["index"], self._atoms[idx2]["index"]) + ) self._atoms[idx1]["bonded_to"].add(idx2) self._atoms[idx2]["bonded_to"].add(idx1) def build_chemical_system(self, aliases): - self._chemicalSystem = ChemicalSystem() - - coordinates = np.empty((self._nAtoms, 3), dtype=np.float64) - - graph = Graph() - - for idx, at in list(self._atoms.items()): - graph.add_node(name=idx, **at) - - for idx, at in list(self._atoms.items()): - for bat in at["bonded_to"]: - graph.add_link(idx, bat) - - clusters = graph.build_connected_components() - - for cluster in clusters: - bruteFormula = collections.defaultdict(lambda: 0) - - atoms = [] - for node in cluster: - symbol = node.element - name = node.atom_name - element = get_element_from_mapping(aliases, symbol, type=name) - at = Atom(symbol=element, name=name, xtdIndex=node.xtd_index) - at.index = node.index - coordinates[at.index] = node.xyz - bruteFormula[element] += 1 - atoms.append(at) - name = "".join(["%s%d" % (k, v) for k, v in sorted(bruteFormula.items())]) - ac = AtomCluster(name, atoms) - self._chemicalSystem.add_chemical_entity(ac) + self._chemical_system = ChemicalSystem() + + coordinates = np.array( + [atom["xyz"] for atom in self._atoms.values()], dtype=np.float64 + ) + element_list = [atom["element"] for atom in self._atoms.values()] + name_list = [atom["atom_name"] for atom in self._atoms.values()] + unique_labels = set(name_list) + label_dict = {label: [] for label in unique_labels} + for temp_index, atom in enumerate(self._atoms.values()): + label_dict[atom["atom_name"]].append(temp_index) + + self._chemical_system.initialise_atoms(element_list, name_list) + self._chemical_system.add_bonds(self._bonds) + self._chemical_system.add_labels(label_dict) + self._chemical_system.find_clusters_from_bonds() if self._pbc: boxConf = PeriodicBoxConfiguration( - self._chemicalSystem, coordinates, self._cell + self._chemical_system, coordinates, self._cell ) - realConf = boxConf.to_real_configuration() + real_conf = boxConf.to_real_configuration() else: coordinates *= measure(1.0, "ang").toval("nm") - realConf = RealConfiguration(self._chemicalSystem, coordinates, self._cell) + real_conf = RealConfiguration( + self._chemical_system, coordinates, self._cell + ) - realConf.fold_coordinates() - self._chemicalSystem.configuration = realConf + real_conf.fold_coordinates() + self._configuration = real_conf def atom_labels(self) -> Iterable[AtomLabel]: """ diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/XYZFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/XYZFileConfigurator.py index ec5ff900f3..62bc1a5676 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/XYZFileConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/XYZFileConfigurator.py @@ -101,7 +101,9 @@ def fetch_time_step(self, step: int): try: timeStep = float(matches[0]) except ValueError: - raise XYZFileError("Could not cast the timestep to a floating") + raise XYZFileError( + "Could not cast the timestep to a floating point number." + ) else: return timeStep diff --git a/MDANSE/Src/MDANSE/Framework/Converters/ASE.py b/MDANSE/Src/MDANSE/Framework/Converters/ASE.py index cfbaeb1816..bef661b957 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/ASE.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/ASE.py @@ -23,13 +23,11 @@ import h5py from MDANSE.Framework.AtomMapping import get_element_from_mapping -from MDANSE.Chemistry.ChemicalEntity import Atom, AtomCluster, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Core.Error import Error from MDANSE.Framework.Converters.Converter import Converter from MDANSE.Framework.Units import measure -from MDANSE.Mathematics.Graph import Graph from MDANSE.MolecularDynamics.Configuration import ( - PeriodicBoxConfiguration, PeriodicRealConfiguration, RealConfiguration, ) @@ -93,7 +91,7 @@ class ASE(Converter): { "label": "MDANSE trajectory (filename, format)", "formats": ["MDTFormat"], - "root": "config_file", + "root": "trajectory_file", }, ) @@ -126,7 +124,7 @@ def initialize(self): # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - self._chemicalSystem, + self._chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], @@ -134,10 +132,6 @@ def initialize(self): initial_charges=self._initial_charges, ) - self._nameToIndex = dict( - [(at.name, at.index) for at in self._trajectory.chemical_system.atom_list] - ) - LOG.info(f"total steps: {self.numberOfSteps}") def run_step(self, index): @@ -185,14 +179,14 @@ def run_step(self, index): if self._initial_masses is not None: velocities = momenta / self._initial_masses.reshape((len(momenta), 1)) else: - velocities = momenta / np.array(self._chemicalSystem.masses).reshape( - (len(momenta), 1) - ) + velocities = momenta / np.array( + self._chemical_system.atom_property("atomic_weight") + ).reshape((len(momenta), 1)) variables["velocities"] = velocities * measure(1.0, "ang/fs").toval("nm/ps") if self._isPeriodic: try: - realConf = PeriodicRealConfiguration( + real_conf = PeriodicRealConfiguration( self._trajectory.chemical_system, coords, unitCell, **variables ) except ValueError: @@ -202,10 +196,10 @@ def run_step(self, index): ) return index, None if self._configuration["fold"]["value"]: - realConf.fold_coordinates() + real_conf.fold_coordinates() else: try: - realConf = RealConfiguration( + real_conf = RealConfiguration( self._trajectory.chemical_system, coords, **variables ) except ValueError: @@ -215,11 +209,11 @@ def run_step(self, index): ) return index, None - self._trajectory.chemical_system.configuration = realConf - # A snapshot is created out of the current configuration. self._trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + real_conf, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) try: @@ -255,6 +249,7 @@ def finalize(self): self._input.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(ASE, self).finalize() @@ -304,42 +299,9 @@ def parse_first_step(self, mapping): LOG.warning("ASE converter could not read partial charges from file.") self._initial_charges = None - g = Graph() - - element_count = {} element_list = first_frame.get_chemical_symbols() self._nAtoms = len(element_list) - self._chemicalSystem = ChemicalSystem() - - for atnum, element in enumerate(element_list): - if element in element_count.keys(): - element_count[element] += 1 - else: - element_count[element] = 1 - g.add_node(atnum, element=element, atomName=f"{element}_{atnum+1}") - - for cluster in g.build_connected_components(): - if len(cluster) == 1: - node = cluster.pop() - try: - element = get_element_from_mapping(mapping, node.element) - obj = Atom(element, name=node.atomName) - except TypeError: - LOG.error("EXCEPTION in ASE loader") - LOG.error(f"node.element = {node.element}") - LOG.error(f"node.atomName = {node.atomName}") - LOG.error(f"rankToName = {self._rankToName}") - obj.index = node.name - else: - atList = [] - for atom in cluster: - element = get_element_from_mapping(mapping, atom.element) - at = Atom(symbol=element, name=atom.atomName) - atList.append(at) - c = collections.Counter([at.element for at in cluster]) - name = "".join(["{:s}{:d}".format(k, v) for k, v in sorted(c.items())]) - obj = AtomCluster(name, atList) - - self._chemicalSystem.add_chemical_entity(obj) + self._chemical_system = ChemicalSystem() + self._chemical_system.initialise_atoms(element_list) diff --git a/MDANSE/Src/MDANSE/Framework/Converters/CASTEP.py b/MDANSE/Src/MDANSE/Framework/Converters/CASTEP.py index 4cdc4893a2..b7a7290b59 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/CASTEP.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/CASTEP.py @@ -15,7 +15,7 @@ # import collections -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Core.Error import Error from MDANSE.Framework.Converters.Converter import Converter from MDANSE.Framework.Units import measure @@ -87,13 +87,14 @@ def initialize(self): # Create a bound universe self._chemical_system = ChemicalSystem() + element_list = [] # Populate the universe with atoms based on how many of each atom is in the read trajectory for symbol, number in self._castepFile["atoms"]: - for i in range(number): + for _ in range(number): element = get_element_from_mapping(self._atomicAliases, symbol) - self._chemical_system.add_chemical_entity( - Atom(symbol=element, name="%s_%d" % (symbol, i)) - ) + element_list.append(element) + + self._chemical_system.initialise_atoms(element_list) # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( @@ -135,9 +136,8 @@ def run_step(self, index): if self.configuration["fold"]["value"]: conf.fold_coordinates() - self._trajectory.chemical_system.configuration = conf - self._trajectory.dump_configuration( + conf, time_step, units={ "time": "ps", @@ -169,6 +169,7 @@ def finalize(self): self._castepFile.close() # Close the .md file. # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(CASTEP, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/CP2K.py b/MDANSE/Src/MDANSE/Framework/Converters/CP2K.py index 4cb91af0b5..b334f7407f 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/CP2K.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/CP2K.py @@ -17,7 +17,7 @@ import numpy as np from MDANSE.Framework.Converters.Converter import Converter -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Core.Error import Error from MDANSE.Framework.Units import measure from MDANSE.MolecularDynamics.Configuration import PeriodicRealConfiguration @@ -54,17 +54,21 @@ def __init__(self, filename): words = line.strip().split() if len(words) != 12: - raise CellFileError("Invalid format for cell line") + raise CellFileError(f"Invalid format for cell line: {line}") try: time_steps.append(float(words[1])) except ValueError: - raise CellFileError("Can not cast time step to floating") + raise CellFileError( + f"Cannot cast time step {words[1]} to a floating point number" + ) try: cell = np.array(words[2:11], dtype=np.float64).reshape((3, 3)) except ValueError: - raise CellFileError("Can not cast cell coordinates to floating") + raise CellFileError( + f"Cannot cast cell coordinates {words[2:11]} to floating point numbers" + ) self["cells"].append(cell) @@ -150,7 +154,7 @@ class CP2K(Converter): "OutputTrajectoryConfigurator", { "formats": ["MDTFormat"], - "root": "xdatcar_file", + "root": "pos_file", "label": "MDANSE trajectory (filename, format)", }, ) @@ -194,12 +198,13 @@ def initialize(self): self.numberOfSteps = self._xyzFile["n_frames"] self._chemical_system = ChemicalSystem() + element_list = [] - for i, symbol in enumerate(self._xyzFile["atoms"]): + for _, symbol in enumerate(self._xyzFile["atoms"]): element = get_element_from_mapping(self._atomicAliases, symbol) - self._chemical_system.add_chemical_entity( - Atom(symbol=element, name="%s_%d" % (symbol, i + 1)) - ) + element_list.append(element) + + self._chemical_system.initialise_atoms(element_list) self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], @@ -236,19 +241,18 @@ def run_step(self, index): 1.0, iunit="ang/fs" ).toval("nm/ps") - realConf = PeriodicRealConfiguration( + real_conf = PeriodicRealConfiguration( self._trajectory.chemical_system, coords, unitcell, **variables ) if self._configuration["fold"]["value"]: - realConf.fold_coordinates() - - self._trajectory.chemical_system.configuration = realConf + real_conf.fold_coordinates() time = index * self._xyzFile["time_step"] * measure(1.0, iunit="fs").toval("ps") # A snapshot is created out of the current configuration. self._trajectory.dump_configuration( + real_conf, time, units={ "time": "ps", @@ -286,6 +290,7 @@ def finalize(self): self._cellFile.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(CP2K, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/DCD.py b/MDANSE/Src/MDANSE/Framework/Converters/DCD.py index 0d122ce2b7..ce42b8d620 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/DCD.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/DCD.py @@ -25,7 +25,6 @@ from MDANSE.Mathematics.Geometry import get_basis_vectors_from_cell_parameters from MDANSE.MolecularDynamics.Configuration import PeriodicRealConfiguration from MDANSE.MolecularDynamics.Trajectory import ( - resolve_undefined_molecules_name, TrajectoryWriter, ) from MDANSE.MolecularDynamics.UnitCell import UnitCell @@ -328,8 +327,6 @@ def initialize(self): pdb_reader = MinimalPDBReader(self.configuration["pdb_file"]["filename"]) self._chemical_system = pdb_reader._chemical_system - resolve_undefined_molecules_name(self._chemical_system) - # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], @@ -362,14 +359,12 @@ def run_step(self, index): if self.configuration["fold"]["value"]: conf.fold_coordinates() - self._trajectory._chemical_system.configuration = conf - # The current time. time = (index + 1) * self.configuration["time_step"]["value"] # Store a snapshot of the current configuration in the output trajectory. self._trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + conf, time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} ) return index, None @@ -390,6 +385,7 @@ def finalize(self): """ # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(DCD, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/DL_POLY.py b/MDANSE/Src/MDANSE/Framework/Converters/DL_POLY.py index ec4e3ea23d..ed643e4162 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/DL_POLY.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/DL_POLY.py @@ -16,7 +16,7 @@ import collections import numpy as np -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Core.Error import Error from MDANSE.Framework.Converters.Converter import Converter from MDANSE.Framework.Units import measure @@ -180,13 +180,15 @@ def initialize(self): # The number of steps of the analysis. self.numberOfSteps = int(self._historyFile["n_frames"]) - self._chemicalSystem = ChemicalSystem() + self._chemical_system = ChemicalSystem() - self._fieldFile.build_chemical_system(self._chemicalSystem, self._atomicAliases) + self._fieldFile.build_chemical_system( + self._chemical_system, self._atomicAliases + ) self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - self._chemicalSystem, + self._chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], @@ -232,9 +234,8 @@ def run_step(self, index): if self._gradients is not None: conf["gradients"] = config[2] - self._trajectory.chemical_system.configuration = conf - self._trajectory.dump_configuration( + conf, time, units={ "time": "ps", @@ -268,6 +269,7 @@ def finalize(self): self._historyFile.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(DL_POLY, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/Discover.py b/MDANSE/Src/MDANSE/Framework/Converters/Discover.py index 2db4d4f8b0..6718523dff 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/Discover.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/Discover.py @@ -299,7 +299,9 @@ def initialize(self): self._xtdfile.build_chemical_system(self._atomicAliases) - self._chemicalSystem = self._xtdfile.chemicalSystem + self._atom_config = self._xtdfile._configuration + + self._chemical_system = self._xtdfile.chemicalSystem self._hisfile = HisFile(self.configuration["his_file"]["filename"]) @@ -310,32 +312,32 @@ def initialize(self): variables["velocities"] = self._hisfile["initial_velocities"] if np.all(self._hisfile["initial_cell"] < 0.11): - self._chemicalSystem.configuration.is_periodic = False - realConf = RealConfiguration( - self._chemicalSystem, self._hisfile["initial_coordinates"], **variables + self._atom_config.is_periodic = False + real_conf = RealConfiguration( + self._chemical_system, self._hisfile["initial_coordinates"], **variables ) - elif self._chemicalSystem.configuration.is_periodic: + elif self._atom_config.is_periodic: unitCell = UnitCell(self._hisfile["initial_cell"]) - realConf = PeriodicRealConfiguration( - self._chemicalSystem, + real_conf = PeriodicRealConfiguration( + self._chemical_system, self._hisfile["initial_coordinates"], unitCell, **variables, ) else: - realConf = RealConfiguration( - self._chemicalSystem, self._hisfile["initial_coordinates"], **variables + real_conf = RealConfiguration( + self._chemical_system, self._hisfile["initial_coordinates"], **variables ) if self.configuration["fold"]["value"]: - realConf.fold_coordinates() + real_conf.fold_coordinates() - self._chemicalSystem.configuration = realConf + self._atom_config = real_conf # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - self._chemicalSystem, + self._chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], @@ -354,7 +356,7 @@ def run_step(self, index): time, cell, config, vel = self._hisfile.read_step(index) - conf = self._trajectory.chemical_system.configuration + conf = self._atom_config if conf.is_periodic: conf.unit_cell = UnitCell(cell) @@ -365,9 +367,8 @@ def run_step(self, index): conf.fold_coordinates() - self._trajectory.chemical_system.configuration = conf - self._trajectory.dump_configuration( + conf, time, units={ "time": "ps", @@ -398,6 +399,7 @@ def finalize(self): self._hisfile.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(Discover, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/Forcite.py b/MDANSE/Src/MDANSE/Framework/Converters/Forcite.py index 54fb400896..49296abf17 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/Forcite.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/Forcite.py @@ -325,7 +325,7 @@ def initialize(self): self._xtdfile.build_chemical_system(self._atomicAliases) - self._chemicalSystem = self._xtdfile.chemicalSystem + self._chemical_system = self._xtdfile.chemicalSystem self._trjfile = TrjFile(self.configuration["trj_file"]["filename"]) @@ -334,14 +334,14 @@ def initialize(self): if self._trjfile["velocities_written"]: self._velocities = np.zeros( - (self._chemicalSystem.number_of_atoms, 3), dtype=np.float64 + (self._chemical_system.number_of_atoms, 3), dtype=np.float64 ) else: self._velocities = None if self._trjfile["gradients_written"]: self._gradients = np.zeros( - (self._chemicalSystem.number_of_atoms, 3), dtype=np.float64 + (self._chemical_system.number_of_atoms, 3), dtype=np.float64 ) else: self._gradients = None @@ -349,7 +349,7 @@ def initialize(self): # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - self._chemicalSystem, + self._chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], @@ -370,7 +370,7 @@ def run_step(self, index): time, cell, xyz, velocities, gradients = self._trjfile.read_step(index) # If the universe is periodic set its shape with the current dimensions of the unit cell. - conf = self._trajectory.chemical_system.configuration + conf = self._xtdfile._configuration movableAtoms = self._trjfile["mvofst"] conf["coordinates"][movableAtoms, :] = xyz if conf.is_periodic: @@ -388,6 +388,7 @@ def run_step(self, index): conf["gradients"] = self._gradients self._trajectory.dump_configuration( + conf, time, units={ "time": "ps", @@ -419,6 +420,7 @@ def finalize(self): self._trjfile.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(Forcite, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py b/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py index 1d4f86e13b..800b8d7dec 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py @@ -17,9 +17,10 @@ import collections import numpy as np +from mdtraj.formats.xtc import XTCTrajectoryFile +from mdtraj.formats.trr import TRRTrajectoryFile from MDANSE.Core.Error import Error -from MDANSE.Extensions import xtc, trr from MDANSE.Framework.Converters.Converter import Converter from MDANSE.IO.MinimalPDBReader import MinimalPDBReader from MDANSE.MolecularDynamics.Configuration import PeriodicRealConfiguration @@ -78,12 +79,12 @@ def initialize(self): # Create XTC or TRR object depending on which kind of trajectory was loaded if self.configuration["xtc_file"]["filename"][-4:] == ".xtc": - self._xdr_file = xtc.XTCTrajectoryFile( + self._xdr_file = XTCTrajectoryFile( self.configuration["xtc_file"]["filename"], "r" ) self._xtc = True elif self.configuration["xtc_file"]["filename"][-4:] == ".trr": - self._xdr_file = trr.TRRTrajectoryFile( + self._xdr_file = TRRTrajectoryFile( self.configuration["xtc_file"]["filename"], "r" ) self._xtc = False @@ -173,12 +174,10 @@ def run_step(self, index): if self.configuration["fold"]["value"]: conf.fold_coordinates() - self._trajectory.chemical_system.configuration = conf - # The current time. time = times[0] - self._trajectory.dump_configuration(time) + self._trajectory.dump_configuration(conf, time) return index, None @@ -201,6 +200,7 @@ def finalize(self): self._xdr_file.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(Gromacs, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/ImprovedASE.py b/MDANSE/Src/MDANSE/Framework/Converters/ImprovedASE.py index 270a7f1f38..1b87541144 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/ImprovedASE.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/ImprovedASE.py @@ -19,11 +19,10 @@ from ase.io.trajectory import Trajectory as ASETrajectory import numpy as np -from MDANSE.Chemistry.ChemicalEntity import Atom, AtomCluster, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Core.Error import Error from MDANSE.Framework.Converters.Converter import Converter from MDANSE.Framework.Units import measure -from MDANSE.Mathematics.Graph import Graph from MDANSE.MolecularDynamics.Configuration import ( PeriodicBoxConfiguration, PeriodicRealConfiguration, @@ -101,7 +100,7 @@ class ImprovedASE(Converter): { "label": "MDANSE trajectory (filename, format)", "formats": ["MDTFormat"], - "root": "config_file", + "root": "trajectory_file", }, ) @@ -111,7 +110,7 @@ def initialize(self): """ super().initialize() - self._chemicalSystem = None + self._chemical_system = None self._fractionalCoordinates = None self._nAtoms = None self._masses = None @@ -132,17 +131,13 @@ def initialize(self): # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - self._chemicalSystem, + self._chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], compression=self.configuration["output_files"]["compression"], ) - self._nameToIndex = dict( - [(at.name, at.index) for at in self._trajectory.chemical_system.atom_list] - ) - LOG.info(f"total steps: {self.numberOfSteps}") def run_step(self, index): @@ -176,17 +171,16 @@ def run_step(self, index): conf = PeriodicBoxConfiguration( self._trajectory.chemical_system, coords, unitCell ) - realConf = conf.to_real_configuration() + real_conf = conf.to_real_configuration() else: - realConf = PeriodicRealConfiguration( + real_conf = PeriodicRealConfiguration( self._trajectory.chemical_system, coords, unitCell ) - - self._trajectory.chemical_system.configuration = realConf - # A snapshot is created out of the current configuration. self._trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + real_conf, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) return index, None @@ -213,6 +207,7 @@ def finalize(self): except: pass # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(ImprovedASE, self).finalize() @@ -232,9 +227,6 @@ def extract_initial_information(self, ase_object): except: pass - g = Graph() - - element_count = {} if self.configuration["elements_from_mass"]["value"]: tolerance = self.configuration["mass_tolerance"]["value"] if self._masses is None: @@ -251,39 +243,10 @@ def extract_initial_information(self, ase_object): else: if self._nAtoms is None: self._nAtoms = len(element_list) - if self._chemicalSystem is None: - self._chemicalSystem = ChemicalSystem() - - for atnum, element in enumerate(element_list): - if element in element_count.keys(): - element_count[element] += 1 - else: - element_count[element] = 1 - g.add_node(atnum, element=element, atomName=f"{element}_{atnum+1}") - - for cluster in g.build_connected_components(): - if len(cluster) == 1: - node = cluster.pop() - try: - obj = Atom(node.element, name=node.atomName) - except TypeError: - LOG.error("EXCEPTION in ASE loader") - LOG.error(f"node.element = {node.element}") - LOG.error(f"node.atomName = {node.atomName}") - LOG.error(f"rankToName = {self._rankToName}") - obj.index = node.name - else: - atList = [] - for atom in cluster: - at = Atom(symbol=atom.element, name=atom.atomName) - atList.append(at) - c = collections.Counter([at.element for at in cluster]) - name = "".join( - ["{:s}{:d}".format(k, v) for k, v in sorted(c.items())] - ) - obj = AtomCluster(name, atList) - - self._chemicalSystem.add_chemical_entity(obj) + if self._chemical_system is None: + self._chemical_system = ChemicalSystem() + + self._chemical_system.initialise_atoms(element_list) def parse_optional_config(self): try: diff --git a/MDANSE/Src/MDANSE/Framework/Converters/LAMMPS.py b/MDANSE/Src/MDANSE/Framework/Converters/LAMMPS.py index dee7f8d3a6..e06e156f27 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/LAMMPS.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/LAMMPS.py @@ -18,11 +18,10 @@ import h5py -from MDANSE.Chemistry.ChemicalEntity import Atom, AtomCluster, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Core.Error import Error from MDANSE.Framework.Converters.Converter import Converter from MDANSE.Framework.Units import measure -from MDANSE.Mathematics.Graph import Graph from MDANSE.MolecularDynamics.Configuration import ( PeriodicBoxConfiguration, PeriodicRealConfiguration, @@ -141,6 +140,8 @@ def parse_first_step(self, aliases, config): comp = -1 + chemical_system = ChemicalSystem() + while True: line = self._file.readline() comp += 1 @@ -199,8 +200,10 @@ def parse_first_step(self, aliases, config): ) self._rankToName = {} + element_list = [] + name_list = [] + index_list = [] - g = Graph() self._itemsPosition["ATOMS"] = [comp + 1, comp + self._nAtoms + 1] for i in range(self._nAtoms): temp = self._file.readline().split() @@ -226,43 +229,22 @@ def parse_first_step(self, aliases, config): self._rankToName[i] = name else: self._rankToName[temp_index - 1] = name - g.add_node(idx, label=label, mass=mass, atomName=name) + element = get_element_from_mapping(aliases, label, mass=mass) + element_list.append(element) + name_list.append(str(ty + 1)) + index_list.append(idx) + + sorting = np.argsort(index_list) + chemical_system.initialise_atoms( + np.array(element_list)[sorting], np.array(name_list)[sorting] + ) if config["n_bonds"] is not None: + bonds = [] for idx1, idx2 in config["bonds"]: - g.add_link(idx1, idx2) - - chemicalSystem = ChemicalSystem() + bonds.append((idx1, idx2)) + chemical_system.add_bonds(bonds) - for cluster in g.build_connected_components(): - if len(cluster) == 1: - node = cluster.pop() - try: - element = get_element_from_mapping( - aliases, node.label, mass=node.mass - ) - obj = Atom(symbol=element, name=node.atomName) - except TypeError: - LOG.error("EXCEPTION in LAMMPS loader") - LOG.error(f"node.element = {node.element}") - LOG.error(f"node.atomName = {node.atomName}") - LOG.error(f"rankToName = {self._rankToName}") - obj.index = node.name - else: - atList = [] - for atom in cluster: - element = get_element_from_mapping( - aliases, atom.label, mass=atom.mass - ) - at = Atom(symbol=element, name=atom.atomName) - atList.append(at) - c = collections.Counter([at.label for at in cluster]) - name = "".join( - ["{:s}{:d}".format(k, v) for k, v in sorted(c.items())] - ) - obj = AtomCluster(name, atList) - - chemicalSystem.add_chemical_entity(obj) self._last = comp + self._nAtoms + 1 break @@ -271,7 +253,7 @@ def parse_first_step(self, aliases, config): self._nAtoms = int(self._file.readline()) comp += 1 continue - return chemicalSystem + return chemical_system def run_step(self, index): """Runs a single step of the job. @@ -362,11 +344,11 @@ def run_step(self, index): ): temp = self._file.readline().split() try: - temp_index = int(temp[0]) + temp_index = int(temp[self._id]) - 1 except ValueError: idx = i else: - idx = self._nameToIndex[self._rankToName[temp_index - 1]] + idx = temp_index coords[idx, :] = np.array( [temp[self._x], temp[self._y], temp[self._z]], dtype=np.float64 ) @@ -377,22 +359,22 @@ def run_step(self, index): conf = PeriodicBoxConfiguration( self._trajectory.chemical_system, coords, unitCell ) - realConf = conf.to_real_configuration() + real_conf = conf.to_real_configuration() else: coords *= measure(1.0, self._length_unit).toval("nm") - realConf = PeriodicRealConfiguration( + real_conf = PeriodicRealConfiguration( self._trajectory.chemical_system, coords, unitCell ) if self._fold: # The whole configuration is folded in to the simulation box. - realConf.fold_coordinates() - - self._trajectory.chemical_system.configuration = realConf + real_conf.fold_coordinates() # A snapshot is created out of the current configuration. self._trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + real_conf, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) if self._charge is not None: self._trajectory.write_charges( @@ -479,8 +461,10 @@ def parse_first_step(self, aliases, config): self._full_cell = full_cell self._rankToName = {} + element_list = [] + name_list = [] + chemical_system = ChemicalSystem() - g = Graph() for i in range(self._nAtoms): idx = i ty = atom_types[i] - 1 @@ -488,43 +472,17 @@ def parse_first_step(self, aliases, config): mass = str(config["elements"][ty][1]) name = "{:s}_{:d}".format(str(config["elements"][ty][0]), idx) self._rankToName[idx] = name - g.add_node(idx, label=label, mass=mass, atomName=name) + element_list.append(get_element_from_mapping(aliases, label, mass=mass)) + name_list.append(str(ty + 1)) + chemical_system.initialise_atoms(element_list, name_list) if config["n_bonds"] is not None: + bonds = [] for idx1, idx2 in config["bonds"]: - g.add_link(idx1, idx2) - - chemicalSystem = ChemicalSystem() - - for cluster in g.build_connected_components(): - if len(cluster) == 1: - node = cluster.pop() - try: - element = get_element_from_mapping( - aliases, node.label, mass=node.mass - ) - obj = Atom(symbol=element, name=node.atomName) - except TypeError: - LOG.error("EXCEPTION in LAMMPS loader") - LOG.error(f"node.element = {node.element}") - LOG.error(f"node.atomName = {node.atomName}") - LOG.error(f"rankToName = {self._rankToName}") - obj.index = node.name - else: - atList = [] - for atom in cluster: - element = get_element_from_mapping( - aliases, atom.label, mass=atom.mass - ) - at = Atom(symbol=element, name=atom.atomName) - atList.append(at) - c = collections.Counter([at.label for at in cluster]) - name = "".join(["{:s}{:d}".format(k, v) for k, v in sorted(c.items())]) - obj = AtomCluster(name, atList) - - chemicalSystem.add_chemical_entity(obj) + bonds.append((idx1, idx2)) + chemical_system.add_bonds(bonds) - return chemicalSystem + return chemical_system def run_step(self, index): """Runs a single step of the job. @@ -547,22 +505,22 @@ def run_step(self, index): conf = PeriodicBoxConfiguration( self._trajectory.chemical_system, positions, unitCell ) - realConf = conf.to_real_configuration() + real_conf = conf.to_real_configuration() else: positions *= measure(1.0, self._length_unit).toval("nm") - realConf = PeriodicRealConfiguration( + real_conf = PeriodicRealConfiguration( self._trajectory.chemical_system, positions, unitCell ) if self._fold: # The whole configuration is folded in to the simulation box. - realConf.fold_coordinates() - - self._trajectory.chemical_system.configuration = realConf + real_conf.fold_coordinates() # A snapshot is created out of the current configuration. self._trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + real_conf, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) return index, 0 @@ -612,8 +570,10 @@ def parse_first_step(self, aliases, config): self._full_cell = full_cell self._rankToName = {} + chemical_system = ChemicalSystem() + element_list = [] + name_list = [] - g = Graph() for i in range(self._nAtoms): idx = i ty = atom_types[i] - 1 @@ -621,43 +581,17 @@ def parse_first_step(self, aliases, config): mass = str(config["elements"][ty][1]) name = "{:s}_{:d}".format(str(config["elements"][ty][0]), idx) self._rankToName[idx] = name - g.add_node(idx, label=label, mass=mass, atomName=name) + element_list.append(get_element_from_mapping(aliases, label, mass=mass)) + name_list.append(str(ty + 1)) + chemical_system.initialise_atoms(element_list, name_list) if config["n_bonds"] is not None: + bonds = [] for idx1, idx2 in config["bonds"]: - g.add_link(idx1, idx2) + bonds.append((idx1, idx2)) + chemical_system.add_bonds(bonds) - chemicalSystem = ChemicalSystem() - - for cluster in g.build_connected_components(): - if len(cluster) == 1: - node = cluster.pop() - try: - element = get_element_from_mapping( - aliases, node.label, mass=node.mass - ) - obj = Atom(symbol=element, name=node.atomName) - except TypeError: - LOG.error("EXCEPTION in LAMMPS loader") - LOG.error(f"node.element = {node.element}") - LOG.error(f"node.atomName = {node.atomName}") - LOG.error(f"rankToName = {self._rankToName}") - obj.index = node.name - else: - atList = [] - for atom in cluster: - element = get_element_from_mapping( - aliases, atom.label, mass=atom.mass - ) - at = Atom(symbol=element, name=atom.atomName) - atList.append(at) - c = collections.Counter([at.label for at in cluster]) - name = "".join(["{:s}{:d}".format(k, v) for k, v in sorted(c.items())]) - obj = AtomCluster(name, atList) - - chemicalSystem.add_chemical_entity(obj) - - return chemicalSystem + return chemical_system def run_step(self, index): """Runs a single step of the job. @@ -678,22 +612,22 @@ def run_step(self, index): conf = PeriodicBoxConfiguration( self._trajectory.chemical_system, positions, unitCell ) - realConf = conf.to_real_configuration() + real_conf = conf.to_real_configuration() else: positions *= measure(1.0, self._length_unit).toval("nm") - realConf = PeriodicRealConfiguration( + real_conf = PeriodicRealConfiguration( self._trajectory.chemical_system, positions, unitCell ) if self._fold: # The whole configuration is folded in to the simulation box. - realConf.fold_coordinates() - - self._trajectory.chemical_system.configuration = realConf + real_conf.fold_coordinates() # A snapshot is created out of the current configuration. self._trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + real_conf, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) if self._charges_fixed is None: try: @@ -804,7 +738,8 @@ def initialize(self): self._reader = self.create_reader(self._lammps_format) self._reader.set_units(self._lammps_units) - self._chemicalSystem = self.parse_first_step(self._atomicAliases) + self._chemical_system = self.parse_first_step(self._atomicAliases) + self._chemical_system.find_clusters_from_bonds() if self.numberOfSteps == 0: self.numberOfSteps = self._reader.get_time_steps( @@ -815,9 +750,9 @@ def initialize(self): np.array(self._lammpsConfig["charges"]) * self._reader._charge_conversion_factor ) - if len(charges_single_cell) < self._chemicalSystem.number_of_atoms: + if len(charges_single_cell) < self._chemical_system.number_of_atoms: charges = list(charges_single_cell) * int( - self._chemicalSystem.number_of_atoms // len(charges_single_cell) + self._chemical_system.number_of_atoms // len(charges_single_cell) ) else: charges = list(charges_single_cell) @@ -825,7 +760,7 @@ def initialize(self): # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - self._chemicalSystem, + self._chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], @@ -833,10 +768,6 @@ def initialize(self): initial_charges=charges, ) - self._reader._nameToIndex = dict( - [(at.name, at.index) for at in self._trajectory.chemical_system.atom_list] - ) - self._start = 0 self._reader.open_file(self.configuration["trajectory_file"]["value"]) self._reader.set_output(self._trajectory) @@ -897,12 +828,13 @@ def finalize(self): self._reader.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(LAMMPS, self).finalize() def parse_first_step(self, aliases): self._reader.open_file(self.configuration["trajectory_file"]["value"]) - chemicalSystem = self._reader.parse_first_step(aliases, self._lammpsConfig) + chemical_system = self._reader.parse_first_step(aliases, self._lammpsConfig) self._reader.close() - return chemicalSystem + return chemical_system diff --git a/MDANSE/Src/MDANSE/Framework/Converters/MDAnalysis.py b/MDANSE/Src/MDANSE/Framework/Converters/MDAnalysis.py index c9368e6248..6a567fc46e 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/MDAnalysis.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/MDAnalysis.py @@ -20,7 +20,7 @@ from MDANSE.Framework.Units import measure from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter from MDANSE.Framework.Converters.Converter import Converter -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem, Atom +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Framework.AtomMapping import get_element_from_mapping from MDANSE.MolecularDynamics.Configuration import ( PeriodicRealConfiguration, @@ -122,14 +122,31 @@ def initialize(self): self.numberOfSteps = len(self.u.trajectory) self._chemical_system = ChemicalSystem() + element_list = [] + name_list = [] + label_dict = {} - for at in self.u.atoms: + for at_number, at in enumerate(self.u.atoms): kwargs = {} for arg in ["element", "name", "type", "resname", "mass"]: if hasattr(at, arg): kwargs[arg] = getattr(at, arg) # the first out of the list above will be the main label (k, main_label) = next(iter(kwargs.items())) + # label_list will be populated too + if "resname" in kwargs: + tag = kwargs["resname"] + elif "type" in kwargs: + tag = kwargs["type"] + elif "name" in kwargs: + tag = kwargs["name"] + else: + tag = None + if tag: + if tag in label_dict.keys(): + label_dict[tag] += [at_number] + else: + label_dict[tag] = [at_number] kwargs.pop(k) element = get_element_from_mapping( self.configuration["atom_aliases"]["value"], main_label, **kwargs @@ -140,7 +157,12 @@ def initialize(self): if hasattr(at, arg): name = getattr(at, arg) break - self._chemical_system.add_chemical_entity(Atom(symbol=element, name=name)) + element_list.append(element) + name_list.append(name) + if None in name_list: + name_list = None + self._chemical_system.initialise_atoms(element_list, name_list) + self._chemical_system.add_labels(label_dict) kwargs = { "positions_dtype": self.configuration["output_files"]["dtype"], @@ -201,14 +223,13 @@ def run_step(self, index: int): if hasattr(self.u.trajectory.ts, "forces"): conf["gradients"] = self.u.trajectory.ts.forces - self._trajectory._chemical_system.configuration = conf - if float(self.configuration["time_step"]["value"]) == 0.0: time = index * self.u.trajectory.ts.dt else: time = index * float(self.configuration["time_step"]["value"]) self._trajectory.dump_configuration( + conf, time, units={ "time": "ps", @@ -225,5 +246,6 @@ def combine(self, index, x): pass def finalize(self): + self._trajectory.write_standard_atom_database() self._trajectory.close() super(MDAnalysis, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Converters/MDTraj.py b/MDANSE/Src/MDANSE/Framework/Converters/MDTraj.py index cdd1b9c574..4d838454a4 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/MDTraj.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/MDTraj.py @@ -19,7 +19,7 @@ from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter from MDANSE.Framework.Converters.Converter import Converter -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem, Atom +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Framework.AtomMapping import get_element_from_mapping from MDANSE.MolecularDynamics.Configuration import ( PeriodicRealConfiguration, @@ -116,9 +116,11 @@ def initialize(self): ) self.numberOfSteps = self.traj.n_frames + mdtraj_to_mdanse = {} self._chemical_system = ChemicalSystem() - for at in self.traj.topology.atoms: + elements, atom_names, atom_labels = [], [], {} + for atnumber, at in enumerate(self.traj.topology.atoms): element = get_element_from_mapping( self.configuration["atom_aliases"]["value"], at.name, @@ -127,9 +129,23 @@ def initialize(self): number=at.element.number, mass=at.element.mass, ) - self._chemical_system.add_chemical_entity( - Atom(symbol=element, name=at.name) - ) + elements.append(element) + atom_names.append(at.name) + mdtraj_to_mdanse[at.index] = atnumber + if at.residue.name: + try: + atom_labels[at.residue.name] + except KeyError: + atom_labels[at.residue.name] = [atnumber] + else: + atom_labels[at.residue.name].append(atnumber) + self._chemical_system.initialise_atoms(elements, atom_names) + self._chemical_system.add_labels(atom_labels) + bonds = [] + for at1, at2 in self.traj.topology.bonds: + bonds.append([mdtraj_to_mdanse[at1.index], mdtraj_to_mdanse[at2.index]]) + self._chemical_system.add_bonds(bonds) + self._chemical_system.find_clusters_from_bonds() kwargs = { "positions_dtype": self.configuration["output_files"]["dtype"], @@ -174,8 +190,6 @@ def run_step(self, index: int): if self.configuration["fold"]["value"]: conf.fold_coordinates() - self._trajectory._chemical_system.configuration = conf - # TODO as of 11/12/2024 MDTraj does not read velocity data # there is a discussion about this on GitHub # (https://github.com/mdtraj/mdtraj/issues/1824). @@ -192,6 +206,7 @@ def run_step(self, index: int): time = index * float(self.configuration["time_step"]["value"]) self._trajectory.dump_configuration( + conf, time, units={ "time": "ps", diff --git a/MDANSE/Src/MDANSE/Framework/Converters/VASP.py b/MDANSE/Src/MDANSE/Framework/Converters/VASP.py index 05e99ad4d7..8ae0ec257d 100644 --- a/MDANSE/Src/MDANSE/Framework/Converters/VASP.py +++ b/MDANSE/Src/MDANSE/Framework/Converters/VASP.py @@ -16,7 +16,7 @@ import collections from MDANSE.Core.Error import Error -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Framework.Converters.Converter import Converter from MDANSE.Framework.Units import measure from MDANSE.MolecularDynamics.Configuration import PeriodicBoxConfiguration @@ -98,21 +98,21 @@ def initialize(self): # The number of steps of the analysis. self.numberOfSteps = int(self._xdatcarFile["n_frames"]) - self._chemicalSystem = ChemicalSystem() + self._chemical_system = ChemicalSystem() + element_list = [] for symbol, number in zip( self._xdatcarFile["atoms"], self._xdatcarFile["atom_numbers"] ): for i in range(number): element = get_element_from_mapping(self._atomicAliases, symbol) - self._chemicalSystem.add_chemical_entity( - Atom(symbol=element, name="{:s}_{:d}".format(symbol, i)) - ) + element_list.append(element) + self._chemical_system.initialise_atoms(element_list) # A trajectory is opened for writing. self._trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - self._chemicalSystem, + self._chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], @@ -144,9 +144,6 @@ def run_step(self, index): # The real coordinates are folded then into the simulation box (-L/2,L/2). real_conf.fold_coordinates() - # Bind the configuration to the chemcial system - self._trajectory.chemical_system.configuration = real_conf - # Compute the actual time time = ( self._xdatcarFile["step_number"] @@ -156,7 +153,9 @@ def run_step(self, index): # Dump the configuration to the output trajectory self._trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + real_conf, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) return index, None @@ -180,6 +179,7 @@ def finalize(self): self._xdatcarFile.close() # Close the output trajectory. + self._trajectory.write_standard_atom_database() self._trajectory.close() super(VASP, self).finalize() diff --git a/MDANSE/Src/MDANSE/Framework/InputData/HDFTrajectoryInputData.py b/MDANSE/Src/MDANSE/Framework/InputData/HDFTrajectoryInputData.py index 42e66c4602..715213aa36 100644 --- a/MDANSE/Src/MDANSE/Framework/InputData/HDFTrajectoryInputData.py +++ b/MDANSE/Src/MDANSE/Framework/InputData/HDFTrajectoryInputData.py @@ -87,11 +87,8 @@ def info(self): mol_types = {} val.append("\nMolecular types found:") - for ce in self._data.chemical_system.chemical_entities: - if ce.__class__.__name__ in mol_types: - mol_types[ce.__class__.__name__] += 1 - else: - mol_types[ce.__class__.__name__] = 1 + for molname, mollist in self._data.chemical_system._clusters.items(): + val.append(f"Molecule: {molname}; Count: {len(mollist)}") for k, v in mol_types.items(): val.append("\t- {:d} {}".format(v, k)) diff --git a/MDANSE/Src/MDANSE/Framework/InputData/MockTrajectoryInputData.py b/MDANSE/Src/MDANSE/Framework/InputData/MockTrajectoryInputData.py index b4faa94f49..b1f2621945 100644 --- a/MDANSE/Src/MDANSE/Framework/InputData/MockTrajectoryInputData.py +++ b/MDANSE/Src/MDANSE/Framework/InputData/MockTrajectoryInputData.py @@ -48,11 +48,8 @@ def info(self): val.append("%s\n" % len(self._data)) mol_types = {} val.append("\nMolecular types found:") - for ce in self._data.chemical_system.chemical_entities: - if ce.__class__.__name__ in mol_types: - mol_types[ce.__class__.__name__] += 1 - else: - mol_types[ce.__class__.__name__] = 1 + for name, list in self._data.chemical_system._clusters.items(): + mol_types[name] = len(list) for k, v in mol_types.items(): val.append("\t- {:d} {}".format(v, k)) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/AngularCorrelation.py b/MDANSE/Src/MDANSE/Framework/Jobs/AngularCorrelation.py index e86467e537..3ca179ecc1 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/AngularCorrelation.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/AngularCorrelation.py @@ -19,6 +19,7 @@ import numpy as np from scipy.signal import correlate +from MDANSE.Mathematics.Geometry import center_of_mass from MDANSE.Framework.Jobs.IJob import IJob @@ -84,17 +85,18 @@ def initialize(self): """ super().initialize() - ce_list = self.configuration["trajectory"][ + self.molecules = self.configuration["trajectory"][ "instance" - ].chemical_system.chemical_entities - self.molecules = [ - ce - for ce in ce_list - if ce.name == self.configuration["molecule_name"]["value"] - ] + ].chemical_system._clusters[self.configuration["molecule_name"]["value"]] self.numberOfSteps = len(self.molecules) + self.masses = np.array( + self.configuration["trajectory"]["instance"].chemical_system.atom_property( + "atomic_weight" + ) + ) + self._outputData.add( "time", "LineOutputVariable", @@ -154,8 +156,7 @@ def run_step(self, index): """ molecule = self.molecules[index] - reference_atom = molecule.atom_list[0] - chemical_system = self.configuration["trajectory"]["instance"].chemical_system + masses = self.masses[molecule] at1_traj = np.empty((self.configuration["frames"]["number"], 3)) at2_traj = np.empty((self.configuration["frames"]["number"], 3)) @@ -170,10 +171,10 @@ def run_step(self, index): configuration = self.configuration["trajectory"]["instance"].configuration( frame_index ) - contiguous_configuration = configuration.contiguous_configuration() - chemical_system.configuration = contiguous_configuration - at1_traj[i] = molecule.centre_of_mass(contiguous_configuration) - at2_traj[i] = reference_atom.centre_of_mass(contiguous_configuration) + coordinates = configuration.contiguous_configuration().coordinates + centre_coordinates = center_of_mass(coordinates[molecule], masses) + at1_traj[i] = centre_coordinates + at2_traj[i] = coordinates[molecule[0]] diff = at2_traj - at1_traj diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/AreaPerMolecule.py b/MDANSE/Src/MDANSE/Framework/Jobs/AreaPerMolecule.py index 19bd012c7c..dece49de23 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/AreaPerMolecule.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/AreaPerMolecule.py @@ -81,7 +81,7 @@ def initialize(self): # This will define the number of steps of the analysis. MUST be defined for all analysis. self.numberOfSteps = self.configuration["frames"]["number"] - # Extract the indexes corresponding to the axis selection (a=0,b=1,c=2). + # Extract the indices corresponding to the axis selection (a=0,b=1,c=2). axis_labels = self.configuration["axis"]["value"] if axis_labels == "ab": self._axisIndexes = [0, 1] @@ -92,12 +92,8 @@ def initialize(self): # The number of molecules that match the input name. Must be > 0. self._nMolecules = len( - [ - ce - for ce in self.configuration["trajectory"][ - "instance" - ].chemical_system.chemical_entities - if ce.name == self.configuration["molecule_name"]["value"] + self.configuration["trajectory"]["instance"].chemical_system._clusters[ + self.configuration["molecule_name"]["value"] ] ) if self._nMolecules == 0: diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py b/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py index a635759130..d91602e680 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py @@ -21,7 +21,6 @@ from ase.atoms import Atoms, Atom from MDANSE.Framework.Units import measure -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms from MDANSE.Framework.Jobs.IJob import IJob from MDANSE import PLATFORM @@ -86,9 +85,9 @@ def initialize(self): self.numberOfSteps = self.configuration["atom_selection"]["selection_length"] - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list target_unit = self.configuration["output_units"]["value"] if target_unit == "Angstrom": @@ -129,22 +128,21 @@ def run_step(self, index): tuple: the result of the step """ - # get selected atom indexes sublist - indexes = self.configuration["atom_selection"]["indexes"][index] - if len(indexes) == 1: + # get selected atom indices sublist + indices = self.configuration["atom_selection"]["indices"][index] + if len(indices) == 1: series = self.configuration["trajectory"][ "instance" ].read_atomic_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], ) else: - selected_atoms = [self._atoms[idx] for idx in indexes] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - selected_atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py index 53c79841db..9f0d9b9e76 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py @@ -18,15 +18,14 @@ import numpy as np - -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem +from MDANSE.Mathematics.Geometry import center_of_mass +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.MolecularDynamics.Configuration import ( PeriodicRealConfiguration, RealConfiguration, ) from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter -from MDANSE.MolecularDynamics.TrajectoryUtils import group_atoms class CenterOfMassesTrajectory(IJob): @@ -64,7 +63,8 @@ class CenterOfMassesTrajectory(IJob): "dependencies": { "trajectory": "trajectory", "atom_selection": "atom_selection", - } + }, + "default": "molecule", }, ) settings["output_files"] = ( @@ -79,26 +79,34 @@ def initialize(self): super().initialize() self.numberOfSteps = self.configuration["frames"]["number"] - - chemical_system = ChemicalSystem() - for i in range(len(self.configuration["atom_selection"]["indexes"])): - at = Atom(symbol="H", name="com_{:d}".format(i)) - chemical_system.add_chemical_entity(at) + chemical_system = self.configuration["trajectory"]["instance"].chemical_system + + new_element_list = [] + used_up_atoms = set() + new_chemical_system = ChemicalSystem() + for cluster_name in chemical_system._clusters.keys(): + for cluster in chemical_system._clusters[cluster_name]: + new_element_list.append(cluster_name) + used_up_atoms.update(set(cluster)) + for index in chemical_system._atom_indices: + if index not in used_up_atoms: + new_element_list.append(chemical_system.atom_list[index]) + self._used_up_atoms = used_up_atoms + new_chemical_system.initialise_atoms(new_element_list) # The output trajectory is opened for writing. self._output_trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], - chemical_system, + new_chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], compression=self.configuration["output_files"]["compression"], ) - - self._grouped_atoms = group_atoms( - self.configuration["trajectory"]["instance"].chemical_system, - self.configuration["atom_selection"]["indexes"], - ) + self._unique_atoms = np.unique(new_element_list) + self._molecule_radii = { + cluster_name: [] for cluster_name in chemical_system._clusters.keys() + } def run_step(self, index): """ @@ -113,15 +121,39 @@ def run_step(self, index): # get the Frame index frameIndex = self.configuration["frames"]["value"][index] + chemical_system = self.configuration["trajectory"]["instance"].chemical_system + atom_database = self.configuration["trajectory"]["instance"] n_coms = self._output_trajectory.chemical_system.number_of_atoms conf = self.configuration["trajectory"]["instance"].configuration(frameIndex) conf = conf.contiguous_configuration() + temp_radii = { + cluster_name: [] for cluster_name in chemical_system._clusters.keys() + } com_coords = np.empty((n_coms, 3), dtype=np.float64) - for i, group in enumerate(self._grouped_atoms): - com_coords[i, :] = group.center_of_mass(conf) + mol_index = 0 + for cluster_name in chemical_system._clusters.keys(): + for cluster in chemical_system._clusters[cluster_name]: + masses = [ + atom_database.get_atom_property( + chemical_system.atom_list[cluster_index], "atomic_weight" + ) + for cluster_index in cluster + ] + individual_coordinates = conf.coordinates[cluster] + centre_of_mass = center_of_mass(individual_coordinates, masses) + com_coords[mol_index] = centre_of_mass + average_radius = individual_coordinates - centre_of_mass.reshape(1, 3) + average_radius = np.linalg.norm(average_radius, axis=1) + average_radius = np.average(average_radius, weights=masses) + temp_radii[cluster_name].append(average_radius) + mol_index += 1 + for atom_index in chemical_system._atom_indices: + if atom_index not in self._used_up_atoms: + com_coords[mol_index] = conf.coordinates[atom_index] + mol_index += 1 if conf.is_periodic: com_conf = PeriodicRealConfiguration( @@ -135,12 +167,12 @@ def run_step(self, index): if self.configuration["fold"]["value"]: com_conf.fold_coordinates() - self._output_trajectory.chemical_system.configuration = com_conf - + for cluster_name in temp_radii.keys(): + self._molecule_radii[cluster_name].append(np.mean(temp_radii[cluster_name])) # The times corresponding to the running index. time = self.configuration["frames"]["time"][index] - self._output_trajectory.dump_configuration(time) + self._output_trajectory.dump_configuration(com_conf, time) return index, None @@ -158,6 +190,16 @@ def finalize(self): Finalizes the calculations (e.g. averaging the total term, output files creations ...). """ + time_averaged_radii = { + cluster_name: np.mean(self._molecule_radii[cluster_name]) + for cluster_name in self._molecule_radii.keys() + } + + self._output_trajectory.write_atom_database( + self._unique_atoms, + self.configuration["trajectory"]["instance"], + time_averaged_radii, + ) # The input trajectory is closed. self.configuration["trajectory"]["instance"].close() diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py b/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py index d27a1841cf..04a5a7f079 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py @@ -46,8 +46,14 @@ class CoordinationNumber(DistanceHistogram): {"dependencies": {"trajectory": "trajectory"}}, ) settings["r_values"] = ( - "RangeConfigurator", - {"valueType": float, "includeLast": True, "mini": 0.0}, + "DistHistCutoffConfigurator", + { + "label": "r values (nm)", + "valueType": float, + "includeLast": True, + "mini": 0.0, + "dependencies": {"trajectory": "trajectory"}, + }, ) settings["atom_selection"] = ( "AtomSelectionConfigurator", @@ -159,7 +165,7 @@ def finalize(self): self.hIntra[idi, idj] += self.hIntra[idj, idi] self.hInter[idi, idj] += self.hInter[idj, idi] - fact = nij * nFrames * shellVolumes + fact = 2 * nij * nFrames * shellVolumes self.hIntra[idi, idj, :] /= fact self.hInter[idi, idj, :] /= fact diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py index ef7b2653a3..71f9b11b09 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py @@ -16,7 +16,6 @@ import collections from MDANSE.Framework.Jobs.IJob import IJob -from MDANSE.MolecularDynamics.Trajectory import sorted_atoms from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter @@ -59,31 +58,29 @@ def initialize(self): self.numberOfSteps = self.configuration["frames"]["number"] - atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + atoms = self.configuration["trajectory"]["instance"].chemical_system.atom_list # The collection of atoms corresponding to the atoms selected for output. - indexes = [ + indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] - self._selectedAtoms = [atoms[ind] for ind in indexes] - self._selected_indices = indexes + self._selectedAtoms = [atoms[ind] for ind in indices] + self._selected_indices = indices # The output trajectory is opened for writing. self._output_trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], self.configuration["trajectory"]["instance"].chemical_system, self.numberOfSteps, - self._selectedAtoms, + self._selected_indices, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], compression=self.configuration["output_files"]["compression"], initial_charges=[ self.configuration["trajectory"]["instance"].charges(0)[ind] - for ind in indexes + for ind in indices ], ) @@ -105,15 +102,13 @@ def run_step(self, index): cloned_conf = conf.clone(self._output_trajectory.chemical_system) - self._output_trajectory.chemical_system.configuration = cloned_conf - time = self.configuration["frames"]["time"][index] charge = self.configuration["trajectory"]["instance"].charges(index)[ self._selected_indices ] - self._output_trajectory.dump_configuration(time) + self._output_trajectory.dump_configuration(cloned_conf, time) self._output_trajectory.write_charges(charge, index) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/CurrentCorrelationFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/CurrentCorrelationFunction.py index c30e898a5b..8fde993606 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/CurrentCorrelationFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/CurrentCorrelationFunction.py @@ -169,7 +169,7 @@ def initialize(self): itertools.combinations_with_replacement(self._elements, 2) ) - self._indexesPerElement = self.configuration["atom_selection"].get_indexes() + self._indicesPerElement = self.configuration["atom_selection"].get_indices() for pair in self._elementsPairs: self._outputData.add( @@ -290,7 +290,7 @@ def run_step(self, index: int): dtype=np.complex64, ) - for element, idxs in list(self._indexesPerElement.items()): + for element, idxs in list(self._indicesPerElement.items()): for idx in idxs: coords = trajectory.read_atomic_trajectory( idx, diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/Density.py b/MDANSE/Src/MDANSE/Framework/Jobs/Density.py index 552b507b15..ac97da0fb0 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/Density.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/Density.py @@ -18,10 +18,8 @@ import numpy as np -from MDANSE.Chemistry import ATOMS_DATABASE from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Framework.Units import measure -from MDANSE.MolecularDynamics.Trajectory import sorted_atoms NAVOGADRO = 6.02214129e23 @@ -67,10 +65,9 @@ def initialize(self): "instance" ].chemical_system.number_of_atoms - self._symbols = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list, - "symbol", - ) + self._symbols = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list # Will store the time. self._outputData.add( @@ -143,7 +140,9 @@ def run_step(self, index): mass_density = ( sum( [ - ATOMS_DATABASE.get_atom_property(s, "atomic_weight") + self.configuration["trajectory"]["instance"].get_atom_property( + s, "atomic_weight" + ) for s in self._symbols ] ) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/DensityOfStates.py b/MDANSE/Src/MDANSE/Framework/Jobs/DensityOfStates.py index d9f4c62aa2..5ae1dcc775 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/DensityOfStates.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/DensityOfStates.py @@ -20,7 +20,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight from MDANSE.Mathematics.Signal import differentiate, get_spectrum -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms from MDANSE.MLogging import LOG @@ -76,7 +75,10 @@ class DensityOfStates(IJob): "WeightsConfigurator", { "default": "atomic_weight", - "dependencies": {"atom_selection": "atom_selection"}, + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + }, }, ) settings["output_files"] = ( @@ -156,9 +158,9 @@ def initialize(self): main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -175,11 +177,11 @@ def run_step(self, index): trajectory = self.configuration["trajectory"]["instance"] # get atom index - indexes = self.configuration["atom_selection"]["indexes"][index] + indices = self.configuration["atom_selection"]["indices"][index] if self.configuration["interpolation_order"]["value"] == 0: series = trajectory.read_configuration_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], @@ -187,7 +189,7 @@ def run_step(self, index): ) else: series = trajectory.read_atomic_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/DensityProfile.py b/MDANSE/Src/MDANSE/Framework/Jobs/DensityProfile.py index a6f2b51d72..5dae1d5302 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/DensityProfile.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/DensityProfile.py @@ -70,7 +70,12 @@ class DensityProfile(IJob): settings["dr"] = ("FloatConfigurator", {"default": 0.01, "mini": 1.0e-9}) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -91,9 +96,7 @@ def initialize(self): axis_index = self.configuration["axis"]["index"] - first_conf = self.configuration["trajectory"][ - "instance" - ].chemical_system.configuration + first_conf = self.configuration["trajectory"]["instance"].configuration() try: axis = first_conf.unit_cell.direct[axis_index, :] @@ -108,9 +111,9 @@ def initialize(self): self._outputData.add("r", "LineOutputVariable", (self._n_bins,), units="nm") - self._indexes_per_element = self.configuration["atom_selection"].get_indexes() + self._indices_per_element = self.configuration["atom_selection"].get_indices() - for element in self._indexes_per_element.keys(): + for element in self._indices_per_element.keys(): self._outputData.add( "dp_%s" % element, "LineOutputVariable", @@ -145,7 +148,7 @@ def run_step(self, index): dp_per_frame = {} - for k, v in self._indexes_per_element.items(): + for k, v in self._indices_per_element.items(): h = np.histogram( box_coords[v, axis_index], bins=self._n_bins, range=[0.0, 1.0] ) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/DipoleAutoCorrelationFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/DipoleAutoCorrelationFunction.py index 4dd3207c51..b48d0e99e5 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/DipoleAutoCorrelationFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/DipoleAutoCorrelationFunction.py @@ -18,6 +18,7 @@ import numpy as np from scipy.signal import correlate +from MDANSE.Mathematics.Geometry import center_of_mass from MDANSE.Framework.Jobs.IJob import IJob @@ -65,13 +66,12 @@ def initialize(self): """Initialize the input parameters and analysis self variables.""" super().initialize() - ce_list = self.configuration["trajectory"][ + self.chemical_system = self.configuration["trajectory"][ "instance" - ].chemical_system.chemical_entities - self.molecules = [ - ce - for ce in ce_list - if ce.name == self.configuration["molecule_name"]["value"] + ].chemical_system + + self.molecules = self.chemical_system._clusters[ + self.configuration["molecule_name"]["value"] ] self.numberOfSteps = len(self.molecules) @@ -120,12 +120,18 @@ def run_step(self, index) -> tuple[int, np.ndarray]: configuration = self.configuration["trajectory"]["instance"].configuration( frame_index ) + masses = [ + self.configuration["trajectory"]["instance"].get_atom_property( + self.chemical_system.atom_list[index], "atomic_weight" + ) + for index in molecule + ] charges = self.configuration["trajectory"]["instance"].charges(frame_index) contiguous_configuration = configuration.contiguous_configuration() - com = molecule.center_of_mass(contiguous_configuration) + coords = contiguous_configuration.coordinates[molecule] + com = center_of_mass(coords, masses) - for atm in molecule.atom_list: - idx = atm.index + for idx in molecule: try: q = self.configuration["atom_charges"]["charges"][idx] except KeyError: diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/DistanceHistogram.py b/MDANSE/Src/MDANSE/Framework/Jobs/DistanceHistogram.py index 47802dd18a..f5ddb7577d 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/DistanceHistogram.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/DistanceHistogram.py @@ -19,7 +19,10 @@ import numpy as np -from MDANSE.Extensions import van_hove +from MDANSE.Framework.Jobs.VanHoveFunctionDistinct import ( + van_hove_distinct, + find_index_groups, +) from MDANSE.Framework.Jobs.IJob import IJob, JobError from MDANSE.MolecularDynamics.TrajectoryUtils import atom_index_to_molecule_index @@ -69,7 +72,12 @@ class DistanceHistogram(IJob): ) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -85,12 +93,12 @@ def initialize(self): self.numberOfSteps = self.configuration["frames"]["number"] - self._indexes = [ + self._indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] - self._indexes = np.array(self._indexes, dtype=np.int32) + self._indices = np.array(self._indices, dtype=np.int32) self.selectedElements = self.configuration["atom_selection"]["unique_names"] @@ -106,7 +114,7 @@ def initialize(self): self.configuration["trajectory"]["instance"].chemical_system ) - self.indexToMolecule = np.array([lut[i] for i in self._indexes], dtype=np.int32) + self.indexToMolecule = np.array([lut[i] for i in self._indices], dtype=np.int32) nElements = len(self.selectedElements) @@ -137,6 +145,7 @@ def initialize(self): self._elementsPairs = sorted( itertools.combinations_with_replacement(self.selectedElements, 2) ) + self.indices_intra = find_index_groups(self.indexToMolecule, self.indexToSymbol) def detailed_unit_cell_error(self): raise ValueError( @@ -175,18 +184,18 @@ def run_step(self, index): if cell_volume < 1e-9: self.detailed_unit_cell_error() - coords = conf["coordinates"] + coords = conf["coordinates"][self._indices] scaleconfig = coords @ inverse_cell hIntraTemp = np.zeros(self.hIntra.shape, dtype=np.float64) - hInterTemp = np.zeros(self.hInter.shape, dtype=np.float64) + hTotalTemp = np.zeros(self.hInter.shape, dtype=np.float64) - van_hove.van_hove_distinct( + van_hove_distinct( direct_cell, - self.indexToMolecule, + self.indices_intra, self.indexToSymbol, hIntraTemp, - hInterTemp, + hTotalTemp, scaleconfig, scaleconfig, self.configuration["r_values"]["first"], @@ -194,9 +203,9 @@ def run_step(self, index): ) np.multiply(hIntraTemp, cell_volume, hIntraTemp) - np.multiply(hInterTemp, cell_volume, hInterTemp) + np.multiply(hTotalTemp, cell_volume, hTotalTemp) - return index, (cell_volume, hIntraTemp, hInterTemp) + return index, (cell_volume, hIntraTemp, hTotalTemp) def combine(self, index, x): """ @@ -216,7 +225,7 @@ def combine(self, index, x): # volume can variate during the MD (e.g. NPT conditions). This volume is the one that intervene in the density # calculation. self.hIntra += x[1] - self.hInter += x[2] + self.hInter += x[2] - x[1] for k, v in list(self._nAtomsPerElement.items()): self._concentrations[k] += float(v) / nAtoms diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/DynamicCoherentStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/DynamicCoherentStructureFactor.py index 818522b327..3482ee4ff4 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/DynamicCoherentStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/DynamicCoherentStructureFactor.py @@ -145,7 +145,7 @@ def initialize(self): self.configuration["atom_selection"]["unique_names"], 2 ) ) - self._indexesPerElement = self.configuration["atom_selection"].get_indexes() + self._indicesPerElement = self.configuration["atom_selection"].get_indices() for pair in self._elementsPairs: self._outputData.add( @@ -217,7 +217,7 @@ def run_step(self, index): coords = traj.configuration(frame)["coordinates"] - for element, idxs in self._indexesPerElement.items(): + for element, idxs in self._indicesPerElement.items(): selectedCoordinates = np.take(coords, idxs, axis=0) rho[element][i, :] = np.sum( np.exp(1j * np.dot(selectedCoordinates, qVectors)), axis=0 diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/DynamicIncoherentStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/DynamicIncoherentStructureFactor.py index 63e937560c..ef6d2ceb7a 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/DynamicIncoherentStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/DynamicIncoherentStructureFactor.py @@ -22,7 +22,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight from MDANSE.Mathematics.Signal import get_spectrum -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class DynamicIncoherentStructureFactor(IJob): @@ -112,9 +111,9 @@ def initialize(self): self._instrResolution = self.configuration["instrument_resolution"] - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list self._nOmegas = self._instrResolution["n_omegas"] @@ -197,22 +196,21 @@ def run_step(self, index): #. atomicSF (np.array): The atomic structure factor """ - indexes = self.configuration["atom_selection"]["indexes"][index] + indices = self.configuration["atom_selection"]["indices"][index] - if len(indexes) == 1: + if len(indices) == 1: series = self.configuration["trajectory"][ "instance" ].read_atomic_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], ) else: - selected_atoms = [self._atoms[idx] for idx in indexes] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - selected_atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/Eccentricity.py b/MDANSE/Src/MDANSE/Framework/Jobs/Eccentricity.py index e42c70f27c..39b568f780 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/Eccentricity.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/Eccentricity.py @@ -19,7 +19,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Geometry import center_of_mass -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class Eccentricity(IJob): @@ -76,13 +75,13 @@ def initialize(self): main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) - self._indexes = np.array( + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list + self._indices = np.array( [ idx - for idxs in self._configuration["atom_selection"]["indexes"] + for idxs in self._configuration["atom_selection"]["indices"] for idx in idxs ] ) @@ -107,7 +106,7 @@ def run_step(self, index: int): conf = self.configuration["trajectory"]["instance"].configuration(frameIndex) conf = conf.contiguous_configuration() - series = conf["coordinates"][self._indexes, :] + series = conf["coordinates"][self._indices, :] com = center_of_mass(series, masses=self._selectionMasses) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/ElasticIncoherentStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/ElasticIncoherentStructureFactor.py index ef1c022a71..755c1a55b4 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/ElasticIncoherentStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/ElasticIncoherentStructureFactor.py @@ -20,7 +20,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class ElasticIncoherentStructureFactor(IJob): @@ -84,7 +83,10 @@ class ElasticIncoherentStructureFactor(IJob): "WeightsConfigurator", { "default": "b_incoherent2", - "dependencies": {"atom_selection": "atom_selection"}, + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + }, }, ) settings["output_files"] = ( @@ -132,9 +134,9 @@ def initialize(self): main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -148,11 +150,10 @@ def run_step(self, index): """ # get atom index - indexes = self.configuration["atom_selection"]["indexes"][index] - atoms = [self._atoms[idx] for idx in indexes] + indices = self.configuration["atom_selection"]["indices"][index] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/GaussianDynamicIncoherentStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/GaussianDynamicIncoherentStructureFactor.py index 8b2f9b7127..5e8ce62d09 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/GaussianDynamicIncoherentStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/GaussianDynamicIncoherentStructureFactor.py @@ -22,7 +22,6 @@ from MDANSE.Mathematics.Arithmetic import weight from MDANSE.Mathematics.Signal import get_spectrum from MDANSE.MolecularDynamics.Analysis import mean_square_displacement -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class GaussianDynamicIncoherentStructureFactor(IJob): @@ -84,7 +83,10 @@ class GaussianDynamicIncoherentStructureFactor(IJob): "WeightsConfigurator", { "default": "b_incoherent2", - "dependencies": {"atom_selection": "atom_selection"}, + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + }, }, ) settings["output_files"] = ( @@ -181,9 +183,9 @@ def initialize(self): main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -197,11 +199,10 @@ def run_step(self, index): """ # get atom index - indexes = self.configuration["atom_selection"]["indexes"][index] - atoms = [self._atoms[idx] for idx in indexes] + indices = self.configuration["atom_selection"]["indices"][index] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/GeneralAutoCorrelationFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/GeneralAutoCorrelationFunction.py index b13a500913..cddda4c8d7 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/GeneralAutoCorrelationFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/GeneralAutoCorrelationFunction.py @@ -63,7 +63,12 @@ class GeneralAutoCorrelationFunction(IJob): settings["normalize"] = ("BooleanConfigurator", {"default": False}) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -119,12 +124,12 @@ def run_step(self, index): #. atomicGACF (np.array): the calculated auto-correlation function for the index """ - indexes = self.configuration["atom_selection"]["indexes"][index] + indices = self.configuration["atom_selection"]["indices"][index] series = self.configuration["trajectory"][ "instance" ].read_configuration_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py index b35766bdf5..579b3834ed 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py @@ -21,10 +21,9 @@ import h5py -from MDANSE.Chemistry.ChemicalEntity import AtomGroup from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.MolecularDynamics.Configuration import RealConfiguration -from MDANSE.MolecularDynamics.Trajectory import sorted_atoms, TrajectoryWriter +from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter class GlobalMotionFilteredTrajectory(IJob): @@ -87,20 +86,16 @@ def initialize(self): self.numberOfSteps = self.configuration["frames"]["number"] # The collection of atoms corresponding to the atoms selected for output. - atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + atoms = self.configuration["trajectory"]["instance"].chemical_system.atom_list self._selected_atoms = [] - for indexes in self.configuration["atom_selection"]["indexes"]: - for idx in indexes: + for indices in self.configuration["atom_selection"]["indices"]: + for idx in indices: self._selected_atoms.append(atoms[idx]) - self._selected_atoms = AtomGroup(self._selected_atoms) self._reference_atoms = [] - for indexes in self.configuration["reference_selection"]["indexes"]: - for idx in indexes: + for indices in self.configuration["reference_selection"]["indices"]: + for idx in indices: self._reference_atoms.append(atoms[idx]) - self._reference_atoms = AtomGroup(self._reference_atoms) self._output_trajectory = TrajectoryWriter( self.configuration["output_files"]["file"], @@ -141,8 +136,6 @@ def run_step(self, index): trajectory.chemical_system, coords, **variables ) - trajectory.chemical_system.configuration = current_configuration - # Case of the first frame. if frameIndex == self.configuration["frames"]["first"]: # A a linear transformation that shifts the center of mass of the reference atoms to the coordinate origin @@ -173,14 +166,14 @@ def run_step(self, index): new_configuration = RealConfiguration( self._output_trajectory.chemical_system, coords, None, **variables ) - self._output_trajectory.chemical_system.configuration = new_configuration - # The times corresponding to the running index. time = self.configuration["frames"]["time"][index] # Write the step. self._output_trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + new_configuration, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) return index, rms diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/Infrared.py b/MDANSE/Src/MDANSE/Framework/Jobs/Infrared.py index ecd2a73d30..5184111256 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/Infrared.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/Infrared.py @@ -18,6 +18,7 @@ import numpy as np from scipy.signal import correlate +from MDANSE.Mathematics.Geometry import center_of_mass from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Signal import differentiate, get_spectrum @@ -73,13 +74,12 @@ class Infrared(IJob): def initialize(self): super().initialize() - ce_list = self.configuration["trajectory"][ + self.chemical_system = self.configuration["trajectory"][ "instance" - ].chemical_system.chemical_entities - self.molecules = [ - ce - for ce in ce_list - if ce.name == self.configuration["molecule_name"]["value"] + ].chemical_system + + self.molecules = self.chemical_system._clusters[ + self.configuration["molecule_name"]["value"] ] self.numberOfSteps = len(self.molecules) @@ -155,12 +155,18 @@ def run_step(self, index: int) -> tuple[int, np.ndarray]: configuration = self.configuration["trajectory"]["instance"].configuration( frame_index ) + masses = [ + self.configuration["trajectory"]["instance"].get_atom_property( + self.chemical_system.atom_list[index], "atomic_weight" + ) + for index in molecule + ] charges = self.configuration["trajectory"]["instance"].charges(frame_index) contiguous_configuration = configuration.contiguous_configuration() - com = molecule.center_of_mass(contiguous_configuration) + coords = contiguous_configuration.coordinates[molecule] + com = center_of_mass(coords, masses) - for atm in molecule.atom_list: - idx = atm.index + for idx in molecule: try: q = self.configuration["atom_charges"]["charges"][idx] except KeyError: diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py b/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py index e54585f5f4..8ef6dc5fdd 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py @@ -23,12 +23,10 @@ import numpy as np -from MDANSE.Chemistry import ATOMS_DATABASE from MDANSE.Core.Error import Error from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Framework.OutputVariables.IOutputVariable import IOutputVariable from MDANSE.Framework.Units import measure -from MDANSE.MolecularDynamics.Trajectory import sorted_atoms from MDANSE.MLogging import LOG MCSTAS_UNITS_LUT = { @@ -128,32 +126,49 @@ def initialize(self): # The number of steps is set to 1 as the job is defined as single McStas run. self.numberOfSteps = 1 - symbols = sorted_atoms( - self.configuration["trajectory"]["instance"]._chemical_system.atom_list, - "symbol", - ) + symbols = self.configuration["trajectory"]["instance"].chemical_system.atom_list # Compute some parameters used for a proper McStas run self._mcStasPhysicalParameters = {"density": 0.0} self._mcStasPhysicalParameters["V_rho"] = 0.0 self._mcStasPhysicalParameters["weight"] = sum( - [ATOMS_DATABASE.get_atom_property(s, "atomic_weight") for s in symbols] + [ + self.configuration["trajectory"]["instance"].get_atom_property( + s, "atomic_weight" + ) + for s in symbols + ] ) self._mcStasPhysicalParameters["sigma_abs"] = ( np.mean( - [ATOMS_DATABASE.get_atom_property(s, "xs_absorption") for s in symbols] + [ + self.configuration["trajectory"]["instance"].get_atom_property( + s, "xs_absorption" + ) + for s in symbols + ] ) * MCSTAS_UNITS_LUT["nm2"] ) self._mcStasPhysicalParameters["sigma_coh"] = ( np.mean( - [ATOMS_DATABASE.get_atom_property(s, "xs_coherent") for s in symbols] + [ + self.configuration["trajectory"]["instance"].get_atom_property( + s, "xs_coherent" + ) + for s in symbols + ] ) * MCSTAS_UNITS_LUT["nm2"] ) self._mcStasPhysicalParameters["sigma_inc"] = ( np.mean( - [ATOMS_DATABASE.get_atom_property(s, "xs_incoherent") for s in symbols] + [ + self.configuration["trajectory"]["instance"].get_atom_property( + s, "xs_incoherent" + ) + for s in symbols + ] ) * MCSTAS_UNITS_LUT["nm2"] ) @@ -166,7 +181,10 @@ def initialize(self): self._mcStasPhysicalParameters["weight"] / cellVolume ) self._mcStasPhysicalParameters["V_rho"] += ( - configuration._chemical_system.number_of_atoms / cellVolume + self.configuration["trajectory"][ + "instance" + ].chemical_system.number_of_atoms + / cellVolume ) self._mcStasPhysicalParameters["density"] /= self.configuration["frames"][ "n_frames" diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/MeanSquareDisplacement.py b/MDANSE/Src/MDANSE/Framework/Jobs/MeanSquareDisplacement.py index 89f2ffac29..b59d04729d 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/MeanSquareDisplacement.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/MeanSquareDisplacement.py @@ -19,7 +19,6 @@ from MDANSE.MolecularDynamics.Analysis import mean_square_displacement from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class MeanSquareDisplacement(IJob): @@ -92,7 +91,12 @@ class MeanSquareDisplacement(IJob): ) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -128,9 +132,9 @@ def initialize(self): partial_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -143,22 +147,21 @@ def run_step(self, index): tuple: the result of the step """ - # get selected atom indexes sublist - indexes = self.configuration["atom_selection"]["indexes"][index] - if len(indexes) == 1: + # get selected atom indices sublist + indices = self.configuration["atom_selection"]["indices"][index] + if len(indices) == 1: series = self.configuration["trajectory"][ "instance" ].read_atomic_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], ) else: - selected_atoms = [self._atoms[idx] for idx in indexes] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - selected_atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py b/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py index 6a284c4816..cae1ed2420 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py @@ -133,9 +133,9 @@ def initialize(self): main_result=True, ) - self._indexes = [ + self._indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] @@ -158,7 +158,7 @@ def run_step(self, index): resolution = self.configuration["spatial_resolution"]["value"] indices = np.floor( - (conf["coordinates"][self._indexes, :] - self.min.reshape((1, 3))) + (conf["coordinates"][self._indices, :] - self.min.reshape((1, 3))) / resolution ).astype(int) unique_indices, counts = np.unique(indices, return_counts=True, axis=0) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/MoleculeFinder.py b/MDANSE/Src/MDANSE/Framework/Jobs/MoleculeFinder.py deleted file mode 100644 index 6ca566042d..0000000000 --- a/MDANSE/Src/MDANSE/Framework/Jobs/MoleculeFinder.py +++ /dev/null @@ -1,186 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import collections - -import numpy as np - -from MDANSE.Framework.Jobs.IJob import IJob -from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter -from MDANSE.MolecularDynamics.TrajectoryUtils import brute_formula -from MDANSE.MolecularDynamics.Connectivity import Connectivity -from MDANSE.Chemistry.Structrures import MoleculeTester -from MDANSE.MolecularDynamics.Configuration import ( - PeriodicRealConfiguration, - RealConfiguration, -) - - -class MoleculeFinder(IJob): - """ - Use this if your trajectory contains molecules, but MDANSE has not assigned - labels to them. MoleculeFinder will output a new trajectory in which bond - information is stored and groups of atoms connected by bonds are given labels - which are either InChI strings or chemical formulae of the groups. -

- THIS IS USABLE, BUT STILL AT AN EARLY STAGE AND NOT OPTIMISED -

- Finds potential molecules in the trajectory, based on - interatomic distances. -

- The tolerance is a fraction of expected bond length calculated from covalent radii. - This means that a pair of C and H atoms will be considered as connected by a bond - if they are at a distance smaller than (r_C + r_H) * (1 + tolerance). - """ - - label = "Molecule Finder" - - category = ( - "Analysis", - "Trajectory", - ) - - ancestor = ["hdf_trajectory", "molecular_viewer"] - - settings = collections.OrderedDict() - settings["trajectory"] = ("HDFTrajectoryConfigurator", {}) - settings["tolerance"] = ( - "FloatConfigurator", - {"default": 0.25}, - ) - settings["frames"] = ( - "FramesConfigurator", - {"dependencies": {"trajectory": "trajectory"}, "default": (0, -1, 1)}, - ) - settings["output_files"] = ( - "OutputTrajectoryConfigurator", - {"format": "MDTFormat"}, - ) - - def initialize(self): - """ - Initialize the input parameters and analysis self variables - """ - super().initialize() - - self.numberOfSteps = self.configuration["frames"]["number"] - self._input_trajectory = self.configuration["trajectory"]["instance"] - self._tolerance = self.configuration["tolerance"]["value"] - chemical_system = self._input_trajectory.chemical_system - - conn = Connectivity(trajectory=self._input_trajectory) - conn.find_molecules(tolerance=self._tolerance) - conn.add_bond_information() - chemical_system.rebuild(conn._molecules) - configuration = chemical_system.configuration - coords = configuration.contiguous_configuration().coordinates - for entity in chemical_system.chemical_entities: - if entity.number_of_atoms > 1: - moltester = MoleculeTester(entity, coords) - try: - inchistring = moltester.identify_molecule() - except: - inchistring = "" - if len(inchistring) > 0: - entity.name = inchistring - else: - entity.name = brute_formula(entity) - - # The output trajectory is opened for writing. - self._output_trajectory = TrajectoryWriter( - self.configuration["output_files"]["file"], - chemical_system, - self.numberOfSteps, - positions_dtype=self.configuration["output_files"]["dtype"], - chunking_limit=self.configuration["output_files"]["chunk_size"], - compression=self.configuration["output_files"]["compression"], - initial_charges=self.configuration["trajectory"]["instance"].charges(0), - ) - - def run_step(self, index): - """ - Runs a single step of the job.\n - - :Parameters: - #. index (int): The index of the step. - :Returns: - #. index (int): The index of the step. - #. None - """ - - # get the Frame index - frameIndex = self.configuration["frames"]["value"][index] - - n_coms = self._output_trajectory.chemical_system.number_of_atoms - - conf = self.configuration["trajectory"]["instance"].configuration(frameIndex) - conf = conf.contiguous_configuration() - - coords = conf.coordinates - - variables = {} - if self.configuration["trajectory"]["instance"].has_variable("velocities"): - variables = { - "velocities": self.configuration["trajectory"]["instance"] - .variable("velocities")[frameIndex, :, :] - .astype(np.float64) - } - - if conf.is_periodic: - com_conf = PeriodicRealConfiguration( - self._output_trajectory.chemical_system, - coords, - conf.unit_cell, - **variables, - ) - else: - com_conf = RealConfiguration( - self._output_trajectory.chemical_system, coords, **variables - ) - - self._output_trajectory.chemical_system.configuration = com_conf - - # The times corresponding to the running index. - time = self.configuration["frames"]["time"][index] - - charge = self.configuration["trajectory"]["instance"].charges(index) - - self._output_trajectory.dump_configuration(time) - - self._output_trajectory.write_charges(charge, index) - - return index, None - - def combine(self, index, x): - """ - Combines returned results of run_step.\n - :Parameters: - #. index (int): The index of the step.\n - #. x (any): The returned result(s) of run_step - """ - pass - - def finalize(self): - """ - Finalizes the calculations (e.g. averaging the total term, output files creations ...). - """ - - # The input trajectory is closed. - self.configuration["trajectory"]["instance"].close() - - # The output trajectory is closed. - self._output_trajectory.close() - super().finalize() diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py index 6b65f337c5..cb827cde9e 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py @@ -18,7 +18,6 @@ import numpy as np -from MDANSE.Chemistry import ATOMS_DATABASE from MDANSE.Core.Error import Error from MDANSE.Framework.Jobs.IJob import IJob @@ -390,8 +389,12 @@ def finalize(self): # Compute coherent functions and structure factor for pair in self._elementsPairs: - bi = ATOMS_DATABASE.get_atom_property(pair[0], "b_coherent") - bj = ATOMS_DATABASE.get_atom_property(pair[1], "b_coherent") + bi = self.configuration["trajectory"]["instance"].get_atom_property( + pair[0], "b_coherent" + ) + bj = self.configuration["trajectory"]["instance"].get_atom_property( + pair[1], "b_coherent" + ) ni = nAtomsPerElement[pair[0]] nj = nAtomsPerElement[pair[1]] ci = ni / nTotalAtoms @@ -426,7 +429,9 @@ def finalize(self): # Compute incoherent functions and structure factor for element, ni in nAtomsPerElement.items(): - bi = ATOMS_DATABASE.get_atom_property(element, "b_incoherent2") + bi = self.configuration["trajectory"]["instance"].get_atom_property( + element, "b_incoherent2" + ) ni = nAtomsPerElement[element] ci = ni / nTotalAtoms diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py index cb9669905f..5a78dfeba7 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py @@ -129,7 +129,7 @@ def finalize(self): self.hIntra[idi, idj] += self.hIntra[idj, idi] self.hInter[idi, idj] += self.hInter[idj, idi] - fact = nij * nFrames * shellVolumes + fact = 2 * nij * nFrames * shellVolumes pdf_intra = self.hIntra[idi, idj, :] / fact pdf_inter = self.hInter[idi, idj, :] / fact diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/PositionAutoCorrelationFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/PositionAutoCorrelationFunction.py index 5b40c2addb..1143f0f3ff 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/PositionAutoCorrelationFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/PositionAutoCorrelationFunction.py @@ -21,7 +21,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight from MDANSE.Mathematics.Signal import normalize -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class PositionAutoCorrelationFunction(IJob): @@ -74,7 +73,12 @@ class PositionAutoCorrelationFunction(IJob): ) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -110,9 +114,9 @@ def initialize(self): partial_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -126,11 +130,10 @@ def run_step(self, index): """ # get atom index - indexes = self.configuration["atom_selection"]["indexes"][index] - atoms = [self._atoms[idx] for idx in indexes] + indices = self.configuration["atom_selection"]["indices"][index] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/PositionPowerSpectrum.py b/MDANSE/Src/MDANSE/Framework/Jobs/PositionPowerSpectrum.py index 9ebc6d34cd..28989ade79 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/PositionPowerSpectrum.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/PositionPowerSpectrum.py @@ -20,8 +20,7 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight -from MDANSE.Mathematics.Signal import differentiate, get_spectrum -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms +from MDANSE.Mathematics.Signal import get_spectrum from MDANSE.MLogging import LOG @@ -71,7 +70,10 @@ class PositionPowerSpectrum(IJob): "WeightsConfigurator", { "default": "atomic_weight", - "dependencies": {"atom_selection": "atom_selection"}, + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + }, }, ) settings["output_files"] = ( @@ -151,9 +153,9 @@ def initialize(self): main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -170,11 +172,10 @@ def run_step(self, index): trajectory = self.configuration["trajectory"]["instance"] # get atom index - indexes = self.configuration["atom_selection"]["indexes"][index] - atoms = [self._atoms[idx] for idx in indexes] + indices = self.configuration["atom_selection"]["indices"][index] series = trajectory.read_com_trajectory( - atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/RadiusOfGyration.py b/MDANSE/Src/MDANSE/Framework/Jobs/RadiusOfGyration.py index 00931c72f5..82a0333bab 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/RadiusOfGyration.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/RadiusOfGyration.py @@ -81,9 +81,9 @@ def initialize(self): main_result=True, ) - self._indexes = [ + self._indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] @@ -113,7 +113,7 @@ def run_step(self, index): conf = self.configuration["trajectory"]["instance"].configuration(frameIndex) rog = radius_of_gyration( - conf["coordinates"][self._indexes, :], masses=self._masses, root=True + conf["coordinates"][self._indices, :], masses=self._masses, root=True ) return index, rog diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py index 015c87a916..1aa6682aed 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py @@ -20,16 +20,14 @@ import h5py -from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import AtomCluster +from MDANSE.Mathematics.Geometry import center_of_mass from MDANSE.Framework.Jobs.IJob import IJob, JobError from MDANSE.Mathematics.LinearAlgebra import Quaternion, Vector from MDANSE.Mathematics.Transformation import Translation -from MDANSE.MolecularDynamics.Configuration import _Configuration, RealConfiguration +from MDANSE.MolecularDynamics.Configuration import RealConfiguration from MDANSE.MolecularDynamics.Trajectory import ( RigidBodyTrajectoryGenerator, TrajectoryWriter, - sorted_atoms, ) @@ -120,16 +118,14 @@ def initialize(self): dtype=np.float64, ) - atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + atoms = self.configuration["trajectory"]["instance"].chemical_system.atom_list self._groups = [] for i in range(self.configuration["atom_selection"]["selection_length"]): - indexes = self.configuration["atom_selection"]["indexes"][i] + indices = self.configuration["atom_selection"]["indices"][i] self._groups.append( - AtomCluster("", [atoms[idx] for idx in indexes], parentless=True) + AtomCluster("", [atoms[idx] for idx in indices], parentless=True) ) self.numberOfSteps = len(self._groups) @@ -142,8 +138,8 @@ def initialize(self): unitCell = trajectory.unit_cell(self.referenceFrame) selectedAtoms = [] - for indexes in self.configuration["atom_selection"]["indexes"]: - for idx in indexes: + for indices in self.configuration["atom_selection"]["indices"]: + for idx in indices: selectedAtoms.append(atoms[idx]) # Create trajectory @@ -194,7 +190,7 @@ def finalize(self): """ """ group_coms = [ - group.center_of_mass(self._reference_configuration) + center_of_mass(self._reference_configuration[group]) for group in self._groups ] @@ -235,9 +231,10 @@ def finalize(self): Vector(*xyz) ) - self._output_trajectory._chemical_system.configuration = real_configuration self._output_trajectory.dump_configuration( - time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} + real_configuration, + time, + units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"}, ) outputFile = h5py.File(self.configuration["output_files"]["file"], "r+") @@ -261,7 +258,7 @@ def finalize(self): # Loop over the groups. for comp in range(self.configuration["atom_selection"]["selection_length"]): - aIndexes = self.configuration["atom_selection"]["indexes"][comp] + aIndexes = self.configuration["atom_selection"]["indices"][comp] outputFile.attrs["info"] += "Group %s: %s\n" % ( comp, diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareDeviation.py b/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareDeviation.py index 3c59460640..1ceeb2001f 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareDeviation.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareDeviation.py @@ -19,7 +19,6 @@ import numpy as np from MDANSE.Framework.Jobs.IJob import IJob -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class RootMeanSquareDeviation(IJob): @@ -111,9 +110,9 @@ def initialize(self): main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -123,11 +122,10 @@ def run_step(self, index): @type index: int. """ - indexes = self.configuration["atom_selection"]["indexes"][index] - atoms = [self._atoms[idx] for idx in indexes] + indices = self.configuration["atom_selection"]["indices"][index] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareFluctuation.py b/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareFluctuation.py index f161da2b67..5b8f3f190f 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareFluctuation.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/RootMeanSquareFluctuation.py @@ -18,7 +18,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.MolecularDynamics.Analysis import mean_square_fluctuation -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class RootMeanSquareFluctuation(IJob): @@ -71,17 +70,17 @@ def initialize(self): self.numberOfSteps = self.configuration["atom_selection"]["selection_length"] - # Will store the indexes. - indexes = [ + # Will store the indices. + indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] if self.configuration["grouping_level"]["value"] == "atom": - self._outputData.add("indexes", "LineOutputVariable", indexes) + self._outputData.add("indices", "LineOutputVariable", indices) else: self._outputData.add( - "indexes", + "indices", "LineOutputVariable", self.configuration["grouping_level"]["group_indices"], ) @@ -91,14 +90,14 @@ def initialize(self): "rmsf", "LineOutputVariable", (self.configuration["atom_selection"]["selection_length"],), - axis="indexes", + axis="indices", units="nm", main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -111,11 +110,10 @@ def run_step(self, index): #. rmsf (np.array): the calculated root mean square fluctuation for atom index """ # read the particle trajectory - indexes = self.configuration["atom_selection"]["indexes"][index] - atoms = [self._atoms[idx] for idx in indexes] + indices = self.configuration["atom_selection"]["indices"][index] series = self.configuration["trajectory"]["instance"].read_com_trajectory( - atoms, + indices, first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/SolventAccessibleSurface.py b/MDANSE/Src/MDANSE/Framework/Jobs/SolventAccessibleSurface.py index 5ed7947b91..79e1b22859 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/SolventAccessibleSurface.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/SolventAccessibleSurface.py @@ -15,13 +15,70 @@ # import collections +from typing import List import numpy as np +from scipy.spatial import KDTree -from MDANSE.Chemistry import ATOMS_DATABASE from MDANSE.Framework.Jobs.IJob import IJob +from MDANSE.MolecularDynamics.Configuration import padded_coordinates from MDANSE.Mathematics.Geometry import generate_sphere_points -from MDANSE.Extensions import sas_fast_calc + + +def solvent_accessible_surface( + coords: np.ndarray, + indexes: List[int], + vdwRadii: np.ndarray, + sphere_points: np.ndarray, + probe_radius_value: float, +): + + # Computes the Solvent Accessible Surface Based on the algorithm published by Shrake, A., and J. A. Rupley. JMB (1973) 79:351-371. + + sas = 0.0 + tree = KDTree(coords) + max_dist = np.max(vdwRadii) + probe_radius_value + min_dist = np.min(vdwRadii) + probe_radius_value + sphere_indices = set(range(len(sphere_points))) + for idx in indexes: + sphere_tree = KDTree( + coords[idx] + sphere_points * (vdwRadii[idx] + probe_radius_value) + ) + distance_dict = sphere_tree.sparse_distance_matrix(tree, max_distance=max_dist) + pair_array = np.array([pair for pair in distance_dict.keys()]) + value_array = np.array([value for value in distance_dict.values()]) + combined_array = np.hstack( + [pair_array, value_array.reshape((len(value_array), 1))] + )[np.where(pair_array[:, 1] != idx)] + blocked_for_sure = set( + combined_array[:, 0][np.where(combined_array[:, 2] <= min_dist)] + ) + free_for_sure = sphere_indices - set(combined_array[:, 0]) + uncertain = sphere_indices - free_for_sure - blocked_for_sure + confirmed = set() + if len(uncertain) > 0: + uncertain_lines = np.array( + [line for line in combined_array if line[0] in uncertain] + ) + neighbour_radii = np.array( + [vdwRadii[int(line[1])] for line in uncertain_lines] + ) + confirmed = set( + uncertain_lines[:, 0][ + np.where( + uncertain_lines[:, 2] < neighbour_radii + probe_radius_value + ) + ] + ) + free_for_sure.update(uncertain - confirmed) + sas += ( + len(free_for_sure) + / len(sphere_points) + * 4 + * np.pi + * (vdwRadii[idx] + probe_radius_value) ** 2 + ) + return sas class SolventAccessibleSurface(IJob): @@ -100,30 +157,17 @@ def initialize(self): generate_sphere_points(self.configuration["n_sphere_points"]["value"]), dtype=np.float64, ) - # The solid angle increment used to convert the sas from a number of accessible point to a surface. - self.solidAngleIncr = 4.0 * np.pi / len(self.spherePoints) - - # A mapping between the atom indexes and covalent_radius radius for the whole universe. - self.vdwRadii = dict( - [ - ( - at.index, - ATOMS_DATABASE.get_atom_property(at.symbol, "covalent_radius"), - ) - for at in self.configuration["trajectory"][ - "instance" - ].chemical_system.atom_list - ] - ) - self.vdwRadii_list = np.zeros( - (max(self.vdwRadii.keys()) + 1, 2), dtype=np.float64 - ) - for k, v in self.vdwRadii.items(): - self.vdwRadii_list[k] = np.array([k, v])[:] - self._indexes = [ + # A mapping between the atom indices and covalent_radius radius for the whole universe. + self.vdwRadii = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_property( + "vdw_radius" + ) # should it be covalent? + + self._indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] @@ -143,12 +187,27 @@ def run_step(self, index): # The configuration is made continuous. conf = conf.continuous_configuration() - - # Loop over the indexes of the selected atoms for the sas calculation. - sas = sas_fast_calc.sas( - conf["coordinates"], - self._indexes, - self.vdwRadii_list, + unit_cell = conf._unit_cell + + if conf.is_periodic: + padding_thickness = 1.05 * max( + self.configuration["probe_radius"]["value"], np.max(self.vdwRadii) + ) + coords, atom_indices = padded_coordinates( + conf["coordinates"], + unit_cell, + padding_thickness, + ) + temp_vdw_radii = [self.vdwRadii[atom_index] for atom_index in atom_indices] + else: + coords = conf["coordinates"] + temp_vdw_radii = self.vdwRadii + + # Loop over the indices of the selected atoms for the sas calculation. + sas = solvent_accessible_surface( + coords, + self._indices, + temp_vdw_radii, self.spherePoints, self.configuration["probe_radius"]["value"], ) @@ -172,9 +231,6 @@ def finalize(self): Finalize the job. """ - # The SAS is converted from a number of accessible points to a surface. - self._outputData["sas"] *= self.solidAngleIncr - # Write the output variables. self._outputData.write( self.configuration["output_files"]["root"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py index 5e20929d7a..2c9445f0a1 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py @@ -77,6 +77,7 @@ class StaticStructureFactor(DistanceHistogram): { "default": "b_coherent", "dependencies": { + "trajectory": "trajectory", "atom_selection": "atom_selection", "atom_transmutation": "atom_transmutation", }, @@ -176,7 +177,7 @@ def finalize(self): self.hIntra[idi, idj] += self.hIntra[idj, idi] self.hInter[idi, idj] += self.hInter[idj, idi] - fact = nij * nFrames * shellVolumes + fact = 2 * nij * nFrames * shellVolumes pdfIntra = self.hIntra[idi, idj, :] / fact pdfInter = self.hInter[idi, idj, :] / fact diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/Temperature.py b/MDANSE/Src/MDANSE/Framework/Jobs/Temperature.py index 740c0a5581..31f5e3822e 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/Temperature.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/Temperature.py @@ -18,11 +18,9 @@ import numpy as np -from MDANSE.Chemistry import ATOMS_DATABASE from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Framework.Units import measure from MDANSE.Mathematics.Signal import differentiate -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms KB = measure(1.380649e-23, "kg m2/s2 K").toval("uma nm2/ps2 K") @@ -114,9 +112,9 @@ def initialize(self): units="K", ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -129,11 +127,11 @@ def run_step(self, index): #. kineticEnergy (np.array): The calculated kinetic energy """ - atom = self._atoms[index] + symbol = self._atoms[index] - symbol = atom.symbol - - mass = ATOMS_DATABASE.get_atom_property(symbol, "atomic_weight") + mass = self.configuration["trajectory"]["instance"].get_atom_property( + symbol, "atomic_weight" + ) trajectory = self.configuration["trajectory"]["instance"] diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/TrajectoryEditor.py b/MDANSE/Src/MDANSE/Framework/Jobs/TrajectoryEditor.py index 7173202a66..407af90dbf 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/TrajectoryEditor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/TrajectoryEditor.py @@ -19,17 +19,14 @@ import numpy as np from MDANSE.Framework.Jobs.IJob import IJob -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.UnitCell import UnitCell from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter -from MDANSE.MolecularDynamics.Trajectory import sorted_atoms from MDANSE.MolecularDynamics.Configuration import ( PeriodicRealConfiguration, RealConfiguration, ) -from MDANSE.MolecularDynamics.TrajectoryUtils import brute_formula from MDANSE.MolecularDynamics.Connectivity import Connectivity -from MDANSE.Chemistry.Structrures import MoleculeTester class TrajectoryEditor(IJob): @@ -98,6 +95,9 @@ def initialize(self): self.numberOfSteps = self.configuration["frames"]["number"] self._input_trajectory = self.configuration["trajectory"]["instance"] + self._input_chemical_system = self.configuration["trajectory"][ + "instance" + ].chemical_system if self.configuration["unit_cell"]["apply"]: self._new_unit_cell = UnitCell(self.configuration["unit_cell"]["value"]) @@ -105,37 +105,36 @@ def initialize(self): self._new_unit_cell for _ in range(len(self._input_trajectory)) ] - atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) - # The collection of atoms corresponding to the atoms selected for output. - indexes = [ + indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] - self._indices = indexes - self._selectedAtoms = [atoms[ind] for ind in indexes] - elements = self.configuration["atom_selection"]["elements"] + self._indices = indices + temp_copy = list(self._input_chemical_system.atom_list) + indices_per_element = self.configuration["atom_selection"].get_indices() + for element, numbers in indices_per_element.items(): + for num in numbers: + temp_copy[num] = element + self._selectedAtoms = [temp_copy[ind] for ind in indices] + name_list = [self._input_chemical_system.name_list[ind] for ind in indices] new_chemical_system = ChemicalSystem("Edited system") - new_chemical_system.from_element_list([entry[0] for entry in elements]) + new_chemical_system.initialise_atoms(self._selectedAtoms, name_list) if self.configuration["molecule_tolerance"]["use_it"]: tolerance = self.configuration["molecule_tolerance"]["value"] - conn = Connectivity(trajectory=self._input_trajectory, selection=indexes) - conn.find_molecules(tolerance=tolerance) - conn.add_bond_information() - new_chemical_system.rebuild(conn._molecules) + conn = Connectivity(trajectory=self._input_trajectory, selection=indices) + conn.find_bonds(tolerance=tolerance) + conn.add_bond_information(new_chemical_system) conf = self.configuration["trajectory"]["instance"].configuration( self.configuration["frames"]["value"][0] ) - conf = conf.contiguous_configuration() - coords = conf.coordinates[indexes] + coords = conf.coordinates[indices] if conf.is_periodic: com_conf = PeriodicRealConfiguration( new_chemical_system, - coords[self._indices], + coords, conf.unit_cell, ) else: @@ -143,19 +142,13 @@ def initialize(self): new_chemical_system, coords, ) - new_chemical_system.configuration = com_conf coords = com_conf.contiguous_configuration().coordinates - for entity in new_chemical_system.chemical_entities: - if entity.number_of_atoms > 1: - moltester = MoleculeTester(entity, coords) - try: - inchistring = moltester.identify_molecule() - except: - inchistring = "" - if len(inchistring) > 0: - entity.name = inchistring - else: - entity.name = brute_formula(entity) + else: + new_chemical_system.add_bonds(self._input_chemical_system._bonds) + for key in self._input_chemical_system._clusters.keys(): + new_chemical_system.add_clusters( + self._input_chemical_system._clusters[key] + ) # The output trajectory is opened for writing. self._output_trajectory = TrajectoryWriter( @@ -182,7 +175,7 @@ def run_step(self, index): frameIndex = self.configuration["frames"]["value"][index] conf = self.configuration["trajectory"]["instance"].configuration(frameIndex) - conf = conf.contiguous_configuration() + conf = conf.contiguous_configuration(bring_to_centre=True) charges = self.configuration["trajectory"]["instance"].charges(frameIndex) coords = conf.coordinates @@ -214,8 +207,6 @@ def run_step(self, index): **variables, ) - self._output_trajectory.chemical_system.configuration = com_conf - new_charges = np.zeros(len(self._indices)) for number, at_index in enumerate(self._indices): try: @@ -227,7 +218,7 @@ def run_step(self, index): # The times corresponding to the running index. time = self.configuration["frames"]["time"][index] - self._output_trajectory.dump_configuration(time) + self._output_trajectory.dump_configuration(com_conf, time) self._output_trajectory.write_charges(new_charges, index) return index, None diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/UnfoldedTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/UnfoldedTrajectory.py index 673783b6f5..845188cd9e 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/UnfoldedTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/UnfoldedTrajectory.py @@ -18,7 +18,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class UnfoldedTrajectory(IJob): @@ -62,31 +61,29 @@ def initialize(self): self.numberOfSteps = self.configuration["frames"]["number"] - atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + atoms = self.configuration["trajectory"]["instance"].chemical_system.atom_list # The collection of atoms corresponding to the atoms selected for output. - indexes = [ + indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] - self._selectedAtoms = [atoms[ind] for ind in indexes] - self._selection_indices = indexes + self._selectedAtoms = [atoms[ind] for ind in indices] + self._selection_indices = indices # The output trajectory is opened for writing. self._outputTraj = TrajectoryWriter( self.configuration["output_files"]["file"], self.configuration["trajectory"]["instance"].chemical_system, self.numberOfSteps, - self._selectedAtoms, + self._selection_indices, positions_dtype=self.configuration["output_files"]["dtype"], chunking_limit=self.configuration["output_files"]["chunk_size"], compression=self.configuration["output_files"]["compression"], initial_charges=[ self.configuration["trajectory"]["instance"].charges(0)[ind] - for ind in indexes + for ind in indices ], ) @@ -110,8 +107,6 @@ def run_step(self, index): cloned_conf = conf.clone(self._outputTraj.chemical_system) - self._outputTraj.chemical_system.configuration = cloned_conf - # The time corresponding to the running index. time = self.configuration["frames"]["time"][index] @@ -119,7 +114,7 @@ def run_step(self, index): self._selection_indices ] # Write the step. - self._outputTraj.dump_configuration(time) + self._outputTraj.dump_configuration(cloned_conf, time) self._outputTraj.write_charges(charge, index) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionDistinct.py b/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionDistinct.py index 4cdad36be0..7dfd561780 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionDistinct.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionDistinct.py @@ -15,15 +15,179 @@ # import collections import itertools as it +from typing import List, Dict, Tuple import numpy as np -from MDANSE.Extensions import van_hove +from MDANSE.MLogging import LOG from MDANSE.Framework.Jobs.IJob import IJob, JobError from MDANSE.MolecularDynamics.TrajectoryUtils import atom_index_to_molecule_index from MDANSE.Mathematics.Arithmetic import weight +def distance_array_2D( + ref_atoms: np.ndarray, other_atoms: np.ndarray, cell_array: np.ndarray +): + """Given two input arrays of atomic positions sized + (N,3) and (M,3), returns an (M, N) array of distances + between the atoms. + + Parameters + ---------- + ref_atoms : np.ndarray + (N, 3)-shaped array of atom positions + other_atoms : np.ndarray + (M, 3)-shaped array of atom positions + cell_array : np.ndarray + direct matrix of the unit cell of the system + + Returns + ------- + np.ndarray + (N, M)-shaped array of distances between atoms from the two arrays + """ + diff_frac = other_atoms.reshape((len(other_atoms), 1, 3)) - ref_atoms.reshape( + (1, len(ref_atoms), 3) + ) + temp = diff_frac + temp -= np.round(temp) + diff_real = np.matmul(temp, cell_array) + r = np.sqrt((diff_real**2).sum(axis=2)) + return r + + +def van_hove_distinct( + cell: np.ndarray, + indices_intra: Dict[int, Tuple[List[int]]], + symbolindex: List[int], + intra: np.ndarray, + total: np.ndarray, + scaleconfig_t0: np.ndarray, + scaleconfig_t1: np.ndarray, + rmin: float, + dr: float, +): + """Calculates the distance histogram between the configurations at + times t0 and t1. Distances are calculated using the minimum image + convention which are all worked out using the fractional coordinates + with the unit cell of the configuration at time t1. The function can + be used to calculate the distinct part of the van Hove function. + + The original implementation by Miguel Angel Gonzalez (Institut Laue + Langevin) has now been replaced by numpy functions. + + Parameters + ---------- + cell : np.ndarray + direct matrix of the unit cell of the system + indices_intra : Dict[int, Tuple[List[int]]] + array indices of the distance matrix elements for each molecule in the system + symbolindex : List[int] + list of int values of atom types in the system + intra : np.ndarray + the array accumulating the intramolecular distance counts for different atom type pairs + total : np.ndarray + the array accumulating all the distance counts for different atom type pairs + scaleconfig_t0 : np.ndarray + array of atom positions at time t0 + scaleconfig_t1 : np.ndarray + array of atom positions at time t1 + rmin : float + lowest distance allowed in the binning of the results + dr : float + size of the binning step + + Returns + ------- + Tuple[np.ndarray] + intra and total input arrays modified by adding new counts + """ + + nbins = intra.shape[2] + unique_types = np.unique(symbolindex) + type_indices = {} + for type1 in unique_types: + type_indices[type1] = np.where(symbolindex == type1) + + all_distances = distance_array_2D(scaleconfig_t0, scaleconfig_t1, cell.T) + + bins = ((all_distances - rmin) / dr).astype(int) + + bins[range(len(symbolindex)), range(len(symbolindex))] = -1 + + for dkey, indices in indices_intra.items(): + type1, type2 = dkey + sub_bins = bins[indices] + bin_numbers, bin_counts = np.unique( + sub_bins, + return_counts=True, + ) + for bin, counts in zip(bin_numbers, bin_counts): + if bin < 0: + continue + if bin >= nbins: + continue + intra[type1, type2, bin] += counts + + for type1 in unique_types: + for type2 in unique_types: + sub_bins = bins[np.ix_(type_indices[type1][0], type_indices[type2][0])] + bin_numbers, bin_counts = np.unique( + sub_bins, + return_counts=True, + ) + for bin, counts in zip(bin_numbers, bin_counts): + if bin < 0: + continue + if bin >= nbins: + continue + total[type1, type2, bin] += counts + + return intra, total + + +def find_index_groups( + molindex: List[int], + symbolindex: List[int], +): + """Finds and returns the indices of the distance array which + will be used for calculating the intramolecular distances. + These indices are expected to remain fixed within a run and + are only calculated once. + + Parameters + ---------- + molindex : List[int] + list containing, for each atom, the number of its corresponding molecule + symbolindex : List[int] + atom type given as an int number for each atom in the simulation + + Returns + ------- + Dict[Tuple[int], Tuple[List[int]]] + A dictionary of (atom_type1, atom_type2) : row_index_list, column_index_list values + """ + unique_types = np.unique(symbolindex) + intra = {} + for s1 in unique_types: + for s2 in unique_types: + intra[(s1, s2)] = [], [] + valid_indices = [x for x in range(len(molindex)) if molindex[x] >= 0] + for i in valid_indices: + for j in valid_indices: + if i == j: + continue + mol1 = molindex[i] + mol2 = molindex[j] + type1 = symbolindex[i] + type2 = symbolindex[j] + dkey = (type1, type2) + if mol1 == mol2: + intra[dkey][0].append(i) + intra[dkey][1].append(j) + return intra + + class VanHoveFunctionDistinct(IJob): """The van Hove function is related to the intermediate scattering function via a Fourier transform and the dynamic structure factor @@ -69,7 +233,12 @@ class VanHoveFunctionDistinct(IJob): ) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -170,13 +339,12 @@ def initialize(self): lut = atom_index_to_molecule_index( self.configuration["trajectory"]["instance"].chemical_system ) - self._indexes = [ + self._indices = [ idx - for idxs in self.configuration["atom_selection"]["indexes"] + for idxs in self.configuration["atom_selection"]["indices"] for idx in idxs ] - self._indexes = np.array(self._indexes, dtype=np.int32) - self.indexToMolecule = np.array([lut[i] for i in self._indexes], dtype=np.int32) + self.indexToMolecule = np.array([lut[i] for i in self._indices], dtype=np.int32) self.indexToSymbol = np.array( [ self.selectedElements.index(name) @@ -208,6 +376,8 @@ def initialize(self): (self.nElements, self.nElements, self.n_mid_points, self.numberOfSteps) ) + self.indices_intra = find_index_groups(self.indexToMolecule, self.indexToSymbol) + def run_step(self, time: int) -> tuple[int, tuple[np.ndarray, np.ndarray]]: """Calculates the distance histogram between the configurations at the inputted time difference. The distance histograms are @@ -225,7 +395,7 @@ def run_step(self, time: int) -> tuple[int, tuple[np.ndarray, np.ndarray]]: inter and intramolecular distance histograms. """ bins_intra = np.zeros((self.nElements, self.nElements, self.n_mid_points)) - bins_inter = np.zeros((self.nElements, self.nElements, self.n_mid_points)) + bins_total = np.zeros((self.nElements, self.nElements, self.n_mid_points)) # average the distance histograms at the inputted time # difference over a number of configuration @@ -234,28 +404,28 @@ def run_step(self, time: int) -> tuple[int, tuple[np.ndarray, np.ndarray]]: conf_t0 = self.configuration["trajectory"]["instance"].configuration( frame_index_t0 ) - coords_t0 = conf_t0["coordinates"] + coords_t0 = conf_t0["coordinates"][self._indices] frame_index_t1 = self.configuration["frames"]["value"][i + time] conf_t1 = self.configuration["trajectory"]["instance"].configuration( frame_index_t1 ) - coords_t1 = conf_t1["coordinates"] + coords_t1 = conf_t1["coordinates"][self._indices] direct_cell = conf_t1.unit_cell.transposed_direct inverse_cell = conf_t1.unit_cell.transposed_inverse scaleconfig_t0 = coords_t0 @ inverse_cell scaleconfig_t1 = coords_t1 @ inverse_cell - inter = np.zeros_like(bins_inter) - intra = np.zeros_like(bins_inter) + intra = np.zeros_like(bins_intra) + total = np.zeros_like(bins_total) - van_hove.van_hove_distinct( + intra, total = van_hove_distinct( direct_cell, - self.indexToMolecule, + self.indices_intra, self.indexToSymbol, intra, - inter, + total, scaleconfig_t0, scaleconfig_t1, self.configuration["r_values"]["first"], @@ -266,9 +436,9 @@ def run_step(self, time: int) -> tuple[int, tuple[np.ndarray, np.ndarray]]: # we multiply my the volume here and divide by the number # of atoms in finalize. bins_intra += conf_t1.unit_cell.volume * intra - bins_inter += conf_t1.unit_cell.volume * inter + bins_total += conf_t1.unit_cell.volume * total - return time, (bins_intra, bins_inter) + return time, (bins_intra, bins_total) def combine(self, time: int, x: tuple[np.ndarray, np.ndarray]): """Add the results into the histograms for the inputted time @@ -283,7 +453,7 @@ def combine(self, time: int, x: tuple[np.ndarray, np.ndarray]): configurations at the inputted time difference. """ self.h_intra[..., time] += x[0] - self.h_inter[..., time] += x[1] + self.h_inter[..., time] += x[1] - x[0] def finalize(self): """Using the distance histograms calculate, normalize and save the @@ -305,7 +475,7 @@ def finalize(self): self.h_intra[idi, idj] += self.h_intra[idj, idi] self.h_inter[idi, idj] += self.h_inter[idj, idi] - fact = nij * self.n_configs * self.shell_volumes + fact = 2 * nij * self.n_configs * self.shell_volumes van_hove_intra = self.h_intra[idi, idj, ...] / fact[:, np.newaxis] van_hove_inter = self.h_inter[idi, idj, ...] / fact[:, np.newaxis] van_hove_total = van_hove_intra + van_hove_inter diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionSelf.py b/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionSelf.py index 056c78b9c1..16bceccbf1 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionSelf.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/VanHoveFunctionSelf.py @@ -17,11 +17,55 @@ import numpy as np -from MDANSE.Extensions import van_hove from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight +def van_hove_self( + xyz: np.ndarray, + histograms: np.ndarray, + cell_vols: np.ndarray, + rmin: float, + dr: float, + n_configs: int, + n_frames: int, +): + """Calculates the distance histogram between an atom at time t0 + and the same atom at a time t0 + t. The results from this function + can be used to calculate the self part of the van Hove function. + + Parameters + ---------- + xyz : np.ndarray + The trajectory of an atom. + histograms : np.ndarray + The histograms to be updated. + cell_vols : np.ndarray + The cell volumes. + rmin : float + The minimum distance of the histogram. + dr : float + The distances between histogram bins. + n_configs : int + Number of configs to be averaged over. + n_frames : int + Number of correlation frames. + """ + + nbins = histograms.shape[0] + + for i in range(n_configs): + x0 = xyz[i] + r_array = xyz[i : i + n_frames] - x0.reshape((1, 3)) + distance_array = np.sqrt((r_array**2).sum(axis=1)) + bins = ((distance_array - rmin) / dr).astype(int) + valid_bins = np.logical_and(bins >= 0, bins < nbins) + for j in range(n_frames): + if valid_bins[j]: + histograms[bins[j], j] += cell_vols[i + j] + return histograms + + class VanHoveFunctionSelf(IJob): """The van Hove function is related to the intermediate scattering function via a Fourier transform and the dynamic structure factor @@ -66,7 +110,12 @@ class VanHoveFunctionSelf(IJob): ) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -184,7 +233,7 @@ def run_step(self, atm_index: int) -> tuple[int, tuple[np.ndarray, np.ndarray]]: last = self.configuration["frames"]["last"] + 1 step = self.configuration["frames"]["step"] - idx = self.configuration["atom_selection"]["indexes"][atm_index][0] + idx = self.configuration["atom_selection"]["indices"][atm_index][0] series = self.configuration["trajectory"]["instance"].read_atomic_trajectory( idx, first=first, @@ -200,7 +249,7 @@ def run_step(self, atm_index: int) -> tuple[int, tuple[np.ndarray, np.ndarray]]: ] ) - van_hove.van_hove_self( + histograms = van_hove_self( series, histograms, cell_vols, diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/VelocityAutoCorrelationFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/VelocityAutoCorrelationFunction.py index 55c96ed2f5..5a9edcf3b5 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/VelocityAutoCorrelationFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/VelocityAutoCorrelationFunction.py @@ -20,7 +20,6 @@ from MDANSE.Framework.Jobs.IJob import IJob from MDANSE.Mathematics.Arithmetic import weight from MDANSE.Mathematics.Signal import differentiate, normalize -from MDANSE.MolecularDynamics.TrajectoryUtils import sorted_atoms class VelocityAutoCorrelationFunction(IJob): @@ -90,7 +89,12 @@ class VelocityAutoCorrelationFunction(IJob): ) settings["weights"] = ( "WeightsConfigurator", - {"dependencies": {"atom_selection": "atom_selection"}}, + { + "dependencies": { + "trajectory": "trajectory", + "atom_selection": "atom_selection", + } + }, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -134,9 +138,9 @@ def initialize(self): main_result=True, ) - self._atoms = sorted_atoms( - self.configuration["trajectory"]["instance"].chemical_system.atom_list - ) + self._atoms = self.configuration["trajectory"][ + "instance" + ].chemical_system.atom_list def run_step(self, index): """ @@ -153,11 +157,11 @@ def run_step(self, index): trajectory = self.configuration["trajectory"]["instance"] # get atom index - indexes = self.configuration["atom_selection"]["indexes"][index] + indices = self.configuration["atom_selection"]["indices"][index] if self.configuration["interpolation_order"]["value"] == 0: series = trajectory.read_configuration_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], @@ -165,7 +169,7 @@ def run_step(self, index): ) else: series = trajectory.read_atomic_trajectory( - indexes[0], + indices[0], first=self.configuration["frames"]["first"], last=self.configuration["frames"]["last"] + 1, step=self.configuration["frames"]["step"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py b/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py index 5840fb49ad..19dde6be18 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py @@ -16,20 +16,23 @@ import collections import math +from typing import List import numpy as np from scipy.spatial import Voronoi as scipyVoronoi from scipy.spatial import Delaunay as scipyDelaunay -from MDANSE.Extensions import mic_fast_calc +from MDANSE.MolecularDynamics.Configuration import padded_coordinates from MDANSE.Framework.Jobs.IJob import IJob -def no_exc_min(l): +def no_exc_min(numbers: List[float]): try: - return min(l) - except: - return -np.pi + return min(numbers) + except ValueError: + return -1 + except TypeError: + return -2 class VoronoiError(Exception): @@ -38,11 +41,14 @@ class VoronoiError(Exception): class Voronoi(IJob): """ - Computes the volume of each Voronoi cell and corresponding 'neighbourhood' statistics for 3d systems. - Delaunay triangulation is used for the decomposition of polytops into simplexes, - Voronoi and Delaunay tessellation are calculated using a cython wrapping of the Qhull library (scipy wrapping used as Externals) + Computes the volume of each Voronoi cell and corresponding 'neighbourhood' + statistics for 3d systems. Vornoi diagram and Delaunay tesselation are + used as implemented in scipy.spatial module. Replicas of atoms from + the simulation box will be included in the calculation within + a finite distance from the box wall (given in nm). - Voronoi analysis is another commonly-used, complementary method for characterising the local structure of a system. + Voronoi analysis is another commonly-used, complementary method for + characterising the local structure of a system. **Acknowledgement:**\n Gael Goret, PELLEGRINI Eric @@ -68,7 +74,7 @@ class Voronoi(IJob): "BooleanConfigurator", {"label": "apply periodic_boundary_condition", "default": True}, ) - settings["pbc_border_size"] = ("FloatConfigurator", {"mini": 0.0, "default": 0.0}) + settings["pbc_border_size"] = ("FloatConfigurator", {"mini": 0.0, "default": 0.2}) settings["output_files"] = ( "OutputFilesConfigurator", {"formats": ["MDAFormat", "TextFormat"]}, @@ -97,9 +103,7 @@ def initialize(self): # Will store neighbourhood histogram for voronoi regions. self.neighbourhood_hist = {} - first_conf = self.configuration["trajectory"][ - "instance" - ].chemical_system.configuration + first_conf = self.configuration["trajectory"]["instance"].configuration() try: cell = first_conf.unit_cell.direct @@ -126,28 +130,31 @@ def run_step(self, index): frameIndex = self.configuration["frames"]["value"][index] conf = self.configuration["trajectory"]["instance"].configuration(frameIndex) + unit_cell = conf._unit_cell if self.configuration["pbc"]["value"]: - conf, _ = mic_fast_calc.mic_generator_3D( + coords, _ = padded_coordinates( conf["coordinates"], + unit_cell, self.configuration["pbc_border_size"]["value"], - self.cell_param, ) + else: + coords = conf["coordinates"] - # Computing Voronoi Diagram ... - Voronoi = scipyVoronoi(conf) + # Computing Voronoi Diagram + Voronoi = scipyVoronoi(coords) vertices_coords = Voronoi.vertices # Option qhull v p - # Extracting valid Voronoi regions ... + # Extracting valid Voronoi regions points_ids = Voronoi.regions # Option qhull v FN valid_regions_points_ids = [] valid_region_id = [] region_id = -1 for p in Voronoi.point_region: region_id += 1 - l = points_ids[p] - if no_exc_min(l) >= 0: - valid_regions_points_ids.append(l) + id_list = points_ids[p] + if no_exc_min(id_list) >= 0: + valid_regions_points_ids.append(id_list) valid_region_id.append(region_id) valid_regions = {} @@ -155,11 +162,11 @@ def run_step(self, index): vrid = valid_region_id[i] valid_regions[vrid] = valid_regions_points_ids[i] - # Extracting ridges of the valid Voronoi regions ... + # Extracting ridges of the valid Voronoi regions input_sites = Voronoi.ridge_points # Option qhull v Fv (part of) self.max_region_id = input_sites.max() - # Calculating neighbourhood ... + # Calculating neighbourhood neighbourhood = np.zeros((self.max_region_id + 1), dtype=np.int32) for s in input_sites.ravel(): neighbourhood[s] += 1 @@ -168,12 +175,12 @@ def run_step(self, index): for i in range(len(neighbourhood)): v = neighbourhood[i] if i in valid_region_id: - if v not in self.neighbourhood_hist: + if v not in self.neighbourhood_hist.keys(): self.neighbourhood_hist[v] = 1 else: self.neighbourhood_hist[v] += 1 - # Delaunay Tesselation of each valid voronoi region ... + # Delaunay Tesselation of each valid voronoi region delaunay_regions_for_each_valid_voronoi_region = {} for vrid, ids in list(valid_regions.items()): if vrid >= self.nb_init_pts: @@ -187,7 +194,7 @@ def run_step(self, index): lut[dv] for dv in Delaunay.simplices ] - # Volume Computation ... " + # Volume Computation global_volumes = {} for vrid, regions in list( delaunay_regions_for_each_valid_voronoi_region.items() diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py index 254f492f13..5da649cec0 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py @@ -18,25 +18,24 @@ import numpy as np -from MDANSE.Chemistry import ATOMS_DATABASE from MDANSE.Framework.Jobs.DistanceHistogram import DistanceHistogram from MDANSE.Mathematics.Arithmetic import weight -def atomic_scattering_factor(element, qvalues): +def atomic_scattering_factor(element, qvalues, trajectory): a = np.empty((4,), dtype=np.float64) - a[0] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_a1") - a[1] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_a2") - a[2] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_a3") - a[3] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_a4") + a[0] = trajectory.get_atom_property(element, "xray_asf_a1") + a[1] = trajectory.get_atom_property(element, "xray_asf_a2") + a[2] = trajectory.get_atom_property(element, "xray_asf_a3") + a[3] = trajectory.get_atom_property(element, "xray_asf_a4") b = np.empty((4,), dtype=np.float64) - b[0] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_b1") - b[1] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_b2") - b[2] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_b3") - b[3] = ATOMS_DATABASE.get_atom_property(element, "xray_asf_b4") + b[0] = trajectory.get_atom_property(element, "xray_asf_b1") + b[1] = trajectory.get_atom_property(element, "xray_asf_b2") + b[2] = trajectory.get_atom_property(element, "xray_asf_b3") + b[3] = trajectory.get_atom_property(element, "xray_asf_b4") - c = ATOMS_DATABASE.get_atom_property(element, "xray_asf_c") + c = trajectory.get_atom_property(element, "xray_asf_c") return c + np.sum( a[:, np.newaxis] @@ -69,12 +68,18 @@ class XRayStaticStructureFactor(DistanceHistogram): {"dependencies": {"trajectory": "trajectory"}}, ) settings["r_values"] = ( - "RangeConfigurator", - {"valueType": float, "includeLast": True, "mini": 0.0}, + "DistHistCutoffConfigurator", + { + "label": "r values (nm)", + "valueType": float, + "includeLast": True, + "mini": 0.0, + "dependencies": {"trajectory": "trajectory"}, + }, ) settings["q_values"] = ( "RangeConfigurator", - {"valueType": float, "includeLast": True, "mini": 0.0}, + {"valueType": float, "includeLast": True, "mini": 0.0, "default": (0, 500, 1)}, ) settings["atom_selection"] = ( "AtomSelectionConfigurator", @@ -165,7 +170,7 @@ def finalize(self): self.hIntra[idi, idj] += self.hIntra[idj, idi] self.hInter[idi, idj] += self.hInter[idj, idi] - fact = nij * nFrames * shellVolumes + fact = 2 * nij * nFrames * shellVolumes pdfIntra = self.hIntra[idi, idj, :] / fact pdfInter = self.hInter[idi, idj, :] / fact @@ -192,7 +197,14 @@ def finalize(self): ) asf = dict( - (k, atomic_scattering_factor(k, self._outputData["q"])) + ( + k, + atomic_scattering_factor( + k, + self._outputData["q"], + self.configuration["trajectory"]["instance"], + ), + ) for k in list(nAtomsPerElement.keys()) ) diff --git a/MDANSE/Src/MDANSE/Framework/QVectors/IQVectors.py b/MDANSE/Src/MDANSE/Framework/QVectors/IQVectors.py index 47588cabd3..573c1be92e 100644 --- a/MDANSE/Src/MDANSE/Framework/QVectors/IQVectors.py +++ b/MDANSE/Src/MDANSE/Framework/QVectors/IQVectors.py @@ -28,10 +28,10 @@ class QVectorsError(Error): class IQVectors(Configurable, metaclass=SubclassFactory): is_lattice = False - def __init__(self, chemical_system, status=None): + def __init__(self, atom_configuration, status=None): Configurable.__init__(self) - self._chemical_system = chemical_system + self._atom_configuration = atom_configuration self._status = status diff --git a/MDANSE/Src/MDANSE/Framework/QVectors/LatticeQVectors.py b/MDANSE/Src/MDANSE/Framework/QVectors/LatticeQVectors.py index 2a4da68fe1..537f545156 100644 --- a/MDANSE/Src/MDANSE/Framework/QVectors/LatticeQVectors.py +++ b/MDANSE/Src/MDANSE/Framework/QVectors/LatticeQVectors.py @@ -22,21 +22,17 @@ class LatticeQVectors(IQVectors): is_lattice = True - def __init__(self, chemical_system, status=None): - super(LatticeQVectors, self).__init__(chemical_system, status) + def __init__(self, atom_configuration, status=None): + super(LatticeQVectors, self).__init__(atom_configuration, status) - if self._chemical_system.configuration is None: + if atom_configuration is None: raise QVectorsError("No configuration set for the chemical system") - if not self._chemical_system.configuration.is_periodic: + if not atom_configuration.is_periodic: raise QVectorsError( "The universe must be periodic for building lattice-based Q vectors" ) - self._inverseUnitCell = ( - 2.0 * np.pi * self._chemical_system.configuration.unit_cell.inverse - ) + self._inverseUnitCell = 2.0 * np.pi * atom_configuration.unit_cell.inverse - self._directUnitCell = ( - 2.0 * np.pi * self._chemical_system.configuration.unit_cell.direct - ) + self._directUnitCell = 2.0 * np.pi * atom_configuration.unit_cell.direct diff --git a/MDANSE/Src/MDANSE/IO/MinimalPDBReader.py b/MDANSE/Src/MDANSE/IO/MinimalPDBReader.py index 9391573a1e..afb0b28d17 100644 --- a/MDANSE/Src/MDANSE/IO/MinimalPDBReader.py +++ b/MDANSE/Src/MDANSE/IO/MinimalPDBReader.py @@ -22,7 +22,7 @@ from MDANSE.MLogging import LOG from MDANSE.Framework.Units import measure from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem, Atom +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Configuration import ( RealConfiguration, PeriodicRealConfiguration, @@ -109,7 +109,7 @@ def find_atoms(self, filename: str, frame_number: int = 0): result.append(line) elif "HETATM" in line[0:6]: result.append(line) - if "ENDMDL" in line[0:6]: + if "END" in line[0:3]: fail_count += 1 if fail_count > 0: break @@ -119,7 +119,7 @@ def build_chemical_system(self, atom_lines: List[str]): """Build the chemical system. Returns: - MDANSE.Chemistry.ChemicalEntity.ChemicalSystem: the chemical system + MDANSE.Chemistry.ChemicalSystem.ChemicalSystem: the chemical system """ coordinates = [] @@ -128,16 +128,25 @@ def build_chemical_system(self, atom_lines: List[str]): posx_slice = atom_line_slice("pos_x") posy_slice = atom_line_slice("pos_y") posz_slice = atom_line_slice("pos_z") - pos_scaling = measure(1.0, "ang").toval("nm") + residue_slice = atom_line_slice("residue_name") + residue_number_slice = atom_line_slice("residue_number") + + element_list = [] + name_list = [] + label_dict = {} + clusters = {} - for atom_line in atom_lines: + for atom_number, atom_line in enumerate(atom_lines): chemical_element = atom_line[element_slice].strip() atom_name = atom_line[name_slice] processed_atom_name = atom_name[:2].strip() if len(processed_atom_name) == 2: - processed_atom_name = ( - processed_atom_name[0].upper() + processed_atom_name[1].lower() - ) + if processed_atom_name[0].isnumeric(): + processed_atom_name = processed_atom_name[1].upper() + else: + processed_atom_name = ( + processed_atom_name[0].upper() + processed_atom_name[1].lower() + ) if len(chemical_element) == 2: chemical_element = ( chemical_element[0].upper() + chemical_element[1].lower() @@ -149,34 +158,41 @@ def build_chemical_system(self, atom_lines: List[str]): else: backup_element3 = "fail" if backup_element in ATOMS_DATABASE.atoms: - atom = Atom(symbol=backup_element, name=atom_name) + element_list.append(backup_element) elif backup_element2 in ATOMS_DATABASE.atoms: - atom = Atom(symbol=backup_element2, name=atom_name) + element_list.append(backup_element2) elif backup_element3 in ATOMS_DATABASE.atoms: - atom = Atom(symbol=backup_element3, name=atom_name) + element_list.append(backup_element3) elif chemical_element in ATOMS_DATABASE.atoms: - atom = Atom(symbol=chemical_element, name=atom_name) + element_list.append(chemical_element) elif processed_atom_name in ATOMS_DATABASE.atoms: - atom = Atom(symbol=processed_atom_name, name=atom_name) + element_list.append(processed_atom_name) else: - LOG.warning(f"Dummy atom introduce from line {atom_line}") - atom = Atom(symbol="Du", name=atom_name) - self._chemical_system.add_chemical_entity(atom) + LOG.warning(f"Dummy atom introduced from line {atom_line}") + element_list.append("Du") x, y, z = ( atom_line[posx_slice], atom_line[posy_slice], atom_line[posz_slice], ) coordinates.append([float(aaa) for aaa in [x, y, z]]) - - coordinates = np.array(coordinates) - coordinates *= pos_scaling - - if self._unit_cell is None: - self._chemical_system.configuration = RealConfiguration( - self._chemical_system, coordinates - ) - else: - self._chemical_system.configuration = PeriodicRealConfiguration( - self._chemical_system, coordinates, UnitCell(self._unit_cell) - ) + residue_name = atom_line[residue_slice] + if residue_name not in label_dict.keys(): + label_dict[residue_name] = [] + label_dict[residue_name].append(atom_number) + name_list.append(atom_name.strip()) + residue_number_string = atom_line[residue_number_slice] + try: + residue_number = int(residue_number_string) + except ValueError: + try: + residue_number = int(residue_number_string, base=16) + except ValueError: + continue + if (residue_name, residue_number) in clusters.keys(): + clusters[(residue_name, residue_number)].append(atom_number) + else: + clusters[(residue_name, residue_number)] = [atom_number] + self._chemical_system.initialise_atoms(element_list, name_list) + self._chemical_system.add_labels(label_dict) + self._chemical_system.add_clusters(clusters.values()) diff --git a/MDANSE/Src/MDANSE/IO/PDB.py b/MDANSE/Src/MDANSE/IO/PDB.py deleted file mode 100644 index 5d1280c489..0000000000 --- a/MDANSE/Src/MDANSE/IO/PDB.py +++ /dev/null @@ -1,1480 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -""" -Parsing and writing of Protein Data Bank (PDB) files - -This module provides classes that represent PDB (Protein Data Bank) -files and configurations contained in PDB files. It provides access to -PDB files on two levels: low-level (line by line) and high-level -(chains, residues, and atoms). - -Caution: The PDB file format has been heavily abused, and it is -probably impossible to write code that can deal with all variants -correctly. This modules tries to read the widest possible range of PDB -files, but gives priority to a correct interpretation of the PDB -format as defined by the Brookhaven National Laboratory. - -A special problem are atom names. The PDB file format specifies that -the first two letters contain the right-justified chemical element -name. A later modification allowed the initial space in hydrogen names -to be replaced by a digit. Many programs ignore all this and treat the -name as an arbitrary left-justified four-character name. This makes it -difficult to extract the chemical element accurately; most programs -write the '"CA"' for C_alpha in such a way that it actually stands for -a calcium atom. For this reason a special element field has been added -later, but only few files use it. In the absence of an element field, -the code in this module attempts to guess the element using all information -available. - -The low-level routines in this module do not try to deal with the atom -name problem; they return and expect four-character atom names -including spaces in the correct positions. The high-level routines use -atom names without leading or trailing spaces, but provide and use the -element field whenever possible. For output, they use the element -field to place the atom name correctly, and for input, they construct -the element field content from the atom name if no explicit element -field is found in the file. - -Except where indicated, numerical values use the same units and -conventions as specified in the PDB format description. - -Example:: - - >>>conf = Structure('example.pdb') - >>>print conf - >>>for residue in conf.residues: - >>> for atom in residue: - >>> print atom - -@undocumented: atom_format -@undocumented: anisou_format -@undocumented: conect_format -@undocumented: ter_format -@undocumented: model_format -@undocumented: header_format -@undocumented: cryst1_format -@undocumented: scalen_format -@undocumented: mtrixn_format -@undocumented: generic_format -@undocumented: export_filters -@undocumented: DummyChain -""" -import string - -import numpy as np - -from MDANSE.Chemistry import NUCLEOTIDES_DATABASE, RESIDUES_DATABASE, RESIDUE_ALT_NAMES -from MDANSE.IO.FortranFormat import FortranFormat, FortranLine -from MDANSE.IO.PDBExportFilters import export_filters -from MDANSE.IO.TextFile import TextFile -from MDANSE.Mathematics.LinearAlgebra import Vector, Tensor - - -def cmp(a, b): - return (a > b) - (a < b) - - -# -# Fortran formats for PDB entries -# -atom_format = FortranFormat("A6,I5,1X,A4,A1,A4,A1,I4,A1,3X,3F8.3,2F6.2,6X,A4,2A2") -anisou_format = FortranFormat("A6,I5,1X,A4,A1,A4,A1,I4,A1,1X,6I7,2X,A4,2A2") -conect_format = FortranFormat("A6,11I5") -ter_format = FortranFormat("A6,I5,6X,A4,A1,I4,A1") -model_format = FortranFormat("A6,4X,I4") -header_format = FortranFormat("A6,4X,A40,A9,3X,A4") -cryst1_format = FortranFormat("A6,3F9.3,3F7.2,1X,A11,I4") -scalen_format = FortranFormat("A6,4X,3F10.6,5X,F10.5") -mtrixn_format = FortranFormat("A6,1X,I3,3F10.6,5X,F10.5,4X,I1") -generic_format = FortranFormat("A6,A74") - - -# -# Low-level file object. It represents line contents as Python dictionaries. -# For output, there are additional methods that generate sequence numbers -# for everything. -# -class PDBFile: - """ - X{PDB} file with access at the record level - - The low-level file access is handled by the module - L{Scientific.IO.TextFile}, therefore compressed files and URLs - (for reading) can be used as well. - """ - - def __init__(self, file_or_filename, mode="r", subformat=None): - """ - @param file_or_filename: the name of the PDB file, or a file object - @type file_or_filename: C{str} or C{file} - @param mode: the file access mode, 'r' (read) or 'w' (write) - @type mode: C{str} - @param subformat: indicates a specific dialect of the PDB format. - Subformats are defined in - L{Scientific.IO.PDBExportFilters}; they are used - only when writing. - @type subformat: C{str} or C{NoneType} - """ - - if isinstance(file_or_filename, str): - self.file = TextFile(file_or_filename, mode) - else: - self.file = file_or_filename - self.output = mode[0].lower() == "w" - self.export_filter = None - if subformat is not None: - export = export_filters.get(subformat, None) - if export is not None: - self.export_filter = export() - self.open = 1 - if self.output: - self.data = { - "serial_number": 0, - "residue_number": 0, - "chain_id": "", - "segment_id": "", - } - self.het_flag = 0 - self.chain_number = -1 - - def readLine(self): - """ - Return the contents of the next non-blank line (= record) The - return value is a tuple whose first element (a string) - contains the record type. For supported record types (HEADER, - CRYST1, SCALEn, MTRIXn, ATOM, HETATM, ANISOU, TERM, MODEL, - CONECT), the items from the remaining fields are put into a - dictionary which is returned as the second tuple element. Most - dictionary elements are strings or numbers; atom positions are - returned as a vector, and anisotropic temperature factors are - returned as a rank-2 tensor, already multiplied by 1.e-4. - White space is stripped from all strings except for atom - names, whose correct interpretation can depend on an initial - space. For unsupported record types, the second tuple element - is a string containing the remaining part of the record. - - @returns: the contents of one PDB record - @rtype: C{tuple} - """ - while 1: - line = self.file.readline() - if not line: - return ("END", "") - if line[-1] == "\n": - line = line[:-1] - line = line.strip() - if line: - break - line = line.ljust(80) - type = line[:6].strip() - if type == "ATOM" or type == "HETATM": - line = FortranLine(line, atom_format) - data = { - "serial_number": line[1], - "name": line[2], - "alternate": line[3].strip(), - "residue_name": line[4].strip(), - "chain_id": line[5].strip(), - "residue_number": line[6], - "insertion_code": line[7].strip(), - "position": Vector(line[8:11]), - "occupancy": line[11], - "temperature_factor": line[12], - "segment_id": line[13].strip(), - "element": line[14].strip(), - "charge": line[15].strip(), - } - return type, data - elif type == "ANISOU": - line = FortranLine(line, anisou_format) - data = { - "serial_number": line[1], - "name": line[2], - "alternate": line[3].strip(), - "residue_name": line[4].strip(), - "chain_id": line[5].strip(), - "residue_number": line[6], - "insertion_code": line[7].strip(), - "u": 1.0e-4 - * Tensor( - [ - [line[8], line[11], line[12]], - [line[11], line[9], line[13]], - [line[12], line[13], line[10]], - ] - ), - "segment_id": line[14].strip(), - "element": line[15].strip(), - "charge": line[16].strip(), - } - return type, data - elif type == "TER": - line = FortranLine(line, ter_format) - data = { - "serial_number": line[1], - "residue_name": line[2].strip(), - "chain_id": line[3].strip(), - "residue_number": line[4], - "insertion_code": line[5].strip(), - } - return type, data - elif type == "CONECT": - line = FortranLine(line, conect_format) - data = { - "serial_number": line[1], - "bonded": [i for i in line[2:6] if i > 0], - "hydrogen_bonded": [i for i in line[6:10] if i > 0], - "salt_bridged": [i for i in line[10:12] if i > 0], - } - return type, data - elif type == "MODEL": - line = FortranLine(line, model_format) - data = {"serial_number": line[1]} - return type, data - elif type == "HEADER": - line = FortranLine(line, header_format) - data = {"compound": line[1], "date": line[2], "pdb_code": line[3]} - return type, data - elif type == "CRYST1": - line = FortranLine(line, cryst1_format) - data = { - "a": line[1], - "b": line[2], - "c": line[3], - "alpha": line[4], - "beta": line[5], - "gamma": line[6], - "space_group": line[7], - "z": line[8], - } - return type, data - elif type[:-1] == "SCALE": - line = FortranLine(line, scalen_format) - data = {"s1": line[1], "s2": line[2], "s3": line[3], "u": line[4]} - return type, data - elif type[:-1] == "MTRIX": - line = FortranLine(line, mtrixn_format) - data = { - "serial": line[1], - "m1": line[2], - "m2": line[3], - "m3": line[4], - "v": line[5], - "given": line[6] == 1, - } - return type, data - else: - return type, line[6:] - - def writeLine(self, type, data): - """ - Write a line using record type and data dictionary in the - same format as returned by readLine(). Default values are - provided for non-essential information, so the data dictionary - need not contain all entries. - - @param type: PDB record type - @type type: C{str} - @param data: PDB record data - @type data: C{tuple} - """ - if self.export_filter is not None: - type, data = self.export_filter.processLine(type, data) - if type is None: - return - line = [type] - if type == "ATOM" or type == "HETATM": - format = atom_format - position = data["position"] - line = line + [ - data.get("serial_number", 1), - data.get("name"), - data.get("alternate", ""), - data.get("residue_name", "").rjust(3), - data.get("chain_id", ""), - data.get("residue_number", 1), - data.get("insertion_code", ""), - position[0], - position[1], - position[2], - data.get("occupancy", 0.0), - data.get("temperature_factor", 0.0), - data.get("segment_id", ""), - data.get("element", "").rjust(2), - data.get("charge", ""), - ] - elif type == "ANISOU": - format = anisou_format - u = 1.0e4 * data["u"] - u = [ - int(u[0, 0]), - int(u[1, 1]), - int(u[2, 2]), - int(u[0, 1]), - int(u[0, 2]), - int(u[1, 2]), - ] - line = ( - line - + [ - data.get("serial_number", 1), - data.get("name"), - data.get("alternate", ""), - data.get("residue_name").rjust(3), - data.get("chain_id", ""), - data.get("residue_number", 1), - data.get("insertion_code", ""), - ] - + u - + [ - data.get("segment_id", ""), - data.get("element", "").rjust(2), - data.get("charge", ""), - ] - ) - elif type == "TER": - format = ter_format - line = line + [ - data.get("serial_number", 1), - data.get("residue_name").rjust(3), - data.get("chain_id", ""), - data.get("residue_number", 1), - data.get("insertion_code", ""), - ] - elif type == "CONECT": - format = conect_format - line = line + [data.get("serial_number")] - line = line + (data.get("bonded", []) + 4 * [None])[:4] - line = line + (data.get("hydrogen_bonded", []) + 4 * [None])[:4] - line = line + (data.get("salt_bridged", []) + 2 * [None])[:2] - elif type == "MODEL": - format = model_format - line = line + [data.get("serial_number")] - elif type == "CRYST1": - format = cryst1_format - line = line + [ - data.get("a"), - data.get("b"), - data.get("c"), - data.get("alpha"), - data.get("beta"), - data.get("gamma"), - data.get("space_group"), - data.get("z"), - ] - elif type[:-1] == "SCALE": - format = scalen_format - line = line + [ - data.get("s1"), - data.get("s2"), - data.get("s3"), - data.get("u"), - ] - elif type[:-1] == "MTRIX": - format = scalen_format - line = line + [ - data.get("serial"), - data.get("m1"), - data.get("m2"), - data.get("m3"), - data.get("v"), - int(data.get("given")), - ] - elif type == "HEADER": - format = header_format - line = line + [ - data.get("compound", ""), - data.get("date", ""), - data.get("pdb_code"), - ] - else: - format = generic_format - line = line + [data] - self.file.write(str(FortranLine(line, format)) + "\n") - - def writeComment(self, text): - """ - Write text into one or several comment lines. - Each line of the text is prefixed with 'REMARK' and written - to the file. - - @param text: the comment contents - @type text: C{str} - """ - while text: - eol = text.find("\n") - if eol == -1: - eol = len(text) - self.file.write("REMARK %s \n" % text[:eol]) - text = text[eol + 1 :] - - def writeAtom( - self, - name, - position, - occupancy=0.0, - temperature_factor=0.0, - element="", - alternate="", - ): - """ - Write an ATOM or HETATM record using the information supplied. - The residue and chain information is taken from the last calls to - the methods L{nextResidue} and L{nextChain}. - - @param name: the atom name - @type name: C{str} - @param position: the atom position - @type position: L{Scientific.Geometry.Vector} - @param occupancy: the occupancy - @type occupancy: C{float} - @param temperature_factor: the temperature factor (B-factor) - @type temperature_factor: C{float} - @param element: the chemical element - @type element: C{str} - @param alternate: the alternate location character - @type element: C{str} - """ - if self.het_flag: - type = "HETATM" - else: - type = "ATOM" - name = name.upper() - if ( - element != "" - and len(element) == 1 - and name - and name[0] == element - and len(name) < 4 - ): - name = " " + name - self.data["name"] = name - self.data["position"] = position - self.data["serial_number"] = (self.data["serial_number"] + 1) % 100000 - self.data["alternate"] = alternate - self.data["occupancy"] = occupancy - self.data["temperature_factor"] = temperature_factor - self.data["element"] = element - self.writeLine(type, self.data) - - def nextResidue(self, name, number=None, terminus=None): - """ - Signal the beginning of a new residue, starting with the - next call to L{writeAtom}. - - @param name: the residue name - @type name: C{str} - @param number: the residue number. If C{None}, the residues - will be numbered sequentially, starting from 1. - @type number: C{int} or C{NoneType} - @param terminus: C{None}, "C", or "N". This information - is passed to export filters that can use this - information in order to use different atom or - residue names in terminal residues. - """ - name = name.upper() - if self.export_filter is not None: - name, number = self.export_filter.processResidue(name, number, terminus) - self.het_flag = not ( - name in RESIDUES_DATABASE - or name in RESIDUE_ALT_NAMES - or name in NUCLEOTIDES_DATABASE - ) - self.data["residue_name"] = name - self.data["residue_number"] = (self.data["residue_number"] + 1) % 10000 - self.data["insertion_code"] = "" - if number is not None: - if isinstance(number, int): - if number >= 0: - self.data["residue_number"] = number % 10000 - else: - self.data["residue_number"] = -((-number) % 1000) - else: - self.data["residue_number"] = number.number % 10000 - self.data["insertion_code"] = number.insertion_code - - def nextChain(self, chain_id=None, segment_id=""): - """ - Signal the beginning of a new chain. - - @param chain_id: a chain identifier. If C{None}, consecutive letters - from the alphabet are used. - @type chain_id: C{str} or C{NoneType} - @param segment_id: a chain identifier - @type segment_id: C{str} - """ - if chain_id is None: - self.chain_number = (self.chain_number + 1) % len(self._chain_ids) - chain_id = self._chain_ids[self.chain_number] - if self.export_filter is not None: - chain_id, segment_id = self.export_filter.processChain(chain_id, segment_id) - self.data["chain_id"] = (chain_id + " ")[:1] - self.data["segment_id"] = (segment_id + " ")[:4] - self.data["residue_number"] = 0 - - _chain_ids = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - - def terminateChain(self): - """ - Signal the end of a chain. - """ - if self.export_filter is not None: - self.export_filter.terminateChain() - self.data["serial_number"] = (self.data["serial_number"] + 1) % 100000 - self.writeLine("TER", self.data) - self.data["chain_id"] = "" - self.data["segment_id"] = "" - - def close(self): - """ - Close the file. This method B{must} be called for write mode - because otherwise the file will be incomplete. - """ - if getattr(self, "open", False): - if self.output: - self.file.write("END\n") - self.file.close() - self.open = 0 - - def __del__(self): - self.close() - - -# -# High-level object representation of PDB file contents. -# - - -# -# Representation of objects. -# -class PDBAtom: - """ - Atom in a PDB structure - """ - - def __init__(self, name, position, **properties): - """ - @param name: the atom name - @type name: C{str} - @param position: the atom position - @type position: L{Scientific.Geometry.Vector} - @param properties: any other atom properties as keyword parameters. - These properties are stored in the atom object - and can be accessed by indexing, as for - dictionaries. - """ - self.position = position - self.properties = properties - if self.properties.get("element", "") == "": - if name[0] == " " or name[0] in string.digits: - self.properties["element"] = name[1] - elif name[1] in string.digits: - self.properties["element"] = name[0] - else: - self.properties["element"] = name[0:2] - self.name = name.strip() - self.parent = None - - def __getitem__(self, item): - """ - @param item: the name of a property, including "name" or "position" - @type item: C{str} - @returns: the property value - """ - try: - return self.properties[item] - except KeyError: - if item == "name": - return self.name - elif item == "position": - return self.position - else: - raise KeyError("Undefined atom property: " + repr(item)) - - def __setitem__(self, item, value): - """ - @param item: the name of an existing or to be defined property - @type item: C{str} - @param value: the new value for the property - """ - self.properties[item] = value - - def __str__(self): - return self.__class__.__name__ + " " + self.name + " at " + str(self.position) - - __repr__ = __str__ - - def type(self): - """ - @returns: the six-letter record type, ATOM or HETATM - @rtype: C{str} - """ - return "ATOM " - - def writeToFile(self, file): - """ - Write an atom record to a file - - @param file: a PDB file object or a filename - @type file: L{PDBFile} or C{str} - """ - close = 0 - if type(file) == type(""): - file = PDBFile(file, "w") - close = 1 - file.writeAtom( - self.name, - self.position, - self.properties.get("occupancy", 0.0), - self.properties.get("temperature_factor", 0.0), - self.properties.get("element", ""), - ) - if close: - file.close() - - -class PDBHetAtom(PDBAtom): - """ - HetAtom in a PDB structure - - A subclass of Atom, which differs only in the return value - of the method type(). - """ - - def type(self): - return "HETATM" - - -class PDBGroup: - """ - Atom group (residue or molecule) in a PDB file - - This is an abstract base class. Instances can be created using - one of the subclasses (L{Molecule}, L{AminoAcidResidue}, - L{NucleotideResidue}). - - Group objects permit iteration over atoms with for-loops, - as well as extraction of atoms by indexing with the - atom name. - """ - - def __init__(self, name, atoms=None, number=None): - """ - @param name: the name of the group - @type name: C{str} - @param atoms: a list of atoms (or C{None} for no atoms) - @type atoms: C{list} or C{NoneType} - @param number: the PDB residue number (or C{None}) - @type number: C{int} or C{NoneType} - """ - self.name = name - self.number = number - self.atom_list = [] - self.atoms = {} - if atoms: - self.atom_list = atoms - for a in atoms: - self.atoms[a.name] = a - - def __len__(self): - return len(self.atom_list) - - def __getitem__(self, item): - """ - @param item: an integer index or an atom name - @type item: C{int} or C{str} - """ - if isinstance(item, int): - return self.atom_list[item] - else: - return self.atoms[item] - - def __str__(self): - s = self.__class__.__name__ + " " + self.name + ":\n" - for atom in self.atom_list: - s = s + " " + repr(atom) + "\n" - return s - - __repr__ = __str__ - - def isCompatible(self, residue_data): - return ( - residue_data["residue_name"] == self.name - and residue_data["residue_number"] == self.number - ) - - def addAtom(self, atom): - """ - Add an atom to the group - - @param atom: the atom - @type atom: L{Atom} - """ - self.atom_list.append(atom) - self.atoms[atom.name] = atom - atom.parent = self - - def deleteAtom(self, atom): - """ - Remove an atom from the group - - @param atom: the atom to be removed - @type atom: L{Atom} - @raises KeyError: if the atom is not part of the group - """ - self.atom_list.remove(atom) - del self.atoms[atom.name] - atom.parent = None - - def deleteHydrogens(self): - """ - Remove all hydrogen atoms of the group - """ - delete = [] - for a in self.atom_list: - if a.name[0] == "H" or (a.name[0] in string.digits and a.name[1] == "H"): - delete.append(a) - for a in delete: - self.deleteAtom(a) - - def changeName(self, name): - """ - Set the PDB residue name - - @param name: the new name - @type name: C{str} - """ - self.name = name - - def writeToFile(self, file): - """ - Write the group to a file - - @param file: a PDBFile object or a file name - @type file: L{PDBFile} or C{str} - """ - close = 0 - if type(file) == type(""): - file = PDBFile(file, "w") - close = 1 - file.nextResidue(self.name, self.number, None) - for a in self.atom_list: - a.writeToFile(file) - if close: - file.close() - - -class PDBMolecule(PDBGroup): - """ - Molecule in a PDB file - - B{Note:} In PDB files, non-chain molecules are treated as residues, - there is no separate molecule definition. This module defines - every residue as a molecule that is not an amino acid residue or a - nucleotide residue. - """ - - pass - - -class PDBResidue(PDBGroup): - pass - - -class PDBAminoAcidResidue(PDBResidue): - """ - Amino acid residue in a PDB file - """ - - is_amino_acid = 1 - - def isCTerminus(self): - """ - @returns: C{True} if the residue is in C-terminal configuration, - i.e. if it has a second oxygen bound to the carbon atom of - the peptide group. C{False} otherwise. - @rtype: C{bool} - """ - return self.name == "NME" or "OXT" in self.atoms or "OT2" in self.atoms - - def isNTerminus(self): - """ - @returns: C{True} if the residue is in N-terminal configuration, - i.e. if it contains more than one hydrogen bound to be - nitrogen atom of the peptide group. C{False} otherwise. - @rtype: C{bool} - """ - return ( - "1HT" in self.atoms - or "2HT" in self.atoms - or "3HT" in self.atoms - or "HT1" in self.atoms - or "HT2" in self.atoms - or "HT3" in self.atoms - or "1H" in self.atoms - or "2H" in self.atoms - or "3H" in self.atoms - or "H1" in self.atoms - or "H2" in self.atoms - or "H3" in self.atoms - ) - - def addAtom(self, atom): - PDBResidue.addAtom(self, atom) - if atom.name == "CA": # Make sure it's not a calcium - atom.properties["element"] = "C" - - def writeToFile(self, file): - close = 0 - if type(file) == type(""): - file = PDBFile(file, "w") - close = 1 - terminus = None - if self.isCTerminus(): - terminus = "C" - if self.isNTerminus(): - terminus = "N" - file.nextResidue(self.name, self.number, terminus) - for a in self.atom_list: - a.writeToFile(file) - if close: - file.close() - - -class PDBNucleotideResidue(PDBResidue): - """ - Nucleotide residue in a PDB file - """ - - is_nucleotide = 1 - - def __init__(self, name, atoms=None, number=None): - self.pdbname = name - name = name.strip() - if name[0] != "D" and name[0] != "R": - name = "D" + name - PDBResidue.__init__(self, name, atoms, number) - for a in atoms: - if a.name == "O2*" or a.name == "O2'": # Ribose - self.name = "R" + self.name[1:] - - def isCompatible(self, residue_data): - return ( - residue_data["residue_name"] == self.name - or residue_data["residue_name"] == self.pdbname - ) and residue_data["residue_number"] == self.number - - def addAtom(self, atom): - PDBResidue.addAtom(self, atom) - if atom.name == "O2*" or atom.name == "O2'": # Ribose - self.name = "R" + self.name[1:] - - def hasRibose(self): - """ - @returns: C{True} if the residue has an atom named O2* - @rtype: C{bool} - """ - return "O2*" in self.atoms or "O2'" in self.atoms - - def hasDesoxyribose(self): - """ - @returns: C{True} if the residue has no atom named O2* - @rtype: C{bool} - """ - return not self.hasRibose() - - def hasPhosphate(self): - """ - @returns: C{True} if the residue has a phosphate group - @rtype: C{bool} - """ - return "P" in self.atoms - - def hasTerminalH(self): - """ - @returns: C{True} if the residue has a 3-terminal H atom - @rtype: C{bool} - """ - return "H3T" in self.atoms - - def writeToFile(self, file): - close = 0 - if type(file) == type(""): - file = PDBFile(file, "w") - close = 1 - terminus = None - if not self.hasPhosphate(): - terminus = "5" - file.nextResidue(self.name[1:], self.number, terminus) - for a in self.atom_list: - a.writeToFile(file) - if close: - file.close() - - -class PDBChain: - """Chain of PDB residues - - This is an abstract base class. Instances can be created using - one of the subclasses (L{PeptideChain}, L{NucleotideChain}). - - Chain objects respond to len() and return their residues - by indexing with integers. - """ - - def __init__(self, residues=None, chain_id=None, segment_id=None): - """ - @param residues: a list of residue objects, or C{None} meaning - that the chain is initially empty - @type residues: C{list} or C{NoneType} - @param chain_id: a one-letter chain identifier or C{None} - @type chain_id: C{str} or C{NoneType} - @param segment_id: a multi-character segment identifier or C{None} - @type segment_id: C{str} or C{NoneType} - """ - if residues is None: - self.residues = [] - else: - self.residues = residues - self.chain_id = chain_id - self.segment_id = segment_id - - def __len__(self): - """ - @returns: the number of residues in the chain - @rtype: C{int} - """ - return len(self.residues) - - def sequence(self): - """ - @returns: the list of residue names - @rtype: C{list} of C{str} - """ - return [r.name for r in self.residues] - - def __getitem__(self, index): - """ - @param index: an index into the chain - @type index: C{int} - @returns: the residue corresponding to the index - @rtype: L{AminoAcidResidue} or L{NucleotideResidue} - @raises IndexError: if index exceeds the chain length - """ - return self.residues[index] - - def __getslice__(self, i1, i2): - """ - @param i1: in index into the chain - @type i1: C{int} - @param i2: in index into the chain - @type i12 C{int} - @returns: the subchain from i1 to i2 - @rtype: L{PeptideChain} or L{NucleotideChain} - """ - return self.__class__(self.residues[i1:i2]) - - def addResidue(self, residue): - """ - Add a residue at the end of the chain - - @param residue: the residue to be added - @type residue: L{AminoAcidResidue} or L{NucleotideResidue} - """ - self.residues.append(residue) - - def removeResidues(self, first, last): - """ - Remove residues in a given index range. - - @param first: the index of the first residue to be removed - @type first: C{int} - @param last: the index of the first residue to be kept, or C{None} - meaning remove everything to the end of the chain. - @type last: C{int} or C{NoneType} - """ - if last is None: - del self.residues[first:] - else: - del self.residues[first:last] - - def deleteHydrogens(self): - """ - Remove all hydrogen atoms in the chain - """ - for r in self.residues: - r.deleteHydrogens() - - def writeToFile(self, file): - """ - Write the chain to a file - - @param file: a PDBFile object or a file name - @type file: L{PDBFile} or C{str} - """ - close = 0 - if type(file) == type(""): - file = PDBFile(file, "w") - close = 1 - file.nextChain(self.chain_id, self.segment_id) - for r in self.residues: - r.writeToFile(file) - file.terminateChain() - if close: - file.close() - - -class PDBPeptideChain(PDBChain): - """ - Peptide chain in a PDB file - """ - - def isTerminated(self): - """ - @returns: C{True} if the last residue is in C-terminal configuration - @rtype: C{bool} - """ - return self.residues and self.residues[-1].isCTerminus() - - def isCompatible(self, chain_data, residue_data): - return ( - chain_data["chain_id"] == self.chain_id - and chain_data["segment_id"] == self.segment_id - and ( - residue_data["residue_name"] in RESIDUES_DATABASE - or residue_data["residue_name"] in RESIDUE_ALT_NAMES - ) - ) - - -class PDBNucleotideChain(PDBChain): - """ - Nucleotide chain in a PDB file - """ - - def isTerminated(self): - """ - @returns: C{True} if the last residue is in 3-terminal configuration - @rtype: C{bool} - @note: There is no way to perform this test with standard PDB files. - The algorithm used works for certain non-standard files only. - """ - return self.residues and ( - self.residues[-1].name[-1] == "3" or self.residues[-1].hasTerminalH() - ) - - def isCompatible(self, chain_data, residue_data): - return ( - chain_data["chain_id"] == self.chain_id - and chain_data["segment_id"] == self.segment_id - and residue_data["residue_name"] in NUCLEOTIDES_DATABASE - ) - - -class PDBDummyChain(PDBChain): - def __init__(self, structure, chain_id, segment_id): - self.structure = structure - self.chain_id = chain_id - self.segment_id = segment_id - - def isTerminated(self): - return 0 - - def addResidue(self, residue): - self.structure.addMolecule(residue) - - def isCompatible(self, chain_data, residue_data): - return ( - chain_data["chain_id"] == self.chain_id - and chain_data["segment_id"] == self.segment_id - and residue_data["residue_name"] not in RESIDUES_DATABASE - and residue_data["residue_name"] not in RESIDUE_ALT_NAMES - and residue_data["residue_name"] not in NUCLEOTIDES_DATABASE - ) - - -# -# Residue number class for dealing with insertion codes -# -class ResidueNumber: - """ - PDB residue number - - Most PDB residue numbers are simple integers, but when insertion - codes are used a number can consist of an integer plus a letter. - Such compound residue numbers are represented by this class. - """ - - def __init__(self, number, insertion_code): - """ - @param number: the numeric part of the residue number - @type number: C{int} - @param insertion_code: the letter part of the residue number - @type insertion_code: C{str} - """ - self.number = number - self.insertion_code = insertion_code - - def __cmp__(self, other): - if isinstance(other, int): - if self.number == other: - return 1 - else: - return cmp(self.number, other) - if self.number == other.number: - return cmp(self.insertion_code, other.insertion_code) - else: - return cmp(self.number, other.number) - - def __str__(self): - return str(self.number) + self.insertion_code - - __repr__ = __str__ - - -# -# The configuration class. -# -class Structure: - """ - A high-level representation of the contents of a PDB file - - The components of a structure can be accessed in several ways - ('s' is an instance of this class): - - - 's.residues' is a list of all PDB residues, in the order in - which they occurred in the file. - - - 's.peptide_chains' is a list of PeptideChain objects, containing - all peptide chains in the file in their original order. - - - 's.nucleotide_chains' is a list of NucleotideChain objects, containing - all nucleotide chains in the file in their original order. - - - 's.molecules' is a list of all PDB residues that are neither - amino acid residues nor nucleotide residues, in their original - order. - - - 's.objects' is a list of all high-level objects (peptide chains, - nucleotide chains, and molecules) in their original order. - - - 's.to_fractional' is the transformation from real-space coordinates - to fractional coordinates, as read from the SCALEn records. - - - 's.from_fractional' is the transformation from fractional coordinates - to real-space coordinates, the inverse of s.to_fractional. - - - 's.ncs_transformations' is a list of transformations that - describe non-crystallographic symmetries, as read from the - MTRIXn records. - - - if a CRYST1 record exists, 's.a', 's.b', 's.c', 's.alpha', - 's.beta', 's.gamma' are the parameters of the unit cell and - 's.space_group' is a string indicating the space group. - If no CRYST1 record exists, all those values are None. - Furthermore, 's.cs_transformations' is a list of transformations - that describe crystallographic symmetries. If no CRYST1 record - exists, the list is empty. - - An iteration over a Structure instance by a for-loop is equivalent - to an iteration over the residue list. - """ - - def __init__(self, file_or_filename, model=0, alternate_code="A"): - """ - @param file_or_filename: the name of the PDB file, or a file object. - Compressed files and URLs are accepted, - as for class L{PDBFile}. - @type file_or_filename: C{str} or C{file} - @param model: the number of the model to read from a multiple-model - file. Only one model can be treated at a time. - @type model: C{int} - @param alternate_code: the version of the positions to be read - from a file with alternate positions. - @type alternate_code: single-letter C{str} - """ - - if isinstance(file_or_filename, str): - self.filename = file_or_filename - else: - self.filename = "" - self.model = model - self.alternate = alternate_code - self.pdb_code = "" - self.residues = [] - self.objects = [] - self.peptide_chains = [] - self.nucleotide_chains = [] - self.molecules = {} - self.to_fractional = self.from_fractional = None - self.ncs_transformations = [] - self.cs_transformations = [] - self.a = self.b = self.c = None - self.alpha = self.beta = self.gamma = None - self.space_group = None - self.parseFile(PDBFile(file_or_filename, "rt")) - self.findSpaceGroupTransformations() - - peptide_chain_constructor = PDBPeptideChain - nucleotide_chain_constructor = PDBNucleotideChain - molecule_constructor = PDBMolecule - - def __len__(self): - return len(self.residues) - - def __getitem__(self, item): - return self.residues[item] - - def addMolecule(self, molecule): - try: - molecule_list = self.molecules[molecule.name] - except KeyError: - molecule_list = [] - self.molecules[molecule.name] = molecule_list - molecule_list.append(molecule) - self.objects.append(molecule) - - def extractData(self, data): - atom_data = {} - for name in [ - "serial_number", - "name", - "position", - "occupancy", - "temperature_factor", - ]: - atom_data[name] = data[name] - for name in ["alternate", "charge"]: - value = data[name] - if value: - atom_data[name] = value - element = data["element"] - if element != "": - try: - int(element) - except ValueError: - atom_data["element"] = element - residue_data = {"residue_name": data["residue_name"]} - number = data["residue_number"] - insertion = data["insertion_code"] - if insertion == "": - residue_data["residue_number"] = number - else: - residue_data["residue_number"] = ResidueNumber(number, insertion) - chain_data = {} - for name in ["chain_id", "segment_id"]: - chain_data[name] = data[name] - if chain_data["segment_id"] == self.pdb_code: - chain_data["segment_id"] = "" - return atom_data, residue_data, chain_data - - def newResidue(self, residue_data): - name = residue_data["residue_name"] - residue_number = residue_data["residue_number"] - if name in RESIDUES_DATABASE or name in RESIDUE_ALT_NAMES: - residue = PDBAminoAcidResidue(name, [], residue_number) - elif name in NUCLEOTIDES_DATABASE: - residue = PDBNucleotideResidue(name, [], residue_number) - else: - residue = self.molecule_constructor(name, [], residue_number) - self.residues.append(residue) - return residue - - def newChain(self, residue, chain_data): - if hasattr(residue, "is_amino_acid"): - chain = self.peptide_chain_constructor( - [], chain_data["chain_id"], chain_data["segment_id"] - ) - self.peptide_chains.append(chain) - self.objects.append(chain) - elif hasattr(residue, "is_nucleotide"): - chain = self.nucleotide_chain_constructor( - [], chain_data["chain_id"], chain_data["segment_id"] - ) - self.nucleotide_chains.append(chain) - self.objects.append(chain) - else: - chain = PDBDummyChain( - self, chain_data["chain_id"], chain_data["segment_id"] - ) - return chain - - def parseFile(self, file): - atom = None - residue = None - chain = None - read = self.model == 0 - while 1: - type, data = file.readLine() - if type == "END": - break - elif type == "HEADER": - self.pdb_code = data["pdb_code"] - elif type == "CRYST1": - for name, value in list(data.items()): - setattr(self, name, value) - self.space_group = self.space_group.strip() - elif type[:-1] == "SCALE": - if not hasattr(self, "_scale_matrix"): - self._scale_matrix = {} - self._scale_matrix[type[-1]] = data - if type[-1] == "3": # last line read - from MDANSE.Mathematics.Transformation import Shear, Translation - - l1 = self._scale_matrix["1"] - l2 = self._scale_matrix["2"] - l3 = self._scale_matrix["3"] - s = np.array( - [ - [l1["s1"], l1["s2"], l1["s3"]], - [l2["s1"], l2["s2"], l2["s3"]], - [l3["s1"], l3["s2"], l3["s3"]], - ] - ) - u = Vector(l1["u"], l2["u"], l3["u"]) - self.to_fractional = Translation(u) * Shear(s) - self.from_fractional = self.to_fractional.inverse() - del self._scale_matrix - elif type[:-1] == "MTRIX": - if not hasattr(self, "_ncs_matrix"): - self._ncs_matrix = {} - self._ncs_matrix[type[-1]] = data - if type[-1] == "3": # last line read - from MDANSE.Mathematics.Transformation import Rotation, Translation - - l1 = self._ncs_matrix["1"] - l2 = self._ncs_matrix["2"] - l3 = self._ncs_matrix["3"] - m = np.array( - [ - [l1["m1"], l1["m2"], l1["m3"]], - [l2["m1"], l2["m2"], l2["m3"]], - [l3["m1"], l3["m2"], l3["m3"]], - ] - ) - v = Vector(l1["v"], l2["v"], l3["v"]) - tr = Translation(v) * Rotation(Tensor(m)) - tr.given = data["given"] - tr.serial = data["serial"] - self.ncs_transformations.append(tr) - del self._ncs_matrix - elif type == "MODEL": - read = data["serial_number"] == self.model - if self.model == 0 and len(self.residues) == 0: - read = 1 - elif type == "ENDMDL": - read = 0 - elif read: - if type == "ATOM" or type == "HETATM": - alt = data["alternate"] - if alt == "" or alt == self.alternate: - atom_data, residue_data, chain_data = self.extractData(data) - if type == "ATOM": - atom = PDBAtom(*(), **atom_data) - else: - atom = PDBHetAtom(*(), **atom_data) - new_chain = chain is None or not chain.isCompatible( - chain_data, residue_data - ) - new_residue = ( - new_chain - or residue is None - or not residue.isCompatible(residue_data) - ) - if new_residue and chain is not None and chain.isTerminated(): - new_chain = 1 - if new_residue: - residue = self.newResidue(residue_data) - if new_chain: - chain = self.newChain(residue, chain_data) - chain.addResidue(residue) - residue.addAtom(atom) - elif type == "ANISOU": - alt = data["alternate"] - if alt == "" or alt == self.alternate: - if atom is None: - raise ValueError("ANISOU record before " + "ATOM record") - atom["u"] = data["u"] - elif type == "TERM": - if chain is None: - raise ValueError("TERM record before chain") - chain = None - - def findSpaceGroupTransformations(self): - if self.space_group is not None and self.to_fractional is not None: - from MDANSE.IO.PDBSpaceGroups import getSpaceGroupTransformations - - try: - trs = getSpaceGroupTransformations(self.space_group) - except KeyError: - return - for tr in trs: - tr = self.from_fractional * tr * self.to_fractional - self.cs_transformations.append(tr) - - def renumberAtoms(self): - """ - Renumber all atoms sequentially starting with 1 - """ - n = 0 - for residue in self.residues: - for atom in residue: - atom["serial_number"] = n - n = n + 1 - - def __repr__(self): - s = self.__class__.__name__ + "(" + repr(self.filename) - if self.model != 0: - s = s + ", model=" + repr(self.model) - if self.alternate != "A": - s = s + ", alternate_code = " + repr(self.alternate) - s = s + "):\n" - for name, list in [ - ("Peptide", self.peptide_chains), - ("Nucleotide", self.nucleotide_chains), - ]: - for c in list: - s = s + " " + name + " chain " - if c.segment_id: - s = s + c.segment_id + " " - elif c.chain_id: - s = s + c.chain_id + " " - s = s + "of length " + repr(len(c)) + "\n" - for name, list in self.molecules.items(): - s = s + " " + repr(len(list)) + " " + name + " molecule" - if len(list) == 1: - s = s + "\n" - else: - s = s + "s\n" - return s - - def writeToFile(self, file): - """ - Write everything to a file - - @param file: a PDB file object or a filename - @type file: L{PDBFile} or C{str} - """ - close = 0 - if type(file) == type(""): - file = PDBFile(file, "w") - close = 1 - for o in self.objects: - o.writeToFile(file) - if close: - file.close() diff --git a/MDANSE/Src/MDANSE/IO/PDBExportFilters.py b/MDANSE/Src/MDANSE/IO/PDBExportFilters.py deleted file mode 100644 index f014ac45f5..0000000000 --- a/MDANSE/Src/MDANSE/IO/PDBExportFilters.py +++ /dev/null @@ -1,50 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - - -class PDBExportFilter: - def processLine(self, type, data): - return type, data - - def processResidue(self, name, number, terminus): - return name, number - - def processChain(self, chain_id, segment_id): - return chain_id, segment_id - - def terminateChain(self): - pass - - -# -# XPlor export filter - -import string - - -class XPlorExportFilter(PDBExportFilter): - xplor_atom_names = {" OXT": "OT2"} - - def processLine(self, type, data): - if type == "TER": - return None, data - if type == "ATOM" or type == "HETATM" or type == "ANISOU": - name = self.xplor_atom_names.get(data["name"], data["name"]) - data["name"] = name - return type, data - - -export_filters = {"xplor": XPlorExportFilter} diff --git a/MDANSE/Src/MDANSE/IO/PDBReader.py b/MDANSE/Src/MDANSE/IO/PDBReader.py deleted file mode 100644 index 52c0b66242..0000000000 --- a/MDANSE/Src/MDANSE/IO/PDBReader.py +++ /dev/null @@ -1,664 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -import copy -import string - -import numpy as np - -from MDANSE.Chemistry.ChemicalEntity import ( - is_molecule, - Atom, - AtomCluster, - ChemicalSystem, - Molecule, - Nucleotide, - NucleotideChain, - Residue, - PeptideChain, - Protein, -) -from MDANSE.Chemistry import ( - ATOMS_DATABASE, - MOLECULES_DATABASE, - NUCLEOTIDES_DATABASE, - RESIDUES_DATABASE, - RESIDUE_ALT_NAMES, -) -from MDANSE.IO.PDB import PDBMolecule, PDBNucleotideChain, PDBPeptideChain, Structure -from MDANSE.MolecularDynamics.Configuration import RealConfiguration - - -class PDBReaderError(Exception): - pass - - -class PDBReader: - def __init__(self, filename): - """Constructor. - - Args: - filename (str): the input PDB file - """ - - self._chemicalEntities = [] - - self._struct = Structure(filename) - - self._build_chemical_entities() - - def _build_chemical_entities(self): - """Build the chemical entities stored in the PDB file.""" - - peptide_chains = [] - for pdb_element in self._struct.objects: - if isinstance(pdb_element, PDBPeptideChain): - peptide_chains.append(self._process_peptide_chain(pdb_element)) - else: - if peptide_chains: - p = Protein() - p.set_peptide_chains(peptide_chains) - self._chemicalEntities.append(p) - peptide_chains = [] - - if isinstance(pdb_element, PDBNucleotideChain): - self._chemicalEntities.append( - self._process_nucleotide_chain(pdb_element) - ) - - elif isinstance(pdb_element, PDBMolecule): - if len(pdb_element) == 1: - self._chemicalEntities.append( - self._process_atom(pdb_element[0]) - ) - else: - if is_molecule(pdb_element.name): - self._chemicalEntities.append( - self._process_molecule(pdb_element) - ) - else: - self._chemicalEntities.append( - self._process_atom_cluster(pdb_element) - ) - - if peptide_chains: - p = Protein() - p.set_peptide_chains(peptide_chains) - self._chemicalEntities.append(p) - peptide_chains = [] - - def _guess_atom_symbol(self, atom): - """Guess the atom symbol from an atom name. - - Args: - atom (str): thhe atom name - - Returns: - str: the atom symbol - """ - - atom_without_digits = "".join([at for at in atom if at not in string.digits]) - - comp = 1 - while True: - if atom_without_digits[:comp] in ATOMS_DATABASE: - return atom_without_digits[:comp] - comp += 1 - if comp > len(atom_without_digits): - raise PDBReaderError(atom) - - def _process_atom(self, atom): - """Process a PDB atom. - - Args: - atom (MDANSE.IO.PDB.PDBAtom): the PDB entry corresponding to the atom - - Returns: - MDANSE.Chemistry.ChemicalEntity.Atom: the processed atom - """ - - atname = atom.name.capitalize() - - atom_found = None - for at, info in ATOMS_DATABASE.items(): - if at == atname: - atom_found = Atom(name=at, **info) - else: - for alt in info.get("alternatives", []): - if alt == atname: - copy_info = copy.deepcopy(info) - atom_found = Atom(name=at, **copy_info) - atom_found.position = atom.position - break - else: - continue - - if atom_found is None: - raise PDBReaderError("The atom {} is unknown".format(atname)) - - return atom_found - - def _process_atom_cluster(self, atom_cluster): - """Process a PDB atom cluster. - - Args: - atom (MDANSE.IO.PDB.PDBAtomCluster): the PDB entry corresponding to the atom cluster - - Returns: - MDANSE.Chemistry.ChemicalEntity.AtomCluster: the processed atom cluster - """ - - ac_name = atom_cluster.name - - pdb_atoms = [at.name for at in atom_cluster] - - atoms = [] - for at in pdb_atoms: - symbol = self._guess_atom_symbol(at) - atoms.append(Atom(name=at, symbol=symbol)) - - new_atom_cluster = AtomCluster(ac_name, atoms) - - return new_atom_cluster - - def _process_molecule(self, molecule): - """Process a PDB molecule. - - Args: - atom (MDANSE.IO.PDB.PDBMolecule): the PDB entry corresponding to the molecule - - Returns: - MDANSE.Chemistry.ChemicalEntity.Molecule: the processed molecule - """ - - code = molecule.name - - pdb_atoms = [at.name for at in molecule] - - atoms_found = [None] * len(pdb_atoms) - for at, info in MOLECULES_DATABASE[code]["atoms"].items(): - try: - idx = pdb_atoms.index(at) - atoms_found[idx] = at - except ValueError: - for alt in info["alternatives"]: - try: - idx = pdb_atoms.index(alt) - atoms_found[idx] = at - except ValueError: - continue - else: - break - else: - raise PDBReaderError("Unknown atom") - - molname = "{}_{}".format(code, molecule.number) - new_molecule = Molecule(code, molname) - new_molecule.reorder_atoms(atoms_found) - - return new_molecule - - def _process_nter_residue(self, residue): - """Process a N terminus residue. - - Args: - residue (MDANSE.IO.PDB.PDBResidue): the residue - - Returns: - MDANSE.Chemistry.ChemicalEntity.Residue: the processed residue - """ - - code = residue.name - - pdb_atoms = [at.name for at in residue] - - atoms_found = [None] * len(pdb_atoms) - - atoms_not_found = [] - - for comp, pdb_atom in enumerate(pdb_atoms): - # Loop over the atoms of this residue stored in the corresponding residue database entry - for at, info in RESIDUES_DATABASE[code]["atoms"].items(): - # A match was found, pass to the next PDB atom - if pdb_atom == at or pdb_atom in info["alternatives"]: - atoms_found[comp] = at - break - # No match was found, the PDB atom is marked as not found - else: - atoms_not_found.append(pdb_atom) - - # Fetch all the N ter variants from the residues database - nter_variants = [ - (name, res) - for name, res in RESIDUES_DATABASE.items() - if res["is_n_terminus"] - ] - - # The atoms that has not been found so far will be searched now in the N ter variants entries of the database - if atoms_not_found: - # Loop over the N ter variants of the residues database - for name, res in nter_variants: - # Loop over the atom of the current variant - for at, info in res["atoms"].items(): - # These are all the names the atoms can take (actual DB name + alternatives) - allNames = [at] + info["alternatives"] - for aa in allNames: - # The variant atom match one of the not found atoms, mark it as known and pass to the next variant atom - if aa in atoms_not_found: - idx = pdb_atoms.index(aa) - atoms_found[idx] = at - break - # The variant atom was not found in the not found atoms, this variant can not be the right one. Skip it. - else: - break - # All the variant atoms has been found in the not found atoms, this variant is the right one - else: - nter = name - break - else: - raise PDBReaderError("The atom {} is unknown".format(at)) - - resname = "{}{}".format(code, residue.number) - new_residue = Residue(code, resname, variant=nter) - new_residue.set_atoms(atoms_found) - - return new_residue - - def _process_5ter_residue(self, residue): - """Process a 5 ter terminus nucleotide. - - Args: - residue (MDANSE.IO.PDB.PDBNucleotide): the nucleotide - - Returns: - MDANSE.Chemistry.ChemicalEntity.Nucleotide: the processed nucleotide - """ - - resname = residue.name - - pdb_atoms = [at.name for at in residue] - - atoms_found = [None] * len(pdb_atoms) - - atoms_not_found = [] - - for comp, pdb_atom in enumerate(pdb_atoms): - for at, info in NUCLEOTIDES_DATABASE[resname]["atoms"].items(): - if pdb_atom == at or pdb_atom in info["alternatives"]: - atoms_found[comp] = at - break - else: - atoms_not_found.append(pdb_atom) - - ter5_variants = [ - (name, res) - for name, res in NUCLEOTIDES_DATABASE.items() - if res["is_5ter_terminus"] - ] - - if atoms_not_found: - for name, res in ter5_variants: - for at, info in res["atoms"].items(): - allNames = [at] + info["alternatives"] - for aa in allNames: - if aa in atoms_not_found: - idx = pdb_atoms.index(aa) - atoms_found[idx] = at - break - else: - break - else: - nter = name - break - else: - raise PDBReaderError("The atom {} is unknown".format(at)) - - new_residue = Nucleotide(resname, residue.number, variant=nter) - new_residue.set_atoms(atoms_found) - - return new_residue - - def _process_cter_residue(self, residue): - """Process the C terminal residue. - - Args: - residue: MDANSE.IO.PDB.PDBResidue - - Returns: - MDANSE.Chemistry.ChemicalEntity.Residue: the C ter residue - """ - - code = residue.name - - pdb_atoms = [at.name for at in residue] - - atoms_found = [None] * len(pdb_atoms) - - # Loop over the PDB atoms of this residue - atoms_not_found = [] - for comp, pdb_atom in enumerate(pdb_atoms): - # Loop over the atoms of this residue stored in the corresponding residue database entry - for at, info in RESIDUES_DATABASE[code]["atoms"].items(): - # A match was found, pass to the next PDB atom - if pdb_atom == at or pdb_atom in info["alternatives"]: - atoms_found[comp] = at - break - # No match was found, the PDB atom is marked as not found - else: - atoms_not_found.append(pdb_atom) - - # Fetch all the C ter variants from the residues database - cter_variants = [ - (name, res) - for name, res in RESIDUES_DATABASE.items() - if res["is_c_terminus"] - ] - - # The atoms that has not been found so far will be searched now in the C ter variants entries of the database - cter = None - if atoms_not_found: - # Loop over the C ter variants of the residues database - for name, res in cter_variants: - # Loop over the atom of the current variant - for at, info in res["atoms"].items(): - # These are all the names the atoms can take (actual DB name + alternatives) - allNames = [at] + info["alternatives"] - for aa in allNames: - # The variant atom match one of the not found atoms, mark it as known and pass to the next variant atom - if aa in atoms_not_found: - idx = pdb_atoms.index(aa) - atoms_found[idx] = at - break - # The variant atom was not found in the not found atoms, this variant can not be the right one. Skip it. - else: - break - # All the variant atoms has been found in the not found atoms, this variant is the right one - else: - cter = name - break - else: - raise PDBReaderError("The atoms {} are unknown".format(atoms_not_found)) - - if cter is None: - raise PDBReaderError( - "Could not find a suitable CTER variant for residue {}{}".format( - residue.name, residue.number - ) - ) - - resname = "{}{}".format(code, residue.number) - new_residue = Residue(code, resname, variant=cter) - new_residue.set_atoms(atoms_found) - - return new_residue - - def _process_3ter_residue(self, residue): - """Process the 3 ter terminal nucleotide. - - Args: - residue: MDANSE.IO.PDB.PDBNucleotide - - Returns: - MDANSE.Chemistry.ChemicalEntity.Nucleotide: the 3 ter nucleotide - """ - - resname = residue.name - - pdb_atoms = [at.name for at in residue] - - atoms_found = [None] * len(pdb_atoms) - - atoms_not_found = [] - - for comp, pdb_atom in enumerate(pdb_atoms): - for at, info in NUCLEOTIDES_DATABASE[resname]["atoms"].items(): - if pdb_atom == at or pdb_atom in info["alternatives"]: - atoms_found[comp] = at - break - else: - atoms_not_found.append(pdb_atom) - - ter3_variants = [ - (name, res) - for name, res in NUCLEOTIDES_DATABASE.items() - if res["is_3ter_terminus"] - ] - - if atoms_not_found: - for name, res in ter3_variants: - for at, info in res["atoms"].items(): - allNames = [at] + info["alternatives"] - for aa in allNames: - if aa in atoms_not_found: - idx = pdb_atoms.index(aa) - atoms_found[idx] = at - break - else: - break - else: - cter = name - break - else: - raise PDBReaderError("The atom {} is unknown".format(at)) - - new_residue = Nucleotide(resname, residue.number, variant=cter) - new_residue.set_atoms(atoms_found) - - return new_residue - - def _process_residue(self, residue): - """Process a residue. - - Args: - residue (MDANSE.IO.PDB.PDBResidue): the PDB residue - - Returns: - MDANSE.Chemistry.ChemicalEntity.Residue: the processed residue - """ - - code = residue.name - - pdb_atoms = [at.name for at in residue] - - if code in RESIDUE_ALT_NAMES: - # RESIDUES_DATABASE is a many-to-one map multiple codes - # can map to one residue in MDANSE. - # If the code is in RESIDUE_ALT_NAMES then it is a - # many-to-many map. In this case the code can map to many - # different residues. - for new_code in RESIDUE_ALT_NAMES[code]: - # go through each name that code could be - atoms_found = [None] * len(pdb_atoms) - for comp, pdb_atom in enumerate(pdb_atoms): - if len(atoms_found) != len( - RESIDUES_DATABASE[new_code]["atoms"].items() - ): - # different number of atoms break and try the next name - break - for at, info in RESIDUES_DATABASE[new_code]["atoms"].items(): - if pdb_atom == at or pdb_atom in info["alternatives"]: - atoms_found[comp] = at - break - else: - # unable to match the atom in the RESIDUES_DATABASE - # try the next residue name - break - if None not in atoms_found: - # matched all atoms found the residue so break - break - else: - # went through all alternative names looks like we were - # unable to match the code to the MDANSE residues - raise PDBReaderError( - "Unable to find residue for {}{}".format(code, residue.number) - ) - # found the match from the alternate name to the name in - # mdanse lets rename the code - code = new_code - residue.name = code - - else: - atoms_found = [None] * len(pdb_atoms) - for comp, pdb_atom in enumerate(pdb_atoms): - for at, info in RESIDUES_DATABASE[code]["atoms"].items(): - if pdb_atom == at or pdb_atom in info["alternatives"]: - atoms_found[comp] = at - break - else: - raise PDBReaderError( - "The atom {}{}:{} is unknown".format( - code, residue.number, pdb_atom - ) - ) - - resname = "{}{}".format(code, residue.number) - new_residue = Residue(code, resname, variant=None) - new_residue.set_atoms(atoms_found) - - return new_residue - - def _process_nucleotide(self, residue): - """Process a nucleotide. - - Args: - residue (MDANSE.IO.PDB.PDBNucleotide): the PDB nucleotide - - Returns: - MDANSE.Chemistry.ChemicalEntity.Nucleotide: the processed nucleotide - """ - - resname = residue.name - - pdb_atoms = [at.name for at in residue] - - atoms_found = [None] * len(pdb_atoms) - - atoms_not_found = [] - - for comp, pdb_atom in enumerate(pdb_atoms): - for at, info in NUCLEOTIDES_DATABASE[resname]["atoms"].items(): - if pdb_atom == at or pdb_atom in info["alternatives"]: - atoms_found[comp] = at - break - else: - atoms_not_found.append(pdb_atom) - - if atoms_not_found: - raise PDBReaderError("The atoms {} are unknown".format(atoms_not_found)) - - new_residue = Nucleotide(resname, residue.number, variant=None) - new_residue.set_atoms(atoms_found) - - return new_residue - - def _process_nucleotide_chain(self, pdb_element): - """Process a PDB nucleotide chain. - - Args: - pdb_element (MDANSE.IO.PDB.PDBNucleotideChain): the PDB nucleotide chain to process - - Returns: - MDANSE.Chemistry.ChemicalEntity.NucleotideChain: the process nucleotide chain - """ - - nucleotide_chain = NucleotideChain(pdb_element.chain_id) - residues = [] - # Loop over the residues of the nucleotide chain - for comp, residue in enumerate(pdb_element.residues): - # Case the 5 ter residue - if comp == 0: - res = self._process_5ter_residue(residue) - # Case the 3 ter residue - elif comp == len(pdb_element.residues) - 1: - res = self._process_3ter_residue(residue) - # Normal residue - else: - res = self._process_nucleotide(residue) - residues.append(res) - nucleotide_chain.set_nucleotides(residues) - - return nucleotide_chain - - def _process_peptide_chain(self, pdb_element): - """Process a PDB peptide chain. - - Args: - pdb_element (MDANSE.IO.PDB.PeptideChain): the PDB peptide chain to process - - Returns: - MDANSE.Chemistry.ChemicalEntity.PeptideChain: the process peptide chain - """ - - peptide_chain = PeptideChain(pdb_element.chain_id) - residues = [] - for comp, residue in enumerate(pdb_element.residues): - if comp == 0: - res = self._process_nter_residue(residue) - elif comp == len(pdb_element.residues) - 1: - res = self._process_cter_residue(residue) - else: - res = self._process_residue(residue) - residues.append(res) - peptide_chain.set_residues(residues) - - return peptide_chain - - def build_chemical_system(self): - """Build the chemical system. - - Returns: - MDANSE.Chemistry.ChemicalEntity.ChemicalSystem: the chemical system - """ - - chemical_system = ChemicalSystem() - - for ce in self._chemicalEntities: - chemical_system.add_chemical_entity(ce) - - coordinates = [] - for obj in self._struct: - for atom in obj.atom_list: - coordinates.append(atom.position) - coordinates = np.array(coordinates) - coordinates *= 0.1 - - chemical_system.configuration = RealConfiguration(chemical_system, coordinates) - - return chemical_system - - -if __name__ == "__main__": - print("Reading") - pdb_reader = PDBReader("/home/pellegrini/apoferritin.pdb") - print("Building chemical system") - cs = pdb_reader.build_chemical_system() - - conf = RealConfiguration( - cs, - np.empty((cs.number_of_atoms(), 3), dtype=np.float64), - np.empty((3, 3), dtype=np.float64), - **{"velocities": np.empty((cs.number_of_atoms(), 3), dtype=np.float64)}, - ) - cs.configuration = conf - - cs1 = cs.copy() - - print(cs1.configuration["velocities"]) - - # print('Serializing') - # cs.serialize('test.h5') - # print('Loading') - # cs.load('test.h5') - # print(cs.chemical_entities) diff --git a/MDANSE/Src/MDANSE/IO/PDBSpaceGroups.py b/MDANSE/Src/MDANSE/IO/PDBSpaceGroups.py deleted file mode 100644 index cb88673056..0000000000 --- a/MDANSE/Src/MDANSE/IO/PDBSpaceGroups.py +++ /dev/null @@ -1,4380 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import numpy as np - -from MDANSE.Mathematics.LinearAlgebra import Vector, Tensor -from MDANSE.Mathematics.Transformation import Rotation, Translation - - -class SpaceGroup(object): - def __init__(self, number, labels, transformations): - self.number = number - self.labels = labels - self.transformations = [] - for rot, trans in transformations: - self.transformations.append(Translation(trans) * Rotation(Tensor(rot))) - - -_space_group_table = {} - - -def getSpaceGroupTransformations(space_group_label_or_number): - try: - return _space_group_table[space_group_label_or_number].transformations - except KeyError: - pass - space_group_label = "".join(space_group_label_or_number.split()) - return _space_group_table[space_group_label].transformations - - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(1, ["C1^1", "1", "P 1", "P1"], transformations) -_space_group_table[1] = sg -_space_group_table["C1^1"] = sg -_space_group_table["1"] = sg -_space_group_table["P 1"] = sg -_space_group_table["P1"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 3, - [ - "C2^1", - "P 2y", - "P121", - "P2:b", - "3:b", - "C2^1", - "P 2", - "P112", - "P2:c", - "3:c", - "C2^1", - "P 2x", - "P211", - "P2:a", - "3:a", - ], - transformations, -) -_space_group_table[3] = sg -_space_group_table["C2^1"] = sg -_space_group_table["P 2y"] = sg -_space_group_table["P121"] = sg -_space_group_table["P2:b"] = sg -_space_group_table["3:b"] = sg -_space_group_table["C2^1"] = sg -_space_group_table["P 2"] = sg -_space_group_table["P112"] = sg -_space_group_table["P2:c"] = sg -_space_group_table["3:c"] = sg -_space_group_table["C2^1"] = sg -_space_group_table["P 2x"] = sg -_space_group_table["P211"] = sg -_space_group_table["P2:a"] = sg -_space_group_table["3:a"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 4, - [ - "C2^2", - "P 2yb", - "P1211", - "P21:b", - "4:b", - "C2^2", - "P 2c", - "P1121", - "P21:c", - "4:c", - "C2^2", - "P 2xa", - "P2111", - "P21:a", - "4:a", - ], - transformations, -) -_space_group_table[4] = sg -_space_group_table["C2^2"] = sg -_space_group_table["P 2yb"] = sg -_space_group_table["P1211"] = sg -_space_group_table["P21:b"] = sg -_space_group_table["4:b"] = sg -_space_group_table["C2^2"] = sg -_space_group_table["P 2c"] = sg -_space_group_table["P1121"] = sg -_space_group_table["P21:c"] = sg -_space_group_table["4:c"] = sg -_space_group_table["C2^2"] = sg -_space_group_table["P 2xa"] = sg -_space_group_table["P2111"] = sg -_space_group_table["P21:a"] = sg -_space_group_table["4:a"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 5, - [ - "C2^3", - "C 2y", - "C121", - "C2:b1", - "5:b1", - "C2^3", - "A 2y", - "A121", - "C2:b2", - "5:b2", - "C2^3", - "I 2y", - "I121", - "C2:b3", - "5:b3", - "C2^3", - "A 2", - "A112", - "C2:c1", - "5:c1", - "C2^3", - "B 2", - "B2", - "B112", - "C2:c2", - "5:c2", - "C2^3", - "I 2", - "I112", - "C2:c3", - "5:c3", - "C2^3", - "B 2x", - "B211", - "C2:a1", - "5:a1", - "C2^3", - "C 2x", - "C211", - "C2:a2", - "5:a2", - "C2^3", - "I 2x", - "I211", - "C2:a3", - "5:a3", - ], - transformations, -) -_space_group_table[5] = sg -_space_group_table["C2^3"] = sg -_space_group_table["C 2y"] = sg -_space_group_table["C121"] = sg -_space_group_table["C2:b1"] = sg -_space_group_table["5:b1"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["A 2y"] = sg -_space_group_table["A121"] = sg -_space_group_table["C2:b2"] = sg -_space_group_table["5:b2"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["I 2y"] = sg -_space_group_table["I121"] = sg -_space_group_table["C2:b3"] = sg -_space_group_table["5:b3"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["A 2"] = sg -_space_group_table["A112"] = sg -_space_group_table["C2:c1"] = sg -_space_group_table["5:c1"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["B 2"] = sg -_space_group_table["B2"] = sg -_space_group_table["B112"] = sg -_space_group_table["C2:c2"] = sg -_space_group_table["5:c2"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["I 2"] = sg -_space_group_table["I112"] = sg -_space_group_table["C2:c3"] = sg -_space_group_table["5:c3"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["B 2x"] = sg -_space_group_table["B211"] = sg -_space_group_table["C2:a1"] = sg -_space_group_table["5:a1"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["C 2x"] = sg -_space_group_table["C211"] = sg -_space_group_table["C2:a2"] = sg -_space_group_table["5:a2"] = sg -_space_group_table["C2^3"] = sg -_space_group_table["I 2x"] = sg -_space_group_table["I211"] = sg -_space_group_table["C2:a3"] = sg -_space_group_table["5:a3"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(16, ["D2^1", "16", "P 2 2", "P222"], transformations) -_space_group_table[16] = sg -_space_group_table["D2^1"] = sg -_space_group_table["16"] = sg -_space_group_table["P 2 2"] = sg -_space_group_table["P222"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 17, - [ - "D2^2", - "17", - "P 2c 2", - "P2221", - "D2^2", - "P 2a 2a", - "P2122", - "17:cab", - "D2^2", - "P 2 2b", - "P2212", - "17:bca", - ], - transformations, -) -_space_group_table[17] = sg -_space_group_table["D2^2"] = sg -_space_group_table["17"] = sg -_space_group_table["P 2c 2"] = sg -_space_group_table["P2221"] = sg -_space_group_table["D2^2"] = sg -_space_group_table["P 2a 2a"] = sg -_space_group_table["P2122"] = sg -_space_group_table["17:cab"] = sg -_space_group_table["D2^2"] = sg -_space_group_table["P 2 2b"] = sg -_space_group_table["P2212"] = sg -_space_group_table["17:bca"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 18, - [ - "D2^3", - "18", - "P 2 2ab", - "P21212", - "D2^3", - "P 2bc 2", - "P22121", - "18:cab", - "D2^3", - "P 2ac 2ac", - "P21221", - "18:bca", - ], - transformations, -) -_space_group_table[18] = sg -_space_group_table["D2^3"] = sg -_space_group_table["18"] = sg -_space_group_table["P 2 2ab"] = sg -_space_group_table["P21212"] = sg -_space_group_table["D2^3"] = sg -_space_group_table["P 2bc 2"] = sg -_space_group_table["P22121"] = sg -_space_group_table["18:cab"] = sg -_space_group_table["D2^3"] = sg -_space_group_table["P 2ac 2ac"] = sg -_space_group_table["P21221"] = sg -_space_group_table["18:bca"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(19, ["D2^4", "19", "P 2ac 2ab", "P212121"], transformations) -_space_group_table[19] = sg -_space_group_table["D2^4"] = sg -_space_group_table["19"] = sg -_space_group_table["P 2ac 2ab"] = sg -_space_group_table["P212121"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 20, - [ - "D2^5", - "20", - "C 2c 2", - "C2221", - "D2^5", - "A 2a 2a", - "A2122", - "20:cab", - "D2^5", - "B 2 2b", - "B2212", - "20:bca", - ], - transformations, -) -_space_group_table[20] = sg -_space_group_table["D2^5"] = sg -_space_group_table["20"] = sg -_space_group_table["C 2c 2"] = sg -_space_group_table["C2221"] = sg -_space_group_table["D2^5"] = sg -_space_group_table["A 2a 2a"] = sg -_space_group_table["A2122"] = sg -_space_group_table["20:cab"] = sg -_space_group_table["D2^5"] = sg -_space_group_table["B 2 2b"] = sg -_space_group_table["B2212"] = sg -_space_group_table["20:bca"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 21, - [ - "D2^6", - "21", - "C 2 2", - "C222", - "D2^6", - "A 2 2", - "A222", - "21:cab", - "D2^6", - "B 2 2", - "B222", - "21:bca", - ], - transformations, -) -_space_group_table[21] = sg -_space_group_table["D2^6"] = sg -_space_group_table["21"] = sg -_space_group_table["C 2 2"] = sg -_space_group_table["C222"] = sg -_space_group_table["D2^6"] = sg -_space_group_table["A 2 2"] = sg -_space_group_table["A222"] = sg -_space_group_table["21:cab"] = sg -_space_group_table["D2^6"] = sg -_space_group_table["B 2 2"] = sg -_space_group_table["B222"] = sg -_space_group_table["21:bca"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(22, ["D2^7", "22", "F 2 2", "F222"], transformations) -_space_group_table[22] = sg -_space_group_table["D2^7"] = sg -_space_group_table["22"] = sg -_space_group_table["F 2 2"] = sg -_space_group_table["F222"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(23, ["D2^8", "23", "I 2 2", "I222"], transformations) -_space_group_table[23] = sg -_space_group_table["D2^8"] = sg -_space_group_table["23"] = sg -_space_group_table["I 2 2"] = sg -_space_group_table["I222"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(24, ["D2^9", "24", "I 2b 2c", "I212121"], transformations) -_space_group_table[24] = sg -_space_group_table["D2^9"] = sg -_space_group_table["24"] = sg -_space_group_table["I 2b 2c"] = sg -_space_group_table["I212121"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(75, ["C4^1", "75", "P 4", "P4"], transformations) -_space_group_table[75] = sg -_space_group_table["C4^1"] = sg -_space_group_table["75"] = sg -_space_group_table["P 4"] = sg -_space_group_table["P4"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(76, ["C4^2", "76", "P 4w", "P41"], transformations) -_space_group_table[76] = sg -_space_group_table["C4^2"] = sg -_space_group_table["76"] = sg -_space_group_table["P 4w"] = sg -_space_group_table["P41"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(77, ["C4^3", "77", "P 4c", "P42"], transformations) -_space_group_table[77] = sg -_space_group_table["C4^3"] = sg -_space_group_table["77"] = sg -_space_group_table["P 4c"] = sg -_space_group_table["P42"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(78, ["C4^4", "78", "P 4cw", "P43"], transformations) -_space_group_table[78] = sg -_space_group_table["C4^4"] = sg -_space_group_table["78"] = sg -_space_group_table["P 4cw"] = sg -_space_group_table["P43"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(79, ["C4^5", "79", "I 4", "I4"], transformations) -_space_group_table[79] = sg -_space_group_table["C4^5"] = sg -_space_group_table["79"] = sg -_space_group_table["I 4"] = sg -_space_group_table["I4"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(80, ["C4^6", "80", "I 4bw", "I41"], transformations) -_space_group_table[80] = sg -_space_group_table["C4^6"] = sg -_space_group_table["80"] = sg -_space_group_table["I 4bw"] = sg -_space_group_table["I41"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(89, ["D4^1", "89", "P 4 2", "P422"], transformations) -_space_group_table[89] = sg -_space_group_table["D4^1"] = sg -_space_group_table["89"] = sg -_space_group_table["P 4 2"] = sg -_space_group_table["P422"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(90, ["D4^2", "90", "P 4ab 2ab", "P4212"], transformations) -_space_group_table[90] = sg -_space_group_table["D4^2"] = sg -_space_group_table["90"] = sg -_space_group_table["P 4ab 2ab"] = sg -_space_group_table["P4212"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 4.0) -transformations.append((rot, trans)) -sg = SpaceGroup(91, ["D4^3", "91", "P 4w 2c", "P4122"], transformations) -_space_group_table[91] = sg -_space_group_table["D4^3"] = sg -_space_group_table["91"] = sg -_space_group_table["P 4w 2c"] = sg -_space_group_table["P4122"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(92, ["D4^4", "92", "P 4abw 2nw", "P41212"], transformations) -_space_group_table[92] = sg -_space_group_table["D4^4"] = sg -_space_group_table["92"] = sg -_space_group_table["P 4abw 2nw"] = sg -_space_group_table["P41212"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(93, ["D4^5", "93", "P 4c 2", "P4222"], transformations) -_space_group_table[93] = sg -_space_group_table["D4^5"] = sg -_space_group_table["93"] = sg -_space_group_table["P 4c 2"] = sg -_space_group_table["P4222"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(94, ["D4^6", "94", "P 4n 2n", "P42212"], transformations) -_space_group_table[94] = sg -_space_group_table["D4^6"] = sg -_space_group_table["94"] = sg -_space_group_table["P 4n 2n"] = sg -_space_group_table["P42212"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -sg = SpaceGroup(95, ["D4^7", "95", "P 4cw 2c", "P4322"], transformations) -_space_group_table[95] = sg -_space_group_table["D4^7"] = sg -_space_group_table["95"] = sg -_space_group_table["P 4cw 2c"] = sg -_space_group_table["P4322"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(96, ["D4^8", "96", "P 4nw 2abw", "P43212"], transformations) -_space_group_table[96] = sg -_space_group_table["D4^8"] = sg -_space_group_table["96"] = sg -_space_group_table["P 4nw 2abw"] = sg -_space_group_table["P43212"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(97, ["D4^9", "97", "I 4 2", "I422"], transformations) -_space_group_table[97] = sg -_space_group_table["D4^9"] = sg -_space_group_table["97"] = sg -_space_group_table["I 4 2"] = sg -_space_group_table["I422"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(98, ["D4^10", "98", "I 4bw 2bw", "I4122"], transformations) -_space_group_table[98] = sg -_space_group_table["D4^10"] = sg -_space_group_table["98"] = sg -_space_group_table["I 4bw 2bw"] = sg -_space_group_table["I4122"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(143, ["C3^1", "143", "P 3", "P3"], transformations) -_space_group_table[143] = sg -_space_group_table["C3^1"] = sg -_space_group_table["143"] = sg -_space_group_table["P 3"] = sg -_space_group_table["P3"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -sg = SpaceGroup(144, ["C3^2", "144", "P 31", "P31"], transformations) -_space_group_table[144] = sg -_space_group_table["C3^2"] = sg -_space_group_table["144"] = sg -_space_group_table["P 31"] = sg -_space_group_table["P31"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -sg = SpaceGroup(145, ["C3^3", "145", "P 32", "P32"], transformations) -_space_group_table[145] = sg -_space_group_table["C3^3"] = sg -_space_group_table["145"] = sg -_space_group_table["P 32"] = sg -_space_group_table["P32"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 146, - ["C3^4", "R 3", "H 3", "R3:H", "146:H", "C3^4", "P 3*", "R3:R", "146:R"], - transformations, -) -_space_group_table[146] = sg -_space_group_table["C3^4"] = sg -_space_group_table["R 3"] = sg -_space_group_table["H 3"] = sg -_space_group_table["R3:H"] = sg -_space_group_table["146:H"] = sg -_space_group_table["C3^4"] = sg -_space_group_table["P 3*"] = sg -_space_group_table["R3:R"] = sg -_space_group_table["146:R"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(149, ["D3^1", "149", "P 3 2", "P312"], transformations) -_space_group_table[149] = sg -_space_group_table["D3^1"] = sg -_space_group_table["149"] = sg -_space_group_table["P 3 2"] = sg -_space_group_table["P312"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(150, ["D3^2", "150", 'P 3 2"', "P321"], transformations) -_space_group_table[150] = sg -_space_group_table["D3^2"] = sg -_space_group_table["150"] = sg -_space_group_table['P 3 2"'] = sg -_space_group_table["P321"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(151, ["D3^3", "151", "P 31 2c (0 0 1)", "P3112"], transformations) -_space_group_table[151] = sg -_space_group_table["D3^3"] = sg -_space_group_table["151"] = sg -_space_group_table["P 31 2c (0 0 1)"] = sg -_space_group_table["P3112"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(152, ["D3^4", "152", 'P 31 2"', "P3121"], transformations) -_space_group_table[152] = sg -_space_group_table["D3^4"] = sg -_space_group_table["152"] = sg -_space_group_table['P 31 2"'] = sg -_space_group_table["P3121"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(153, ["D3^5", "153", "P 32 2c (0 0 -1)", "P3212"], transformations) -_space_group_table[153] = sg -_space_group_table["D3^5"] = sg -_space_group_table["153"] = sg -_space_group_table["P 32 2c (0 0 -1)"] = sg -_space_group_table["P3212"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(154, ["D3^6", "154", 'P 32 2"', "P3221"], transformations) -_space_group_table[154] = sg -_space_group_table["D3^6"] = sg -_space_group_table["154"] = sg -_space_group_table['P 32 2"'] = sg -_space_group_table["P3221"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0) -transformations.append((rot, trans)) -sg = SpaceGroup( - 155, - ["D3^7", "R 3 2", "H 3 2", "R32:H", "155:H", "D3^7", "P 3* 2", "R32:R", "155:R"], - transformations, -) -_space_group_table[155] = sg -_space_group_table["D3^7"] = sg -_space_group_table["R 3 2"] = sg -_space_group_table["H 3 2"] = sg -_space_group_table["R32:H"] = sg -_space_group_table["155:H"] = sg -_space_group_table["D3^7"] = sg -_space_group_table["P 3* 2"] = sg -_space_group_table["R32:R"] = sg -_space_group_table["155:R"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(168, ["C6^1", "168", "P 6", "P6"], transformations) -_space_group_table[168] = sg -_space_group_table["C6^1"] = sg -_space_group_table["168"] = sg -_space_group_table["P 6"] = sg -_space_group_table["P6"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 5.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(169, ["C6^2", "169", "P 61", "P61"], transformations) -_space_group_table[169] = sg -_space_group_table["C6^2"] = sg -_space_group_table["169"] = sg -_space_group_table["P 61"] = sg -_space_group_table["P61"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 5.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(170, ["C6^3", "170", "P 65", "P65"], transformations) -_space_group_table[170] = sg -_space_group_table["C6^3"] = sg -_space_group_table["170"] = sg -_space_group_table["P 65"] = sg -_space_group_table["P65"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(171, ["C6^4", "171", "P 62", "P62"], transformations) -_space_group_table[171] = sg -_space_group_table["C6^4"] = sg -_space_group_table["171"] = sg -_space_group_table["P 62"] = sg -_space_group_table["P62"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(172, ["C6^5", "172", "P 64", "P64"], transformations) -_space_group_table[172] = sg -_space_group_table["C6^5"] = sg -_space_group_table["172"] = sg -_space_group_table["P 64"] = sg -_space_group_table["P64"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(173, ["C6^6", "173", "P 6c", "P63"], transformations) -_space_group_table[173] = sg -_space_group_table["C6^6"] = sg -_space_group_table["173"] = sg -_space_group_table["P 6c"] = sg -_space_group_table["P63"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(177, ["D6^1", "177", "P 6 2", "P622"], transformations) -_space_group_table[177] = sg -_space_group_table["D6^1"] = sg -_space_group_table["177"] = sg -_space_group_table["P 6 2"] = sg -_space_group_table["P622"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 5.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 5.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 6.0) -transformations.append((rot, trans)) -sg = SpaceGroup(178, ["D6^2", "178", "P 61 2 (0 0 -1)", "P6122"], transformations) -_space_group_table[178] = sg -_space_group_table["D6^2"] = sg -_space_group_table["178"] = sg -_space_group_table["P 61 2 (0 0 -1)"] = sg -_space_group_table["P6122"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 5.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 6.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 5.0 / 6.0) -transformations.append((rot, trans)) -sg = SpaceGroup(179, ["D6^3", "179", "P 65 2 (0 0 1)", "P6522"], transformations) -_space_group_table[179] = sg -_space_group_table["D6^3"] = sg -_space_group_table["179"] = sg -_space_group_table["P 65 2 (0 0 1)"] = sg -_space_group_table["P6522"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -sg = SpaceGroup(180, ["D6^4", "180", "P 62 2c (0 0 1)", "P6222"], transformations) -_space_group_table[180] = sg -_space_group_table["D6^4"] = sg -_space_group_table["180"] = sg -_space_group_table["P 62 2c (0 0 1)"] = sg -_space_group_table["P6222"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 3.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 2.0 / 3.0) -transformations.append((rot, trans)) -sg = SpaceGroup(181, ["D6^5", "181", "P 64 2c (0 0 01)", "P6422"], transformations) -_space_group_table[181] = sg -_space_group_table["D6^5"] = sg -_space_group_table["181"] = sg -_space_group_table["P 64 2c (0 0 01)"] = sg -_space_group_table["P6422"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, -1, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, -1, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 1, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 1, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(182, ["D6^6", "182", "P 6c 2c", "P6322"], transformations) -_space_group_table[182] = sg -_space_group_table["D6^6"] = sg -_space_group_table["182"] = sg -_space_group_table["P 6c 2c"] = sg -_space_group_table["P6322"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(195, ["T^1", "195", "P 2 2 3", "P23"], transformations) -_space_group_table[195] = sg -_space_group_table["T^1"] = sg -_space_group_table["195"] = sg -_space_group_table["P 2 2 3"] = sg -_space_group_table["P23"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(196, ["T^2", "196", "F 2 2 3", "F23"], transformations) -_space_group_table[196] = sg -_space_group_table["T^2"] = sg -_space_group_table["196"] = sg -_space_group_table["F 2 2 3"] = sg -_space_group_table["F23"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(197, ["T^3", "197", "I 2 2 3", "I23"], transformations) -_space_group_table[197] = sg -_space_group_table["T^3"] = sg -_space_group_table["197"] = sg -_space_group_table["I 2 2 3"] = sg -_space_group_table["I23"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(198, ["T^4", "198", "P 2ac 2ab 3", "P213"], transformations) -_space_group_table[198] = sg -_space_group_table["T^4"] = sg -_space_group_table["198"] = sg -_space_group_table["P 2ac 2ab 3"] = sg -_space_group_table["P213"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(199, ["T^5", "199", "I 2b 2c 3", "I213"], transformations) -_space_group_table[199] = sg -_space_group_table["T^5"] = sg -_space_group_table["199"] = sg -_space_group_table["I 2b 2c 3"] = sg -_space_group_table["I213"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(207, ["O^1", "207", "P 4 2 3", "P432"], transformations) -_space_group_table[207] = sg -_space_group_table["O^1"] = sg -_space_group_table["207"] = sg -_space_group_table["P 4 2 3"] = sg -_space_group_table["P432"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(208, ["O^2", "208", "P 4n 2 3", "P4232"], transformations) -_space_group_table[208] = sg -_space_group_table["O^2"] = sg -_space_group_table["208"] = sg -_space_group_table["P 4n 2 3"] = sg -_space_group_table["P4232"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -sg = SpaceGroup(209, ["O^3", "209", "F 4 2 3", "F432"], transformations) -_space_group_table[209] = sg -_space_group_table["O^3"] = sg -_space_group_table["209"] = sg -_space_group_table["F 4 2 3"] = sg -_space_group_table["F432"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -sg = SpaceGroup(210, ["O^4", "210", "F 4d 2 3", "F4132"], transformations) -_space_group_table[210] = sg -_space_group_table["O^4"] = sg -_space_group_table["210"] = sg -_space_group_table["F 4d 2 3"] = sg -_space_group_table["F4132"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -sg = SpaceGroup(211, ["O^5", "211", "I 4 2 3", "I432"], transformations) -_space_group_table[211] = sg -_space_group_table["O^5"] = sg -_space_group_table["211"] = sg -_space_group_table["I 4 2 3"] = sg -_space_group_table["I432"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -sg = SpaceGroup(212, ["O^6", "212", "P 4acd 2ab 3", "P4332"], transformations) -_space_group_table[212] = sg -_space_group_table["O^6"] = sg -_space_group_table["212"] = sg -_space_group_table["P 4acd 2ab 3"] = sg -_space_group_table["P4332"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -sg = SpaceGroup(213, ["O^7", "213", "P 4bd 2ab 3", "P4132"], transformations) -_space_group_table[213] = sg -_space_group_table["O^7"] = sg -_space_group_table["213"] = sg -_space_group_table["P 4bd 2ab 3"] = sg -_space_group_table["P4132"] = sg - -transformations = [] -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(0.0, 0.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 0.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(0.0, 1.0 / 2.0, 0.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 3.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 4.0, 1.0 / 4.0, 1.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, -1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, 0, 1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 5.0 / 4.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, 1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 5.0 / 4.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, 1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 5.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 5.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, -1, 0, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, 1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, -1, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, -1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, 0, 0, 1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, -1, 0, 0, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 1, 0, 0, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 0, 0, -1, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0) -transformations.append((rot, trans)) -rot = np.array([1, 0, 0, 0, -1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0 / 2.0, 1.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 1, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(1.0, 1.0 / 2.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, -1, 0, 0, 0, 1]) -rot.shape = (3, 3) -trans = Vector(1.0 / 2.0, 1.0, 1.0 / 2.0) -transformations.append((rot, trans)) -rot = np.array([0, 1, 0, 1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 5.0 / 4.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, -1, 0, -1, 0, 0, 0, 0, -1]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, 1, 0, -1, 0, 1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 5.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([0, 0, -1, 0, -1, 0, -1, 0, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, 1, 0, 1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 5.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -rot = np.array([-1, 0, 0, 0, 0, -1, 0, -1, 0]) -rot.shape = (3, 3) -trans = Vector(3.0 / 4.0, 3.0 / 4.0, 3.0 / 4.0) -transformations.append((rot, trans)) -sg = SpaceGroup(214, ["O^8", "214", "I 4bd 2c 3", "I4132"], transformations) -_space_group_table[214] = sg -_space_group_table["O^8"] = sg -_space_group_table["214"] = sg -_space_group_table["I 4bd 2c 3"] = sg -_space_group_table["I4132"] = sg - - -del transformations -del rot -del trans diff --git a/MDANSE/Src/MDANSE/Mathematics/Arithmetic.py b/MDANSE/Src/MDANSE/Mathematics/Arithmetic.py index fa08a203e6..a43bdd511b 100644 --- a/MDANSE/Src/MDANSE/Mathematics/Arithmetic.py +++ b/MDANSE/Src/MDANSE/Mathematics/Arithmetic.py @@ -37,7 +37,12 @@ def get_weights(props, contents, dim): else: normFactor += fact - if abs(normFactor) > 0.0: # if normFactor is 0, all weights are 0 too. + normalise = True + try: + len(normFactor) + except TypeError: + normalise = abs(normFactor) > 0.0 # if normFactor is 0, all weights are 0 too. + if normalise: for k in list(weights.keys()): weights[k] /= np.float64(normFactor) diff --git a/MDANSE/Src/MDANSE/Mathematics/Geometry.py b/MDANSE/Src/MDANSE/Mathematics/Geometry.py index 60639fc9b4..f21051b783 100644 --- a/MDANSE/Src/MDANSE/Mathematics/Geometry.py +++ b/MDANSE/Src/MDANSE/Mathematics/Geometry.py @@ -88,22 +88,24 @@ def build_cartesian_axes(origin, p1, p2, dtype=np.float64): return n1, n2, n3 -def generate_sphere_points(n): +def generate_sphere_points(n: int) -> np.ndarray: """Returns list of 3d coordinates of points on a sphere using the Golden Section Spiral algorithm. """ - points = [] + inputs = np.arange(int(n)) + points = np.empty([len(inputs), 3]) inc = np.pi * (3 - np.sqrt(5)) offset = 2 / float(n) - for k in range(int(n)): - y = k * offset - 1 + (offset / 2) - r = np.sqrt(1 - y * y) - phi = k * inc - points.append([np.cos(phi) * r, y, np.sin(phi) * r]) + y = inputs * offset - 1 + (offset / 2) + r = np.sqrt(1 - y * y) + phi = inputs * inc + points[:, 0] = np.cos(phi) * r + points[:, 1] = y + points[:, 2] = np.sin(phi) * r return points diff --git a/MDANSE/Src/MDANSE/Mathematics/Graph.py b/MDANSE/Src/MDANSE/Mathematics/Graph.py deleted file mode 100644 index 3f69613584..0000000000 --- a/MDANSE/Src/MDANSE/Mathematics/Graph.py +++ /dev/null @@ -1,104 +0,0 @@ -# This file is part of MDANSE. -# -# MDANSE is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import collections - - -class Node(object): - def __init__(self, name, **kwargs): - self._name = name - self._links = set() - for k, v in list(kwargs.items()): - setattr(self, k, v) - - @property - def name(self): - return self._name - - @property - def links(self): - return self._links - - def add_link(self, other): - self._links.add(other) - other._links.add(self) - - -class Graph(object): - def __init__(self): - self._nodes = collections.OrderedDict() - - @property - def nodes(self): - return self._nodes - - def add_node(self, name, **kwargs): - self._nodes[name] = Node(name, **kwargs) - - def add_link(self, source, target): - self._nodes[source].add_link(self._nodes[target]) - - def build_connected_components(self): - # List of connected components found. The order is random. - result = [] - - # Make a copy of the set, so we can modify it. - nodes = [self._nodes[k] for k in sorted(self._nodes.keys())] - nodes.reverse() - - # Iterate while we still have nodes to process. - while nodes: - # Get a random node and remove it from the global set. - n = nodes.pop() - - # This set will contain the next group of nodes connected to each other. - group = set([n]) - - # Build a queue with this node in it. - queue = [n] - - # Iterate the queue. - # When it's empty, we finished visiting a group of connected nodes. - while queue: - # Consume the next item from the queue. - n = queue.pop(0) - - # Fetch the neighbors. - neighbors = n.links - - # Remove the neighbors we already visited. - neighbors.difference_update(group) - - # Remove the remaining nodes from the global set. - for neigh in neighbors: - if neigh in nodes: - nodes.remove(neigh) - - # Add them to the group of connected nodes. - group.update(neighbors) - - # Add them to the queue, so we visit them in the next iterations. - queue.extend(neighbors) - - # Sort the group - group = list(group) - group.sort(key=lambda n: n.name) - - # Add the group to the list of groups. - result.append(group) - - # Return the list of groups. - return result diff --git a/MDANSE/Src/MDANSE/MolecularDynamics/Configuration.py b/MDANSE/Src/MDANSE/MolecularDynamics/Configuration.py index 99aea88a39..f34c2f21f0 100644 --- a/MDANSE/Src/MDANSE/MolecularDynamics/Configuration.py +++ b/MDANSE/Src/MDANSE/MolecularDynamics/Configuration.py @@ -16,16 +16,264 @@ from __future__ import annotations import abc import copy -from typing import Union, TYPE_CHECKING +from typing import Union, TYPE_CHECKING, List, Tuple +from functools import reduce import numpy as np +import networkx as nx from numpy.typing import ArrayLike if TYPE_CHECKING: - from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem, Atom, _ChemicalEntity + from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Mathematics.Transformation import RigidBodyTransformation from MDANSE.MolecularDynamics.UnitCell import UnitCell -from MDANSE.Extensions import atoms_in_shell, contiguous_coordinates + + +def remove_jumps(input_coords: np.ndarray) -> np.ndarray: + """Takes a series of particle positions in time + and makes the motion continuous by removing any jumps across + the simulation box boundary. + + Parameters + ---------- + input_coords : np.ndarray + An (n_time_steps, 3) array of FRACTIONAL atom coordinates + + Returns + ------- + np.ndarray + The same array of atom positions, corrected for jumps by 1 + full box length + """ + steps = np.round(input_coords[1:] - input_coords[:-1]).astype(int) + offsets = np.zeros_like(input_coords) + for axis_index in range(3): + changes = np.argwhere(steps[:, axis_index]) + for time_step_with_jump in changes: + try: + time_index = time_step_with_jump[0] + except IndexError: + continue + offsets[time_index + 1 :, axis_index] -= steps[time_index, axis_index] + return input_coords + offsets + + +def contiguous_coordinates_real( + coords: np.ndarray, + cell: np.ndarray, + rcell: np.ndarray, + indices: List[Tuple[int]], + bring_to_centre: bool = False, +): + """Translates atoms by a lattice vector. Returns a coordinate array + in which atoms in each segment are separated from the first atom + by less than half the simulation box length. + + Parameters + ---------- + coords : np.ndarray + Array of atom coordinates + cell : np.ndarray + 3x3 unit cell array + rcell : np.ndarray + 3x3 reciprocal cell array + indices : List[Tuple[int]] + a list of index group, as in [[1,2,3], [7,8]] + (this would ensure 2 and 3 are close to 1, and 8 is close to 7) + bring_to_centre: bool + if true, atoms are shifted to minimise the distance from the average + position and not from the first atom + + Returns + ------- + np.ndarray + new coordinate array with the translations applied + """ + + contiguous_coords = coords.copy() + + scaleconfig = np.matmul(coords, rcell) + + for idxs in indices: + + if len(idxs) < 2: + continue + if bring_to_centre: + centre = np.mean(scaleconfig[idxs], axis=0) + minimum_offsets = scaleconfig[idxs] - centre + minimum_offsets -= np.round(minimum_offsets) + newconfig = centre + minimum_offsets + newconfig = np.matmul(newconfig, cell) + contiguous_coords[idxs] = newconfig + else: + minimum_offsets = scaleconfig[idxs[1:]] - scaleconfig[idxs[0]] + minimum_offsets -= np.round(minimum_offsets) + newconfig = scaleconfig[idxs[0]] + minimum_offsets + newconfig = np.matmul(newconfig, cell) + contiguous_coords[idxs[1:]] = newconfig + + return contiguous_coords + + +def contiguous_coordinates_box( + frac_coords: np.ndarray, + indices: List[Tuple[int]], + bring_to_centre: bool = False, +): + """Translates atoms by a lattice vector. Returns a FRACTIONAL coordinate array + in which atoms in each segment are separated from the first atom + by less than half the simulation box length. + + Parameters + ---------- + coords : np.ndarray + Array of fractional coordinates + cell : np.ndarray + 3x3 unit cell array + indices : List[Tuple[int]] + a list of index group, as in [[1,2,3], [7,8]] + (this would ensure 2 and 3 are close to 1, and 8 is close to 7) + bring_to_centre: bool + if true, atoms are shifted to minimise the distance from the average + position and not from the first atom + + Returns + ------- + np.ndarray + array of atom coordinates with the translations applied + """ + + contiguous_coords = frac_coords.copy() + + for tupleidxs in indices: + + if len(tupleidxs) < 2: + continue + + idxs = list(tupleidxs) + if bring_to_centre: + centre = np.mean(frac_coords[idxs], axis=0) + sdx = frac_coords[idxs] - centre + sdx -= np.round(sdx) + contiguous_coords[idxs] = frac_coords[idxs] + sdx + else: + sdx = frac_coords[idxs[1:]] - frac_coords[idxs[0]] + sdx -= np.round(sdx) + contiguous_coords[idxs[1:]] = frac_coords[idxs[0]] + sdx + + return contiguous_coords + + +def continuous_coordinates( + coords: np.ndarray, + cell: np.ndarray, + rcell: np.ndarray, + bond_list: List[Tuple[int]], +): + """Translates atoms by lattice vectors to ensure that + no bonds are broken. Does nothing if no bonds are defined + in the system. + + Parameters + ---------- + coords : np.ndarray + Array of atom coordinates + cell : np.ndarray + 3x3 unit cell array + rcell : np.ndarray + 3x3 reciprocal cell array + bond_list : List[Tuple[int]] + List of bonds in the system + + Returns + ------- + np.ndarray + new array of atom coordinates with translations applied + """ + atom_pool = list(range(len(coords))) + total_graph = nx.Graph() + total_graph.add_nodes_from(atom_pool) + total_graph.add_edges_from(bond_list) + segments = [] + while len(atom_pool) > 0: + last_atom = atom_pool.pop() + temp_dict = nx.dfs_successors(total_graph, last_atom) + others = reduce(list.__add__, temp_dict.values(), []) + for atom in others: + atom_pool.pop(atom_pool.index(atom)) + segment = [last_atom] + others + segments.append(sorted(segment)) + return contiguous_coordinates_real(coords, cell, rcell, segments) + + +def padded_coordinates( + coords: np.ndarray, + unit_cell: "UnitCell", + thickness: float, + fold_into_box: bool = True, +) -> np.ndarray: + """Repeats coordinates in copies of the unit cell, and removes + the atoms that are now within the specified distance from the cell wall. + The returned coordinate array contains all the original atoms, + and additionally the atoms from the copies within the thickness + from the original cell walls. + + Parameters + ---------- + coords : np.ndarray + Array of all the atoms in the unit cell + unit_cell : UnitCell + an instance of the UnitCell class, defining the simulation box + thickness : float + thickness of the outer layer to be included + fold_into_box : bool + if True, translates all the atoms so their fractional coordinates are in [0.0, 1.0) + + Returns + ------- + np.ndarray + Array of atom coordinates, together with their copies + + Raises + ------ + VoronoiError + Any error that may indicate that a Voronoi job failed + """ + if abs(thickness) < 1e-6: + return coords, np.arange(len(coords), dtype=int) + vectors = ( + unit_cell.a_vector, + unit_cell.b_vector, + unit_cell.c_vector, + ) + fractional_lengths = [thickness / np.linalg.norm(vector) for vector in vectors] + all_indices = np.arange(len(coords), dtype=int) + for axis in range(3): + extra_arrays = [] + extra_indices = [] + cutoff_max = 1 + fractional_lengths[axis] + cutoff_min = -fractional_lengths[axis] + for shift in [-1, 1]: + offset = vectors[axis] * shift + new_points = coords + offset.reshape((1, 3)) + frac_points = np.matmul(new_points, unit_cell.inverse) + if fold_into_box: + frac_points -= np.floor(frac_points) + if shift > 0: + criterion = np.where(frac_points[:, axis] < cutoff_max) + new_points = new_points[criterion] + new_indices = all_indices[criterion] + else: + criterion = np.where(frac_points[:, axis] > cutoff_min) + new_points = new_points[criterion] + new_indices = all_indices[criterion] + if len(new_points) > 0: + extra_arrays.append(new_points) + extra_indices.append(new_indices) + if len(extra_arrays) > 0: + coords = np.vstack([coords] + extra_arrays) + all_indices = np.concatenate([all_indices] + extra_indices) + return coords, all_indices class ConfigurationError(Exception): @@ -40,7 +288,7 @@ def __init__(self, chemical_system: ChemicalSystem, coords: ArrayLike, **variabl Constructor. :param chemical_system: The chemical system described by this configuration. - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` + :type chemical_system: :class: `MDANSE.Chemistry.ChemicalSystem.ChemicalSystem` :param coords: the coordinates of all the particles in the chemical system :type coords: numpy.ndarray @@ -144,7 +392,7 @@ def chemical_system(self) -> ChemicalSystem: Returns the chemical system stored in this configuration. :return: the chemical system that this configuration describes - :rtype: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` + :rtype: :class: `MDANSE.Chemistry.ChemicalSystem.ChemicalSystem` """ return self._chemical_system @@ -154,7 +402,7 @@ def clone(self, chemical_system: ChemicalSystem): Clones this configuration. :param chemical_system: the chemical system that this configuration describes - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` + :type chemical_system: :class: `MDANSE.Chemistry.ChemicalSystem.ChemicalSystem` """ pass @@ -201,7 +449,7 @@ def __init__( Constructor. :param chemical_system: The chemical system described by this configuration. - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` + :type chemical_system: :class: `MDANSE.Chemistry.ChemicalSystem.ChemicalSystem` :param coords: the coordinates of all the particles in the chemical system :type coords: numpy.ndarray @@ -226,7 +474,7 @@ def clone(self, chemical_system: Union[ChemicalSystem, None] = None): :param chemical_system: the chemical system that is to be used for copying. It must have the same number of atoms as the chemical system that this configuration describes - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem): + :type chemical_system: :class: `MDANSE.Chemistry.ChemicalSystem.ChemicalSystem): """ if chemical_system is None: chemical_system = self._chemical_system @@ -332,40 +580,9 @@ def to_real_configuration(self) -> PeriodicRealConfiguration: return real_conf - def atoms_in_shell( - self, ref: int, mini: float = 0.0, maxi: float = 10.0 - ) -> list[Atom]: - """ - Returns all atoms found in a shell around a reference atom. The shell is a (hollow) sphere around the reference - atom defined by parameters mini and maxi. All atoms within the sphere with radius maxi but not within that of - radius mini are returned. Atoms that are exactly mini or maxi distance away from the reference atom ARE counted - For more details, see :func: `MDANSE.Extensions.atoms_in_shell.atoms_in_shell_box`, which is called under the - hood. - - :param ref: the index of the reference atom - :type ref: int - - :param mini: the inner radius of the shell - :type mini: float - - :param maxi: the outer radius of the shell - :type maxi: float - - :return: list of atoms within the defined shell - :rtype: list - """ - - indexes = atoms_in_shell.atoms_in_shell_box( - self._variables["coordinates"].astype(np.float64), ref, mini, maxi - ) - - atom_list = self._chemical_system.atom_list - - selected_atoms = [atom_list[idx] for idx in indexes] - - return selected_atoms - - def contiguous_configuration(self) -> PeriodicBoxConfiguration: + def contiguous_configuration( + self, bring_to_centre: bool = False + ) -> PeriodicBoxConfiguration: """ Return a configuration with chemical entities made contiguous. @@ -373,62 +590,20 @@ def contiguous_configuration(self) -> PeriodicBoxConfiguration: :rtype: :class: `MDANSE.MolecularDynamics.Configuration.PeriodicBoxConfiguration` """ - indexes = [] - for ce in self._chemical_system.chemical_entities: - indexes.append([at.index for at in ce.atom_list]) + indices_grouped = reduce( + list.__add__, self._chemical_system._clusters.values(), [] + ) - contiguous_coords = contiguous_coordinates.contiguous_coordinates_box( - self._variables["coordinates"], self._unit_cell.transposed_direct, indexes + contiguous_coords = contiguous_coordinates_box( + self._variables["coordinates"], + indices_grouped, + bring_to_centre, ) conf = self.clone() conf._variables["coordinates"] = contiguous_coords return conf - def contiguous_offsets( - self, chemical_entities: list[_ChemicalEntity] = None - ) -> np.ndarray: - """ - Returns the contiguity offsets for a list of chemical entities. - - :param chemical_entities: the list of chemical entities whose offsets are to be calculated or None for all - entities in the chemical system - :type chemical_entities: list - - :return: the offsets - :rtype: numpy.ndarray - """ - - if chemical_entities is None: - chemical_entities = self._chemical_system.chemical_entities - else: - for i, ce in enumerate(chemical_entities): - root = ce.root_chemical_system - if root is not self._chemical_system: - raise ConfigurationError( - "All the entities provided in the chemical_entities parameter must belong " - "to the chemical system registered with this configuration, which is " - f"{self._chemical_system.name}. However, the entity at index {i}, " - f"{str(ce)}, is in chemical system " - f"{root.name if root is not None else None}.\nExpected chemical system: " - f"{repr(self._chemical_system)}\nActual chemical system: {repr(root)}\n" - f"Faulty chemical entity: {repr(ce)}" - ) - - indexes = [] - for ce in chemical_entities: - indexes.append([at.index for at in ce.atom_list]) - - offsets = contiguous_coordinates.contiguous_offsets_box( - self._variables["coordinates"][ - [item for sublist in indexes for item in sublist] - ], - self._unit_cell.transposed_direct, - indexes, - ) - - return offsets - class PeriodicRealConfiguration(_PeriodicConfiguration): is_periodic = True @@ -472,45 +647,9 @@ def to_real_coordinates(self) -> np.ndarray: """ return self._variables["coordinates"] - def atoms_in_shell( - self, ref: int, mini: float = 0.0, maxi: float = 10.0 - ) -> list[Atom]: - """ - Returns all atoms found in a shell around a reference atom. The shell is a (hollow) sphere around the reference - atom defined by parameters mini and maxi. All atoms within the sphere with radius maxi but not within that of - radius mini are returned. Atoms that are exactly mini or maxi distance away from the reference atom ARE counted - For more details, see :func: `MDANSE.Extensions.atoms_in_shell.atoms_in_shell_real`, which is called under the - hood. - - :param ref: the index of the reference atom - :type ref: int - - :param mini: the inner radius of the shell - :type mini: float - - :param maxi: the outer radius of the shell - :type maxi: float - - :return: list of atoms within the defined shell - :rtype: list - """ - - indexes = atoms_in_shell.atoms_in_shell_real( - self._variables["coordinates"].astype(np.float64), - self._unit_cell.transposed_direct, - self._unit_cell.transposed_inverse, - ref, - mini, - maxi, - ) - - atom_list = self._chemical_system.atom_list - - selected_atoms = [atom_list[idx] for idx in indexes] - - return selected_atoms - - def contiguous_configuration(self) -> PeriodicRealConfiguration: + def contiguous_configuration( + self, bring_to_centre: bool = False + ) -> PeriodicRealConfiguration: """ Return a configuration with chemical entities made contiguous. @@ -518,15 +657,16 @@ def contiguous_configuration(self) -> PeriodicRealConfiguration: :rtype: :class: `MDANSE.MolecularDynamics.Configuration.PeriodicBoxConfiguration` """ - indexes = [] - for ce in self._chemical_system.chemical_entities: - indexes.append([at.index for at in ce.atom_list]) + indices_grouped = reduce( + list.__add__, self._chemical_system._clusters.values(), [] + ) - contiguous_coords = contiguous_coordinates.contiguous_coordinates_real( + contiguous_coords = contiguous_coordinates_real( self._variables["coordinates"], - self._unit_cell.transposed_direct, - self._unit_cell.transposed_inverse, - indexes, + self._unit_cell.direct, + self._unit_cell.inverse, + indices_grouped, + bring_to_centre, ) conf = self.clone() @@ -541,61 +681,17 @@ def continuous_configuration(self) -> PeriodicRealConfiguration: :rtype: :class: `MDANSE.MolecularDynamics.Configuration.PeriodicBoxConfiguration` """ - contiguous_coords = contiguous_coordinates.continuous_coordinates( + continuous_coords = continuous_coordinates( self._variables["coordinates"], - self._unit_cell.transposed_direct, - self._unit_cell.transposed_inverse, - self._chemical_system, + self._unit_cell.direct, + self._unit_cell.inverse, + self.chemical_system._bonds, ) conf = self.clone() - conf._variables["coordinates"] = contiguous_coords + conf._variables["coordinates"] = continuous_coords return conf - def contiguous_offsets( - self, chemical_entities: Union[None, list[_ChemicalEntity]] = None - ) -> np.ndarray: - """ - Returns the contiguity offsets for a list of chemical entities. - - :param chemical_entities: the list of chemical entities whose offsets are to be calculated or None for all - entities in the chemical system - :type chemical_entities: list - - :return: the offsets - :rtype: numpy.ndarray - """ - - if chemical_entities is None: - chemical_entities = self._chemical_system.chemical_entities - else: - for i, ce in enumerate(chemical_entities): - root = ce.root_chemical_system - if root is not self._chemical_system: - raise ConfigurationError( - "All the entities provided in the chemical_entities parameter must belong " - "to the chemical system registered with this configuration, which is " - f"{self._chemical_system.name}. However, the entity at index {i}, " - f"{str(ce)}, is in chemical system " - f"{root.name if root is not None else None}.\nExpected chemical system: " - f"{repr(self._chemical_system)}\nActual chemical system: {repr(root)}\n" - f"Faulty chemical entity: {repr(ce)}" - ) - indexes = [] - for ce in chemical_entities: - indexes.append([at.index for at in ce.atom_list]) - - offsets = contiguous_coordinates.contiguous_offsets_real( - self._variables["coordinates"][ - [item for sublist in indexes for item in sublist] - ], - self._unit_cell.transposed_direct, - self._unit_cell.transposed_inverse, - indexes, - ) - - return offsets - class RealConfiguration(_Configuration): is_periodic = False @@ -608,7 +704,7 @@ def clone( :param chemical_system: the chemical system that is to be used for copying. It must have the same number of atoms as the chemical system that this configuration describes - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` + :type chemical_system: :class: `MDANSE.Chemistry.ChemicalSystem.ChemicalSystem` :return: the cloned configuration :rtype: :class: `MDANSE.MolecularDynamics.Configuration.RealConfiguration` @@ -641,40 +737,9 @@ def to_real_coordinates(self) -> np.ndarray: """ return self._variables["coordinates"] - def atoms_in_shell( - self, ref: int, mini: float = 0.0, maxi: float = 10.0 - ) -> list[Atom]: - """ - Returns all atoms found in a shell around a reference atom. The shell is a (hollow) sphere around the reference - atom defined by parameters mini and maxi. All atoms within the sphere with radius maxi but not within that of - radius mini are returned. Atoms that are exactly mini or maxi distance away from the reference atom ARE counted - For more details, see :func: `MDANSE.Extensions.atoms_in_shell.atoms_in_shell_nopbc`, which is called under the - hood. - - :param ref: the index of the reference atom - :type ref: int - - :param mini: the inner radius of the shell - :type mini: float - - :param maxi: the outer radius of the shell - :type maxi: float - - :return: list of atoms within the defined shell - :rtype: list - """ - - indexes = atoms_in_shell.atoms_in_shell_nopbc( - self._variables["coordinates"].astype(np.float64), ref, mini, maxi - ) - - atom_list = self._chemical_system.atom_list - - selected_atoms = [atom_list[idx] for idx in indexes] - - return selected_atoms - - def contiguous_configuration(self) -> RealConfiguration: + def contiguous_configuration( + self, bring_to_centre: bool = False + ) -> RealConfiguration: """ Return a configuration with chemical entities made contiguous, which is always itself. @@ -692,54 +757,13 @@ def continuous_configuration(self) -> RealConfiguration: """ return self - def contiguous_offsets( - self, chemical_entities: Union[None, list[_ChemicalEntity]] = None - ) -> np.ndarray: - """ - Returns the contiguity offsets for a list of chemical entities, which are always zero for every atom. - - :param chemical_entities: the list of chemical entities whose offsets are to be calculated or None for all - entities in the chemical system - :type chemical_entities: list - - :return: the offsets - :rtype: numpy.ndarray - """ - - if chemical_entities is None: - chemical_entities = self._chemical_system.chemical_entities - else: - for j, ce in enumerate(chemical_entities): - root = ce.root_chemical_system - if root is not self._chemical_system: - raise ConfigurationError( - "All the entities provided in the chemical_entities parameter must belong " - "to the chemical system registered with this configuration, which is " - f"{self._chemical_system.name}. However, the entity at index {j}, " - f"{str(ce)}, is in chemical system " - f"{root.name if root is not None else None}.\nExpected chemical system: " - f"{repr(self._chemical_system)}\nActual chemical system: {repr(root)}\n" - f"Faulty chemical entity: {repr(ce)}" - ) - - number_of_particles = sum( - [ce.total_number_of_atoms for ce in chemical_entities] - ) - - offsets = np.zeros((number_of_particles, 3)) - - return offsets - if __name__ == "__main__": np.random.seed(1) - from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem - n_atoms = 2 cs = ChemicalSystem() - for i in range(n_atoms): - cs.add_chemical_entity(Atom(symbol="H")) + cs.initialise_atoms(["H", "H"]) coordinates = np.empty((n_atoms, 3), dtype=float) coordinates[0, :] = [1, 1, 1] @@ -748,5 +772,3 @@ def contiguous_offsets( uc = np.array([[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]) conf = RealConfiguration(cs, coordinates) - - print(conf.atoms_in_shell(0, 0, 5)) diff --git a/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py b/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py index 000484c7ea..a4f2b0841b 100644 --- a/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py +++ b/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py @@ -15,14 +15,14 @@ # from itertools import product -from typing import List, Dict +from typing import List import numpy as np from numpy.typing import NDArray from scipy.spatial import KDTree from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Trajectory import Trajectory @@ -39,10 +39,11 @@ def __init__( **kwargs, ): self._chemical_system = trajectory.chemical_system + self._input_trajectory = trajectory self._selection = selection self._frames = trajectory - self._unit_cell = self._chemical_system.configuration - self._periodic = self._chemical_system.configuration.is_periodic + self._unit_cell = self._input_trajectory.configuration(0) + self._periodic = self._input_trajectory.configuration(0).is_periodic self.check_composition(self._chemical_system) self._bonds = None self._bond_mapping = None @@ -58,10 +59,10 @@ def check_composition(self, chemical: ChemicalSystem): """ if self._selection is not None: atom_elements = [ - atom.symbol for atom in chemical.atoms if atom.index in self._selection + self._chemical_system.atom_list[index] for index in self._selection ] else: - atom_elements = [atom.symbol for atom in chemical.atoms] + atom_elements = self._chemical_system.atom_list unique_elements = np.unique(atom_elements) radii = { element: ATOMS_DATABASE.get_atom_property(element, "covalent_radius") @@ -126,7 +127,7 @@ def periodic_distances( NDArray -- an (N,N) array of squared distances between all the atom pairs, one for each combination of the unit cell vectors. """ - unit_cell = self._chemical_system.configuration.unit_cell + unit_cell = self._input_trajectory.configuration(frame_number).unit_cell vector_a, vector_b, vector_c = ( unit_cell.a_vector, unit_cell.b_vector, @@ -168,7 +169,7 @@ def find_bonds(self, frames: List[int] = None, tolerance: float = 0.2): for pair in pairs } total_max_length = np.max([x for x in maxbonds.values()]) - for nstep, frame_number in enumerate(samples): + for _, frame_number in enumerate(samples): distances = self.internal_distances( frame_number=frame_number, max_distance=total_max_length ) @@ -193,59 +194,9 @@ def find_bonds(self, frames: List[int] = None, tolerance: float = 0.2): self._bond_mapping = bond_mapping self._unique_bonds = np.unique(np.sort(bonds, axis=1), axis=0) - def find_molecules(self, tolerance: float = 0.2): - """Uses the internal list of bonds to find atoms that belong to the same - molecules. The grouping of atoms is saved internally. - """ - if self._bond_mapping is None: - self.find_bonds(tolerance=tolerance) - - def recursive_walk( - number: int, bond_mapping: Dict[int, int], atom_pool: List[int] - ): - """Returns a list of atoms connected by bonds to the input atom. - Called recursively in order to find the entire molecule. - - Arguments: - number -- number (index) of the starting atom on the atom list. - bond_mapping -- dictionary of the interatomic connections, - determined using the find_bonds method. - atom_pool -- a list of all the atom numbers, each atom to be used - once only. Once an atom number has been assigned to a molecule, - it will also be removed from this list. - - Returns: - List[int] -- a list of atom numbers (indices) - """ - connected_atoms = [number] - for at_number in bond_mapping[number]: - if at_number in atom_pool: - connected_atoms.append(at_number) - atom_pool.pop(atom_pool.index(at_number)) - connected_atoms += recursive_walk( - at_number, bond_mapping, atom_pool - ) - return connected_atoms - - molecules = [] - atom_pool = list(range(len(self._elements))) - while len(atom_pool): - new_molecule = recursive_walk( - atom_pool.pop(), self._bond_mapping, atom_pool - ) - molecules.append(list(np.unique(new_molecule))) - self._molecules = molecules - - def add_bond_information(self): - for bond in self._unique_bonds: - ind1, ind2 = bond - at1, at2 = ( - self._chemical_system.atoms[bond[0]], - self._chemical_system.atoms[bond[1]], - ) - at1.bonds.append(at2) - at2.bonds.append(at1) - self._chemical_system._bonds.append((ind1, ind2)) + def add_bond_information(self, new_chemical_system: ChemicalSystem): + new_chemical_system.add_bonds(self._unique_bonds) + new_chemical_system.find_clusters_from_bonds() def add_point(self, index: int, point: np.ndarray, radius: float) -> bool: return True diff --git a/MDANSE/Src/MDANSE/MolecularDynamics/MockTrajectory.py b/MDANSE/Src/MDANSE/MolecularDynamics/MockTrajectory.py index fdbd8900f3..91c8185939 100644 --- a/MDANSE/Src/MDANSE/MolecularDynamics/MockTrajectory.py +++ b/MDANSE/Src/MDANSE/MolecularDynamics/MockTrajectory.py @@ -16,18 +16,19 @@ import math import json -from typing import TypeVar +from typing import TypeVar, List import numpy as np from MDANSE.Chemistry import ATOMS_DATABASE +from MDANSE.Mathematics.Geometry import center_of_mass from MDANSE.Framework.Units import measure -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem -from MDANSE.Extensions import com_trajectory +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Configuration import ( PeriodicRealConfiguration, RealConfiguration, _Configuration, + contiguous_coordinates_real, ) from MDANSE.MolecularDynamics.TrajectoryUtils import atomic_trajectory from MDANSE.MolecularDynamics.UnitCell import UnitCell @@ -76,9 +77,8 @@ def __init__( self._variables = {} self._coordinates = None - self._chemicalSystem = ChemicalSystem("MockSystem") - for atom in self._atom_types: - self._chemicalSystem.add_chemical_entity(Atom(symbol=atom)) + self._chemical_system = ChemicalSystem("MockSystem") + self._chemical_system.initialise_atoms(self._atom_types) def set_coordinates(self, coords: np.ndarray): """Sets the initial (equlibrium) positions of atoms from @@ -286,16 +286,27 @@ def configuration(self, frame: int) -> "_Configuration": if self._pbc: conf = PeriodicRealConfiguration( - self._chemicalSystem, coordinates, unit_cell, **variables + self._chemical_system, coordinates, unit_cell, **variables ) else: - conf = RealConfiguration(self._chemicalSystem, coordinates, **variables) + conf = RealConfiguration(self._chemical_system, coordinates, **variables) return conf def _load_unit_cells(self): """Only added for compatibility with Trajectory.""" + def get_atom_property(self, atom_symbol: str, property: str): + return ATOMS_DATABASE.get_atom_property(atom_symbol, property) + + @property + def atoms_in_database(self) -> List[str]: + return ATOMS_DATABASE.atoms + + @property + def properties_in_database(self) -> List[str]: + return ATOMS_DATABASE.properties + def unit_cell(self, frame: int) -> UnitCell: """Returns the UnitCell the size of the system. @@ -324,12 +335,12 @@ def __len__(self) -> int: return self._number_of_frames def read_com_trajectory( - self, atoms, first=0, last=None, step=1, box_coordinates=False + self, indices, first=0, last=None, step=1, box_coordinates=False ): """Build the trajectory of the center of mass of a set of atoms. :param atoms: the atoms for which the center of mass should be computed - :type atoms: list MDANSE.Chemistry.ChemicalEntity.Atom + :type atoms: list MDANSE.Chemistry.ChemicalSystem.Atom :param first: the index of the first frame :type first: int :param last: the index of the last frame @@ -346,59 +357,45 @@ def read_com_trajectory( if last is None: last = len(self) - indexes = [at.index for at in atoms] masses = np.array( [ - ATOMS_DATABASE.get_atom_property(at.symbol, "atomic_weight") - for at in atoms + self.chemical_system.atom_property("atomic_weight")[index] + for index in indices ] ) frames = np.array([self.coordinates(fnum) for fnum in range(first, last, step)]) - coords = frames[:, indexes, :].astype(np.float64) + coords = frames[:, indices, :].astype(np.float64) if coords.ndim == 2: coords = coords[np.newaxis, :, :] if self._pbc: direct_cells = np.array( - [ - self.unit_cell(fnum).transposed_direct - for fnum in range(first, last, step) - ] + [self.unit_cell(fnum).direct for fnum in range(first, last, step)] ) inverse_cells = np.array( - [ - self.unit_cell(fnum).transposed_inverse - for fnum in range(first, last, step) - ] + [self.unit_cell(fnum).inverse for fnum in range(first, last, step)] ) - - top_lvl_chemical_entities = set( - [at.top_level_chemical_entity for at in atoms] - ) - top_lvl_chemical_entities_indexes = [ - [at.index for at in e.atom_list] for e in top_lvl_chemical_entities - ] - bonds = {} - for e in top_lvl_chemical_entities: - for at in e.atom_list: - bonds[at.index] = [other_at.index for other_at in at.bonds] - - com_traj = com_trajectory.com_trajectory( + temp_coords = contiguous_coordinates_real( coords, direct_cells, inverse_cells, - masses, - top_lvl_chemical_entities_indexes, - indexes, - bonds, - box_coordinates=box_coordinates, + [list(range(len(coords)))], + bring_to_centre=True, ) + com_coords = np.vstack( + [ + center_of_mass(temp_coords[tstep], masses) + for tstep in range(len(temp_coords)) + ] + ) + + com_traj = atomic_trajectory(com_coords, direct_cells, inverse_cells) else: com_traj = np.sum( - coords[:, indexes, :] * masses[np.newaxis, :, np.newaxis], axis=1 + coords[:, indices, :] * masses[np.newaxis, :, np.newaxis], axis=1 ) com_traj /= np.sum(masses) @@ -518,9 +515,9 @@ def chemical_system(self): """Return the chemical system stored in the trajectory. :return: the chemical system - :rtype: MDANSE.Chemistry.ChemicalEntity.ChemicalSystem + :rtype: MDANSE.Chemistry.ChemicalSystem.ChemicalSystem """ - return self._chemicalSystem + return self._chemical_system @property def file(self) -> str: diff --git a/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py b/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py index a72c7a7e8e..0abda4616c 100644 --- a/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py +++ b/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py @@ -15,7 +15,10 @@ # import os from ast import operator -from typing import Collection +from typing import Collection, List, Dict, TYPE_CHECKING, Any + +if TYPE_CHECKING: + from MDANSE.Chemistry.Databases import AtomsDatabase import math import numpy as np @@ -25,14 +28,10 @@ from MDANSE.Trajectory.MdanseTrajectory import MdanseTrajectory from MDANSE.Trajectory.H5MDTrajectory import H5MDTrajectory from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem, _ChemicalEntity +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Configuration import ( RealConfiguration, ) -from MDANSE.MolecularDynamics.TrajectoryUtils import ( - sorted_atoms, - resolve_undefined_molecules_name, -) from MDANSE import PLATFORM @@ -56,6 +55,7 @@ def __init__(self, filename, trajectory_format=None): self._trajectory = self.open_trajectory(self._format) self._min_span = np.zeros(3) self._max_span = np.zeros(3) + self._atom_cache = {} def guess_correct_format(self): """This is a placeholder for now. As the number of @@ -122,7 +122,7 @@ def coordinates(self, frame): return self._trajectory.coordinates(frame) - def configuration(self, frame): + def configuration(self, frame: int = 0): """Build and return a configuration at a given frame. :param frame: the frame @@ -177,12 +177,12 @@ def min_span(self): return self._min_span def read_com_trajectory( - self, atoms, first=0, last=None, step=1, box_coordinates=False + self, atom_indices, first=0, last=None, step=1, box_coordinates=False ): """Build the trajectory of the center of mass of a set of atoms. :param atoms: the atoms for which the center of mass should be computed - :type atoms: list MDANSE.Chemistry.ChemicalEntity.Atom + :type atoms: list MDANSE.Chemistry.ChemicalSystem.Atom :param first: the index of the first frame :type first: int :param last: the index of the last frame @@ -196,7 +196,11 @@ def read_com_trajectory( :rtype: ndarray """ return self._trajectory.read_com_trajectory( - atoms, first=first, last=last, step=step, box_coordinates=box_coordinates + atom_indices, + first=first, + last=last, + step=step, + box_coordinates=box_coordinates, ) def to_real_coordinates(self, box_coordinates, first, last, step): @@ -219,7 +223,7 @@ def to_real_coordinates(self, box_coordinates, first, last, step): def read_atomic_trajectory( self, index, first=0, last=None, step=1, box_coordinates=False ): - """Read an atomic trajectory. The trajectory is corrected from box jumps. + """Read an atomic trajectory. The trajectory is corrected for box jumps. :param index: the index of the atom :type index: int @@ -279,12 +283,48 @@ def has_variable(self, variable: str) -> bool: """ return self._trajectory.has_variable(variable) + def get_atom_property(self, atom_symbol: str, property: str): + if (atom_symbol, property) not in self._atom_cache.keys(): + self._atom_cache[(atom_symbol, property)] = ( + self._trajectory.get_atom_property(atom_symbol, property) + ) + return self._atom_cache[(atom_symbol, property)] + + def has_atom(self, symbol: str): + return symbol in self.atoms_in_database + + def get_property_dict(self, symbol: str) -> Dict[str, Any]: + """Returns a dictionary of all the properties of an atom type. + + Parameters + ---------- + symbol : str + Symbol of the atom. + + Returns + ------- + Union[int, float, str] + The atom property. + """ + return { + property_name: self.get_atom_property(symbol, property_name) + for property_name in self.properties_in_database + } + + @property + def atoms_in_database(self) -> List[str]: + return self._trajectory.atoms_in_database() + + @property + def properties_in_database(self) -> List[str]: + return self._trajectory.properties_in_database() + @property def chemical_system(self): """Return the chemical system stored in the trajectory. :return: the chemical system - :rtype: MDANSE.Chemistry.ChemicalEntity.ChemicalSystem + :rtype: MDANSE.Chemistry.ChemicalSystem.ChemicalSystem """ return self._trajectory.chemical_system @@ -325,6 +365,93 @@ def variables(self): return self._trajectory.variables() +additive_atom_properties = [ + "nucleon", + "b_incoherent2", + "xray_asf_b4", + "proton", + "atomic_weight", + "ionization_energy", + "xs_absorption", + "xs_scattering", + "b_incoherent", + "b_coherent", + "charge", + "xray_asf_c", + "neutron", + "xray_asf_a4", + "xray_asf_a2", + "xray_asf_a3", + "xray_asf_a1", + "xray_asf_b3", + "xray_asf_b2", + "xray_asf_b1", + "xs_incoherent", + "xs_coherent", + "nuclear_spin", +] +averaged_atom_properties = [ + "electronegativity", + "electroaffinity", + "atomic_number", + "group", + "serie", +] +constant_atom_properties = { + "equal": 1.0, + "abundance": 100.0, +} +atom_radii = [ + "covalent_radius", + "vdw_radius", + "atomic_radius", +] + + +def create_average_atom( + atom_dictionary: Dict[str, int], database: Trajectory, radius_padding: float = 0.0 +): + all_properties = database.properties_in_database + values = {} + for property in all_properties: + temp = [] + total = 0 + for element_name, element_count in atom_dictionary.items(): + temp.append( + [database.get_atom_property(element_name, property), element_count] + ) + if property in additive_atom_properties: + total = np.sum([float(x[0]) * int(x[1]) for x in temp]) + elif property in averaged_atom_properties: + total = np.sum([float(x[0]) * int(x[1]) for x in temp]) / np.sum( + [int(x[1]) for x in temp] + ) + elif property in constant_atom_properties.keys(): + total = constant_atom_properties[property] + elif property in atom_radii: + total = ( + np.sum([float(x[0]) * int(x[1]) for x in temp]) + / np.sum([int(x[1]) for x in temp]) + + radius_padding + ) + else: + for entry in temp: + try: + converted = float(entry[0]) + except TypeError: + total = entry + except ValueError: + total = entry + else: + total += converted * entry[1] + values[property] = total + is_dummy = 1 + for element_name, _ in atom_dictionary.items(): + is_dummy = is_dummy and database.get_atom_property(element_name, "dummy") + values["dummy"] = is_dummy + return values + + class TrajectoryWriterError(Exception): pass @@ -348,11 +475,11 @@ def __init__( :param h5_filename: the trajectory filename :type h5_filename: str :param chemical_system: the chemical system - :type h5_filename: MDANSE.Chemistry.ChemicalEntity.ChemicalSystem + :type h5_filename: MDANSE.Chemistry.ChemicalSystem.ChemicalSystem :param h5_filename: the number of steps :type h5_filename: int :param selected_atoms: the selected atoms of the chemical system to write - :type selected_atoms: list of MDANSE.Chemistry.ChemicalEntity.Atom + :type selected_atoms: list of MDANSE.Chemistry.ChemicalSystem.Atom """ self._h5_filename = h5_filename @@ -360,23 +487,14 @@ def __init__( self._h5_file = h5py.File(self._h5_filename, "w") self._chemical_system = chemical_system + self._last_configuration = None if selected_atoms is None: - self._selected_atoms = self._chemical_system.atom_list + self._selected_atoms = list(self._chemical_system._atom_indices) else: - for at in selected_atoms: - if at.root_chemical_system != chemical_system: - LOG.error( - f"atom.chemical_system={at.root_chemical_system} and traj.chemical_system={chemical_system}" - ) - raise TrajectoryWriterError( - "One or more atoms of the selection comes from a different chemical system" - ) - self._selected_atoms = sorted_atoms(selected_atoms) + self._selected_atoms = selected_atoms - self._selected_atoms = [at.index for at in self._selected_atoms] - - all_atoms = self._chemical_system.atom_list + all_atoms = list(self._chemical_system.atom_list) for idx in self._selected_atoms: all_atoms[idx] = False @@ -410,6 +528,132 @@ def __init__( else: self._initial_charges = initial_charges + def write_atom_properties( + self, symbol: str, properties: Dict[str, Any], ptypes: Dict[str, str] = None + ): + if "atom_database" not in self._h5_file.keys(): + group = self._h5_file.create_group("/atom_database") + else: + group = self._h5_file["/atom_database"] + string_dt = h5py.special_dtype(vlen=str) + if "property_labels" not in group: + label_dataset = group.create_dataset( + "property_labels", data=200 * [""], dtype=string_dt + ) + else: + label_dataset = self._h5_file["/atom_database/property_labels"] + if "property_types" not in group: + type_dataset = group.create_dataset( + "property_types", data=200 * [""], dtype=string_dt + ) + else: + type_dataset = self._h5_file["/atom_database/property_types"] + next_index = 0 + for label in label_dataset[:]: + if len(label) > 0: + next_index += 1 + else: + break + properties["dummy"] = 0 + if symbol == "Du": + properties["dummy"] = 1 + if "element" in properties.keys(): + if properties["element"] == "dummy": + properties["dummy"] = 1 + new_labels = [str(x) for x in properties.keys()] + if ptypes is None: + ptypes = ATOMS_DATABASE._properties + ptypes["dummy"] = "int" + for label in new_labels: + if label.encode("utf-8") not in label_dataset: + label_dataset[next_index] = label + type_dataset[next_index] = ptypes[label] + next_index += 1 + mapping = { + property_label.decode("utf-8"): index + for index, property_label in enumerate(label_dataset[:]) + } + atom_dataset = group.create_dataset(symbol, data=200 * [-1.0]) + for key, value in properties.items(): + try: + float(value) + except ValueError: + continue + except TypeError: + continue + else: + atom_dataset[mapping[key]] = value + try: + colour = [int(x) for x in properties["color"].split(";")] + except AttributeError: + colour = [int(x) for x in properties["color"][0].split(";")] + atom_dataset[mapping["color"]] = ( + 0x10000 * colour[0] + 0x100 * colour[1] + colour[2] + ) + + def write_atom_database( + self, + symbols: List[str], + database: "AtomsDatabase", + optional_molecule_radii: Dict[str, float] = None, + ): + for atom_symbol in symbols: + if database.has_atom(atom_symbol): + property_dict = database.get_property_dict(atom_symbol) + else: + atom_dict = {} + molecule_radius = 0.0 + for token in atom_symbol.split("_"): + symbol = "" + number = "" + noletters = True + for char in token: + if char.isnumeric(): + if noletters: + symbol += char + else: + number += char + else: + symbol += char + noletters = False + atom_dict[symbol] = int(number) + if optional_molecule_radii is not None: + molecule_radius = optional_molecule_radii.get(atom_symbol, 0.0) + property_dict = create_average_atom( + atom_dict, database, radius_padding=molecule_radius + ) + if hasattr(database, "_properties"): + self.write_atom_properties( + atom_symbol, property_dict, database._properties + ) + else: + self.write_atom_properties(atom_symbol, property_dict) + + def write_standard_atom_database(self): + symbols = list(np.unique(self._chemical_system.atom_list)) + database = ATOMS_DATABASE + for atom_symbol in symbols: + if database.has_atom(atom_symbol): + property_dict = database.get_property_dict(atom_symbol) + else: + atom_dict = {} + for token in atom_symbol.split("_"): + symbol = "" + number = "" + noletters = True + for char in token: + if char.isnumeric(): + if noletters: + symbol += char + else: + number += char + else: + symbol += char + noletters = False + atom_dict[symbol] = int(number) + property_dict = create_average_atom(atom_dict, database) + self.write_atom_properties(atom_symbol, property_dict, database._properties) + def _dump_chemical_system(self): """Dump the chemical system to the trajectory file.""" @@ -423,11 +667,10 @@ def close(self): """Close the trajectory file""" self.validate_charges() - configuration = self._chemical_system.configuration n_atoms = self._chemical_system.total_number_of_atoms - if configuration is not None: + if self._last_configuration is not None: configuration_grp = self._h5_file["/configuration"] - for k, v in configuration.variables.items(): + for k, v in self._last_configuration.variables.items(): dset = configuration_grp.get(k, None) dset.resize((self._current_index, n_atoms, 3)) try: @@ -488,7 +731,7 @@ def validate_charges(self): if variable_charge_dset is not None: del self._h5_file[variable_charge_dset.name] - def dump_configuration(self, time, units=None): + def dump_configuration(self, configuration, time, units=None): """Dump the chemical system configuration at a given time. :param time: the time @@ -503,7 +746,6 @@ def dump_configuration(self, time, units=None): f"The current index {self._current_index} is greater than the actual number of steps of the trajectory {self._n_steps}" ) - configuration = self._chemical_system.configuration if configuration is None: return @@ -560,6 +802,7 @@ def dump_configuration(self, time, units=None): time_dset[self._current_index] = time self._current_index += 1 + self._last_configuration = configuration class RigidBodyTrajectoryGenerator: @@ -575,7 +818,7 @@ class RigidBodyTrajectoryGenerator: def __init__( self, trajectory, - chemical_entity: _ChemicalEntity, + chemical_entity: List[int], reference, first=0, last=None, @@ -586,7 +829,7 @@ def __init__( :param trajectory: the input trajectory :type trajectory: MDANSE.Trajectory.Trajectory :param chemical_entity: the chemical enitty for which the Rigig-Body trajectory should be computed - :type chemical_entity: MDANSE.Chemistry.ChemicalEntity.ChemicalEntity + :type chemical_entity: MDANSE.Chemistry.ChemicalSystem.ChemicalEntity :param reference: the reference configuration. Must be continuous. :type reference: MDANSE.MolecularDynamics.Configuration.Configuration :param first: the index of the first frame @@ -604,9 +847,7 @@ def __init__( atoms = chemical_entity.atom_list - masses = [ - ATOMS_DATABASE.get_atom_property(at.symbol, "atomic_weight") for at in atoms - ] + masses = [ATOMS_DATABASE.get_atom_property(at, "atomic_weight") for at in atoms] mass = sum(masses) @@ -624,7 +865,7 @@ def __init__( # relative coords of the CONTIGUOUS reference r_ref = np.zeros((len(atoms), 3), np.float64) for i, at in enumerate(atoms): - r_ref[i] = reference["coordinates"][at.index, :] - ref_com + r_ref[i] = reference["coordinates"][i, :] - ref_com unit_cells, inverse_unit_cells = self._trajectory.get_unit_cells() if unit_cells is not None: @@ -632,9 +873,7 @@ def __init__( inverse_unit_cells = inverse_unit_cells[first:last:step, :, :] for i, at in enumerate(atoms): - r = self._trajectory.read_atomic_trajectory( - at.index, first, last, step, True - ) + r = self._trajectory.read_atomic_trajectory(i, first, last, step, True) r = r - rcms r = r[:, np.newaxis, :] @@ -745,7 +984,7 @@ def read_atoms_trajectory( # from MDANSE.MolecularDynamics.Configuration import RealConfiguration - # from MDANSE.Chemistry.ChemicalEntity import Atom + # from MDANSE.Chemistry.ChemicalSystem import Atom # cs = ChemicalSystem() # for i in range(768): # cs.add_chemical_entity(Atom(symbol='H')) @@ -770,8 +1009,6 @@ def read_atoms_trajectory( if __name__ == "__main__": from MDANSE.MolecularDynamics.Configuration import RealConfiguration - from MDANSE.Chemistry.ChemicalEntity import Atom - cs = ChemicalSystem() for i in range(2): cs.add_chemical_entity(Atom(symbol="H")) diff --git a/MDANSE/Src/MDANSE/MolecularDynamics/TrajectoryUtils.py b/MDANSE/Src/MDANSE/MolecularDynamics/TrajectoryUtils.py index c4ca42d668..3f27c1519b 100644 --- a/MDANSE/Src/MDANSE/MolecularDynamics/TrajectoryUtils.py +++ b/MDANSE/Src/MDANSE/MolecularDynamics/TrajectoryUtils.py @@ -15,20 +15,13 @@ # import operator -from typing import Union, Iterable +from typing import Union, Iterable, List import numpy as np from MDANSE.Core.Error import Error from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import ( - Atom, - AtomCluster, - AtomGroup, - ChemicalSystem, - _ChemicalEntity, -) -from MDANSE.Extensions import fast_calculation +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem class MolecularDynamicsError(Error): @@ -53,440 +46,61 @@ def elements_from_masses(masses: Iterable[float], tolerance: float = 0.01): return result -def atom_index_to_molecule_index(chemical_system: ChemicalSystem) -> dict[int, int]: - """ - Maps the indices of all atoms in a chemical system to the indices of their root parent chemical entities and returns - the lookup table. This can then be used to retrieve the root parent chemical entity of an atom through the - chemical_entities property of ChemicalSystem. - - :param chemical_system: the chemical system whose lookup table is to be generated - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` +def atom_index_to_molecule_index(chemical_system: ChemicalSystem) -> List[int]: + """Returns a list of molecule numbers, per atom. Single + atoms are assigned the value -1. - :return: a lookup table mapping atom indices to the indices of their root chemical entity - :rtype: dict - - :Example: + Parameters + ---------- + chemical_system : ChemicalSystem + Object describing all the atoms and molecules in the system - >>> from MDANSE.Chemistry.ChemicalEntity import Molecule, ChemicalSystem - >>> - >>> # Set up a chemical system - >>> molecules = [Molecule('WAT', 'w1'), Molecule('WAT', 'w2')] - >>> cs = ChemicalSystem() - >>> for molecule in molecules: - >>> cs.add_chemical_entity(molecule) - >>> - >>> atom_index_to_molecule_index(cs) - {0: 0, 1: 0, 2: 0, 3: 1, 4: 1, 5: 1} + Returns + ------- + List[int] + list of molecule numbers, per atom. """ - lut = {} - for i, ce in enumerate(chemical_system.chemical_entities): - for at in ce.atom_list: - lut[at.index] = i - + lut = [-1] * len(chemical_system.atom_list) + last_cluster = 1 + for cluster_name in chemical_system._clusters: + for cluster in chemical_system._clusters[cluster_name]: + for atom_index in cluster: + lut[atom_index] = last_cluster + last_cluster += 1 return lut -def brute_formula( - chemical_entity: _ChemicalEntity, sep: str = "_", ignore_ones: bool = False -) -> str: - """ - Determine the molecular formula of a given chemical entity. - - :param chemical_entity: the chemical entity whose formula is to be determined - :type chemical_entity: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalEntity` - - :param sep: the separator used between elements, i.e. with the default separator a water molecule will return H2_O1 - :type sep: str - - :param ignore_ones: determines whether number 1 should be printed, i.e. whether water should be H2_O1 or H2_O - :type ignore_ones: bool - - :return: the molecular formula - :rtype: str - - :Example: - - >>> from MDANSE.Chemistry.ChemicalEntity import Molecule - >>> m = Molecule('WAT', 'water') - >>> - >>> brute_formula(m) - 'H2_O1' - >>> brute_formula(m, '') - 'H2O1' - >>> brute_formula(m, '', True) - 'H2O' - """ - - contents = {} - - for at in chemical_entity.atom_list: - contents[at.symbol] = str(int(contents.get(at.symbol, 0)) + 1) - - formula = sep.join(["".join(v) for v in sorted(contents.items())]) - - if ignore_ones: - return formula.replace("1", "") - else: - return formula - - -def build_connectivity( - chemical_system: ChemicalSystem, tolerance: float = 0.05 -) -> None: - """ - Creates bonds between atoms within each atom cluster as well as between loose atoms. Bonds are created only between - atoms whose covalent radii overlap, i.e. for whose the sum of their covalent radii plus the tolerance is smaller - than their distance. Bonds are created only between atoms within a - :class: `MDANSE.Chemistry.ChemicalEntity.AtomCluster` or between loose atoms (atoms that are not part of another - chemical entity or which are the only atom in their chemical entity). No bonds are created between atoms of other - chemical entities (unless it's the only atom in it), and no bonds are created between atoms in different atom - clusters or between atoms in an entity and loose atoms, regardless of their positions. - - NOTE: Periodic boundary conditions are ignored in this function; bonds are not created across PBC, only within. - - Please note that the coordinates of atoms are taken from the configuration registered in the provided chemical - system. - - :param chemical_system: the chemical system in which bonds are to be created - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` - - :param tolerance: the tolerance used for defining whether two atoms are bonded in nm - :type tolerance: float - - :return: None - - :Example: - - >>> from MDANSE.Chemistry.ChemicalEntity import Atom, AtomCluster, AtomGroup, Molecule, ChemicalSystem - >>> from MDANSE.MolecularDynamics.Configuration import RealConfiguration - >>> import numpy as np - >>> - >>> cs = ChemicalSystem() - >>> - >>> # Atoms in a Molecule object will not be changed - >>> molecule = Molecule('WAT', 'water') - >>> - >>> # Atoms in atom clusters and loose atoms as well as the atom group as it has only one atom will be bonded - >>> # if appropriate. All the atoms here are H atoms whose covalent radius is 0.023 in MDANSE atom database - >>> atom = Atom(name='loose atom') - >>> cluster = AtomCluster('name', [Atom(name=f'cluster atom {i}') for i in range(4)]) - >>> group = AtomGroup([Atom(name='group atom', parent=cs)]) - >>> - >>> for entity in [molecule, atom, cluster, group]: - >>> cs.add_chemical_entity(entity) - >>> - >>> # The coordinates of all the previously defined particles, in the order they were added to the chemical system - >>> coords = np.array([[1, 1.05, 1], [1, 0.98, 1], [1, 1.12, 1], - [1, 1, 1.02], - [1, 1, 1], [1.044, 1, 1], [0.95, 1, 1], [1.055, 1, 1], - [1, 1, 1.06]]) - >>> configuration = RealConfiguration(cs, coords) - >>> cs.configuration = configuration - >>> - >>> build_connectivity(cs, 0.005) - >>> - >>> # The bonding of atoms in molecule has not changed even though. by coordinates, some are within bonding distance - >>> # of some of the other atoms. - >>> molecule['OW'].bonds - [Atom(name='HW2'), Atom(name='HW1')] - >>> molecule['HW2'].bonds - [Atom(name='OW')] - >>> molecule['HW1'].bonds - [Atom(name='OW')] - >>> - >>> # The loose atom is within the bonding distance of the first atom in the cluster, but no bond is created between - >>> # them since bonds are created only in atoms within each cluster and between other atoms separately. A bond is, - >>> # however created between the loose atom and the atom in the atom group since they are within bonding distance - >>> # and are considered to be 'other' atoms (not withing a cluster but not within a large structure). - >>> atom.bonds - [Atom(name='group atom')] - >>> group._atoms[0].bonds - [Atom(name='loose atom')] - >>> - >>> # Atoms are bonded purely based on distance (sum of the two atoms' covalent radii + tolerance) regardless of the - >>> # element, so it is possible to end up in a chemically unfeasible situation such as this chain of 4 H atoms: - >>> cluster[0].bonds - [Atom(name='cluster atom 1'), Atom(name='cluster atom 2')] - >>> cluster[1].bonds - [Atom(name='cluster atom 0'), Atom(name='cluster atom 3')] - >>> cluster[2].bonds - [Atom(name='cluster atom 0')] - >>> cluster[3].bonds - [Atom(name='cluster atom 1')] - """ - - conf = chemical_system.configuration - - atom_clusters = [] - single_atoms_objects = [] - - # Find all atom clusters, loose atoms, and one-atom objects - for ce in chemical_system.chemical_entities: - if isinstance(ce, Atom): - single_atoms_objects.append(ce) - elif isinstance(ce, AtomCluster): - atom_clusters.append(ce) - else: - if ce.number_of_atoms == 1: - single_atoms_objects.extend(ce.atom_list) - - if single_atoms_objects: - atom_clusters.append(AtomCluster("", single_atoms_objects, parentless=True)) - - for ce in atom_clusters: - atoms = sorted(ce.atom_list, key=operator.attrgetter("index")) - - n_atoms = len(atoms) - indexes = [at.index for at in atoms] - coords = conf.to_real_coordinates()[indexes, :] - cov_radii = np.zeros((n_atoms,), dtype=np.float64) - for i, at in enumerate(atoms): - cov_radii[i] = ATOMS_DATABASE.get_atom_property( - at.symbol.capitalize(), "covalent_radius" - ) - - bonds = fast_calculation.cpt_cluster_connectivity_nopbc( - coords, cov_radii, tolerance - ) - - for idx1, idx2 in bonds: - atoms[idx1].bonds.append(atoms[idx2]) - atoms[idx2].bonds.append(atoms[idx1]) - - def find_atoms_in_molecule( chemical_system: ChemicalSystem, entity_name: str, atom_names: list[str], - indexes: bool = False, -) -> list[list[Union[Atom, int]]]: - """ - Finds all chemical entities of the provided name within the chemical system, and then retrieves all atoms from each - of the found entities whose name matches one of the provided atom names. However, please note that only the chemical - entities directly registered in the chemical system are searched, i.e. the chemical_entities property of - :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem`. Therefore, for example, if a chemical system consists of a - Protein with name 'protein' which consists of 2 NucleotideChains 'chain1' and 'chain2', and this function is called - with the entity_name parameter set to 'chain1', an empty list will be returned. - - :param chemical_system: the chemical system to be searched - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` - - :param entity_name: the name of the chemical entity to match - :type entity_name: str - - :param atom_names: the list of atom names to search - :type atom_names: list - - :param indexes: if True the indexes of the atoms will be returned otherwise the Atom instances will be returned - :type indexes: bool - - :return: the list of indexes or atom instances found - :rtype: list - - :Example: - - >>> from MDANSE.Chemistry.ChemicalEntity import Molecule, ChemicalSystem - >>> - >>> cs = ChemicalSystem() - >>> - >>> molecules = [Molecule('WAT', 'water'), Molecule('WAT', 'water'), Molecule('WAT', 'totally not water')] - >>> for molecule in molecules: - >>> cs.add_chemical_entity(molecule) - >>> - >>> # Search for all hydrogen atoms in molecules called water - >>> find_atoms_in_molecule(cs, 'water', ['HW2', 'HW1']) - [[Atom(name='HW2'), Atom(name='HW1')], [Atom(name='HW2'), Atom(name='HW1')]] - >>> # Searching for atoms in molecules that do not exist returns an empty list - >>> find_atoms_in_molecule(cs, 'INVALID', ['HW1']) - [] - >>> # Searching for atoms that do not exist in the molecules of the specified name returns a list of empty lists - >>> find_atoms_in_molecule(cs, 'water', ['INVALID']) - [[], []] - >>> # Setting the indexes parameter to True causes indices to be returned instead of atom objects - >>> find_atoms_in_molecule(cs, 'water', ['HW2', 'HW1'], True) - [[1, 2], [4, 5]] - """ - - # Loop over the chemical entities of the chemical entity and keep only those whose name match |ce_name| - chemical_entities = [] - for ce in chemical_system.chemical_entities: - if ce.name == entity_name: - chemical_entities.append(ce) - - match = [] - for ce in chemical_entities: - atoms = ce.atom_list - names = [at.name for at in atoms] - try: - match.append([atoms[names.index(at_name)] for at_name in atom_names]) - except ValueError: - match.append([]) - - if indexes is True: - match = [[at.index for at in at_list] for at_list in match] - - return match - - -def get_chemical_objects_dict( - chemical_system: ChemicalSystem, -) -> dict[str, list[_ChemicalEntity]]: - """ - Maps all chemical entities in a chemical system to their names, and returns this as a dict. Please note that only - the top level chemical entities (those directly registered in chemical system) are mapped; children entities are not - mapped. - - :param chemical_system: the chemical system whose entities are to be retrieved - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` - - :return: a dict mapping the names of the entities in a chemical system to a list of entities with that name - :rtype: dict - - :Examples: - - >>> from MDANSE.Chemistry.ChemicalEntity import Molecule, ChemicalSystem - >>> - >>> compounds = [Molecule('WAT', 'water'), Molecule('WAT', 'water'), Molecule('WAT', 'dihydrogen monoxide'), Atom()] - >>> - >>> cs = ChemicalSystem() - >>> for compound in compounds: - >>> cs.add_chemical_entity(compound) - >>> - >>> # The atoms that are part of molecules (and so not registered with the chemical system directly) are not mapped - >>> get_chemical_objects_dict(cs) - {'water': [Molecule(name='water'), Molecule(name='water')], - 'dihydrogen monoxide': [Molecule(name='dihydrogen monoxide')], '': [Atom(name='')]} - """ - - d = {} - for ce in chemical_system.chemical_entities: - d.setdefault(ce.name, []).append(ce) - - return d - - -def group_atoms( - chemical_system: ChemicalSystem, groups: list[list[int]] -) -> list[AtomGroup]: - """ - Groups select atoms into :class: `MDANSE.Chemistry.ChemicalEntity.AtomGroup` objects according to the instructions - in the 'groups' argument. Please note, however, that the groups are created strictly according to this parameter, - meaning that not all atoms are necessarily placed into a group and that some atoms may be placed into multiple - groups, depending on the instructions. The only exception to this is if one of the lists in the 'groups' parameters - is empty, in which case no group is created for that list, the instruction silently ignored. - - :param chemical_system: the chemical system whose atoms are to be grouped - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` - - :param groups: the nested list of indexes, each sublist defining a group - :type groups: list - - :return: list of atom groups as specified - :rtype: list - - :Example: - - >>> from MDANSE.Chemistry.ChemicalEntity import Atom, Molecule, ChemicalSystem - >>> - >>> compounds = [Atom(), Molecule('WAT', ''), Molecule('WAT', ''), Molecule('WAT', '')] - >>> - >>> cs = ChemicalSystem() - >>> for compound in compounds: - >>> cs.add_chemical_entity(compound) - >>> - >>> # An atom group is created for each non-empty list in the provided list of lists - >>> groups = group_atoms(cs, [[0, 2], [], [3], [5, 7, 9]]) - >>> groups - [AtomGroup(), AtomGroup(), AtomGroup()] - >>> groups[0].atom_list - [Atom(name=''), Atom(name='HW2')] - >>> groups[1].atom_list - [Atom(name='HW1')] - >>> groups[2].atom_list - [Atom(name='HW2'), Atom(name='OW'), Atom(name='HW1')] - """ - atoms = chemical_system.atom_list - - groups = [AtomGroup([atoms[index] for index in gr]) for gr in groups if gr] - - return groups - - -def resolve_undefined_molecules_name(chemical_system: ChemicalSystem) -> None: - """ - Changes the names of all top-level chemical entities (those directly registered with a chemical system) that have no - name to their molecular formulae. To be considered to have no name, the molecule's name must be an empty string or a - string that consists of only spaces. The molecular formula is determined through - :func: `MDANSE.MolecularDynamics.TrajectoryUtils.resolve_undefined_molecules_name`. - - :param chemical_system: the chemical system - :type chemical_system: :class: `MDANSE.Chemistry.ChemicalEntity.ChemicalSystem` - - :return: None - - :Example: - - >>> from MDANSE.Chemistry.ChemicalEntity import Atom, Molecule, ChemicalSystem - >>> - >>> compounds = [Molecule('WAT', ''), Molecule('WAT', ' water '), Molecule('WAT', ' '), Atom()] - >>> - >>> # Alter the name of one of the atoms in a molecule to see that it will not be changed - >>> compounds[0]['OW'].name = '' - >>> - >>> cs = ChemicalSystem() - >>> for compound in compounds: - >>> cs.add_chemical_entity(compound) - >>> - >>> resolve_undefined_molecules_name(cs) - >>> cs.chemical_entities - [Molecule(name='H2O1'), Molecule(name=' water '), Molecule(name='H2O1'), Atom(name='H1')] - >>> compounds[0]['OW'].name - '' - """ - # Is it okay to not do this for children? - for ce in chemical_system.chemical_entities: - if not ce.name.strip(): - ce.name = brute_formula(ce, sep="") - - -def sorted_atoms( - atoms: list[Atom], attribute: str = None -) -> list[Union[Atom, float, int, str, list]]: - """ - Sort a list of atoms according to their index, and returns either the sorted list of atoms or the values of the - specified attribute. - - :param atoms: the atom list to be sorted - :type atoms: list - - :param attribute: if not None, return the attribute of the atom instead of the atom instance - :type attribute: str - - :return: the sorted atoms or the value of their specified attributes - :rtype: list - - :Example: - - >>> from MDANSE.Chemistry.ChemicalEntity import Atom - >>> - >>> atoms = [Atom() for _ in range(4)] - >>> for i, atom in enumerate(atoms): - >>> atom.index = i - >>> atom.name = f'atom{i}' - >>> - >>> sorted_atoms([atoms[0], atoms[3], atoms[2], atoms[1]]) - [Atom(name='atom0'), Atom(name='atom3'), Atom(name='atom2'), Atom(name='atom1')] - >>> sorted_atoms([atoms[0], atoms[3], atoms[2], atoms[1]], 'name') - ['atom0', 'atom3', 'atom2', 'atom1'] - """ - - atoms = sorted(atoms, key=operator.attrgetter("index")) + indices: bool = False, +) -> list[list[int]]: + + if entity_name not in chemical_system._clusters: + return [] + + result = [] + for index_list in chemical_system._clusters[entity_name]: + if indices: + result.append( + [ + chemical_system._atom_indices[index] + for index in index_list + if chemical_system.atom_list[index] in atom_names + ] + ) + else: + result.append( + [ + chemical_system.atom_list[index] + for index in index_list + if chemical_system.atom_list[index] in atom_names + ] + ) - if attribute is None: - return atoms - else: - return [getattr(at, attribute) for at in atoms] + return result def atomic_trajectory(config, cell, rcell, box_coordinates=False): diff --git a/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py b/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py index e5d252c84e..2684e54bfe 100644 --- a/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py +++ b/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py @@ -14,6 +14,7 @@ # along with this program. If not, see . # +from typing import List import os import numpy as np @@ -21,15 +22,15 @@ from MDANSE.MLogging import LOG from MDANSE.Framework.Units import measure +from MDANSE.Mathematics.Geometry import center_of_mass from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem -from MDANSE.Extensions import com_trajectory +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Configuration import ( PeriodicRealConfiguration, RealConfiguration, + contiguous_coordinates_real, ) from MDANSE.MolecularDynamics.TrajectoryUtils import ( - resolve_undefined_molecules_name, atomic_trajectory, ) from MDANSE.MolecularDynamics.UnitCell import UnitCell @@ -62,7 +63,7 @@ def __init__(self, h5_filename): self._chemical_system = ChemicalSystem( os.path.splitext(os.path.basename(self._h5_filename))[0] ) - self._chemical_system.from_element_list(chemical_elements) + self._chemical_system.initialise_atoms(chemical_elements) # Load all the unit cells self._load_unit_cells() @@ -78,15 +79,6 @@ def __init__(self, h5_filename): pos_unit = "ang" conv_factor = measure(1.0, pos_unit).toval("nm") coords *= conv_factor - if self._unit_cells: - unit_cell = self._unit_cells[0] - conf = PeriodicRealConfiguration(self._chemical_system, coords, unit_cell) - else: - conf = RealConfiguration(self._chemical_system, coords) - self._chemical_system.configuration = conf - - # Define a default name for all chemical entities which have no name - resolve_undefined_molecules_name(self._chemical_system) self._variables_to_skip = [] @@ -95,12 +87,12 @@ def file_is_right(self, filename): result = True try: temp = h5py.File(filename) - except: + except FileNotFoundError: result = False else: try: temp["h5md"] - except: + except KeyError: result = False return result @@ -324,12 +316,12 @@ def __len__(self): return grp.shape[0] def read_com_trajectory( - self, atoms, first=0, last=None, step=1, box_coordinates=False + self, atom_indices, first=0, last=None, step=1, box_coordinates=False ): """Build the trajectory of the center of mass of a set of atoms. :param atoms: the atoms for which the center of mass should be computed - :type atoms: list MDANSE.Chemistry.ChemicalEntity.Atom + :type atoms: list MDANSE.Chemistry.ChemicalSystem.Atom :param first: the index of the first frame :type first: int :param last: the index of the last frame @@ -346,19 +338,33 @@ def read_com_trajectory( if last is None: last = len(self) - indexes = [at.index for at in atoms] + if len(atom_indices) == 1: + return self.read_atomic_trajectory( + atom_indices[0], + first=first, + last=last, + step=step, + box_coordinates=box_coordinates, + ) + + atoms = self.chemical_system.atom_list + try: - masses = self._h5_file["/particles/all/mass/value"][:].astype(np.float64) + masses = self._h5_file["/particles/all/mass/value"][atom_indices].astype( + np.float64 + ) except KeyError: try: - masses = self._h5_file["/particles/all/mass"][:].astype(np.float64) + masses = self._h5_file["/particles/all/mass"][atom_indices].astype( + np.float64 + ) except KeyError: masses = np.array( [ - ATOMS_DATABASE.get_atom_property(at.symbol, "atomic_weight") + ATOMS_DATABASE.get_atom_property(at, "atomic_weight") for at in atoms ] - ) + )[atom_indices] grp = self._h5_file["/particles/all/position/value"] try: pos_unit = self._h5_file["/particles/all/position/value"].attrs["unit"] @@ -369,50 +375,37 @@ def read_com_trajectory( pos_unit = "ang" conv_factor = measure(1.0, pos_unit).toval("nm") - coords = grp[first:last:step, :, :].astype(np.float64) * conv_factor + coords = grp[first:last:step, atom_indices, :].astype(np.float64) * conv_factor if coords.ndim == 2: coords = coords[np.newaxis, :, :] if self._unit_cells is not None: direct_cells = np.array( - [ - self.unit_cell(nf).transposed_direct - for nf in range(first, last, step) - ] + [self.unit_cell(nf).direct for nf in range(first, last, step)] ) inverse_cells = np.array( - [ - self.unit_cell(nf).transposed_inverse - for nf in range(first, last, step) - ] + [self.unit_cell(nf).inverse for nf in range(first, last, step)] ) - - top_lvl_chemical_entities = set( - [at.top_level_chemical_entity for at in atoms] - ) - top_lvl_chemical_entities_indexes = [ - [at.index for at in e.atom_list] for e in top_lvl_chemical_entities - ] - bonds = {} - for e in top_lvl_chemical_entities: - for at in e.atom_list: - bonds[at.index] = [other_at.index for other_at in at.bonds] - - com_traj = com_trajectory.com_trajectory( + temp_coords = contiguous_coordinates_real( coords, direct_cells, inverse_cells, - masses, - top_lvl_chemical_entities_indexes, - indexes, - bonds, - box_coordinates=box_coordinates, + [list(range(len(coords)))], + bring_to_centre=True, + ) + com_coords = np.vstack( + [ + center_of_mass(temp_coords[tstep], masses) + for tstep in range(len(temp_coords)) + ] ) + com_traj = atomic_trajectory(com_coords, direct_cells, inverse_cells) + else: com_traj = np.sum( - coords[:, indexes, :] * masses[np.newaxis, :, np.newaxis], axis=1 + coords[:, atom_indices, :] * masses[np.newaxis, :, np.newaxis], axis=1 ) com_traj /= np.sum(masses) @@ -553,12 +546,21 @@ def has_variable(self, variable: str) -> bool: else: return False + def get_atom_property(self, atom_symbol: str, property: str): + return ATOMS_DATABASE.get_atom_property(atom_symbol, property) + + def atoms_in_database(self) -> List[str]: + return ATOMS_DATABASE.atoms + + def properties_in_database(self) -> List[str]: + return ATOMS_DATABASE.properties + @property def chemical_system(self): """Return the chemical system stored in the trajectory. :return: the chemical system - :rtype: MDANSE.Chemistry.ChemicalEntity.ChemicalSystem + :rtype: MDANSE.Chemistry.ChemicalSystem.ChemicalSystem """ return self._chemical_system diff --git a/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py b/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py index 0dd69910ad..eab9d9dd8e 100644 --- a/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py +++ b/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py @@ -14,20 +14,22 @@ # along with this program. If not, see . # import os +from typing import List +import traceback import numpy as np import h5py from MDANSE.MLogging import LOG from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem -from MDANSE.Extensions import com_trajectory +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem +from MDANSE.Mathematics.Geometry import center_of_mass from MDANSE.MolecularDynamics.Configuration import ( PeriodicRealConfiguration, RealConfiguration, + contiguous_coordinates_real, ) from MDANSE.MolecularDynamics.TrajectoryUtils import ( - resolve_undefined_molecules_name, atomic_trajectory, ) from MDANSE.MolecularDynamics.UnitCell import UnitCell @@ -52,40 +54,32 @@ def __init__(self, h5_filename): # Load the chemical system self._chemical_system = ChemicalSystem( - os.path.splitext(os.path.basename(self._h5_filename))[0] + os.path.splitext(os.path.basename(self._h5_filename))[0], self ) - self._chemical_system.load(self._h5_filename) + self._chemical_system.load(self._h5_file) # Load all the unit cells self._load_unit_cells() - # Load the first configuration - coords = self._h5_file["/configuration/coordinates"][0, :, :] - if self._unit_cells: - unit_cell = self._unit_cells[0] - conf = PeriodicRealConfiguration(self._chemical_system, coords, unit_cell) - else: - conf = RealConfiguration(self._chemical_system, coords) - self._chemical_system.configuration = conf - - # Define a default name for all chemical entities which have no name - resolve_undefined_molecules_name(self._chemical_system) - @classmethod def file_is_right(self, filename): result = True try: - temp = h5py.File(filename) - except: + file_object = h5py.File(filename) + except FileNotFoundError: result = False else: try: temp_cs = ChemicalSystem( os.path.splitext(os.path.basename(filename))[0] ) - temp_cs.load(filename) - except: + temp_cs.load(file_object) + except Exception: + LOG.warning( + f"Could not load ChemicalSystem from {filename}. MDANSE will try to read it as H5MD next." + ) result = False + file_object.close() return result def close(self): @@ -241,12 +235,12 @@ def __len__(self): return grp["coordinates"].shape[0] def read_com_trajectory( - self, atoms, first=0, last=None, step=1, box_coordinates=False + self, atom_indices, first=0, last=None, step=1, box_coordinates=False ): """Build the trajectory of the center of mass of a set of atoms. :param atoms: the atoms for which the center of mass should be computed - :type atoms: list MDANSE.Chemistry.ChemicalEntity.Atom + :type atoms: list MDANSE.Chemistry.ChemicalSystem.Atom :param first: the index of the first frame :type first: int :param last: the index of the last frame @@ -263,49 +257,58 @@ def read_com_trajectory( if last is None: last = len(self) - indexes = [at.index for at in atoms] - masses = np.array( - [ - ATOMS_DATABASE.get_atom_property(at.symbol, "atomic_weight") - for at in atoms - ] - ) + if len(atom_indices) == 1: + return self.read_atomic_trajectory( + atom_indices[0], + first=first, + last=last, + step=step, + box_coordinates=box_coordinates, + ) + + try: + masses = self.chemical_system.atom_property("atomic_weight") + except KeyError: + masses = np.array( + [ + ATOMS_DATABASE.get_atom_property(at, "atomic_weight") + for at in self.chemical_system.atom_list + ] + ) + masses = [masses[index] for index in atom_indices] grp = self._h5_file["/configuration"] - coords = grp["coordinates"][first:last:step, :, :].astype(np.float64) + coords = grp["coordinates"][first:last:step, atom_indices, :].astype(np.float64) if coords.ndim == 2: coords = coords[np.newaxis, :, :] if self._unit_cells is not None: - direct_cells = np.array([uc.transposed_direct for uc in self._unit_cells]) - inverse_cells = np.array([uc.transposed_inverse for uc in self._unit_cells]) - - top_lvl_chemical_entities = set( - [at.top_level_chemical_entity for at in atoms] + direct_cells = np.array( + [uc.direct for uc in self._unit_cells[first:last:step]] ) - top_lvl_chemical_entities_indexes = [ - [at.index for at in e.atom_list] for e in top_lvl_chemical_entities - ] - bonds = {} - for e in top_lvl_chemical_entities: - for at in e.atom_list: - bonds[at.index] = [other_at.index for other_at in at.bonds] - - com_traj = com_trajectory.com_trajectory( + inverse_cells = np.array( + [uc.inverse for uc in self._unit_cells[first:last:step]] + ) + temp_coords = contiguous_coordinates_real( coords, direct_cells, inverse_cells, - masses, - top_lvl_chemical_entities_indexes, - indexes, - bonds, - box_coordinates=box_coordinates, + [list(range(len(coords)))], + bring_to_centre=True, ) + com_coords = np.vstack( + [ + center_of_mass(temp_coords[tstep], masses) + for tstep in range(len(temp_coords)) + ] + ) + + com_traj = atomic_trajectory(com_coords, direct_cells, inverse_cells) else: com_traj = np.sum( - coords[:, indexes, :] * masses[np.newaxis, :, np.newaxis], axis=1 + coords[:, atom_indices, :] * masses[np.newaxis, :, np.newaxis], axis=1 ) com_traj /= np.sum(masses) @@ -438,12 +441,61 @@ def has_variable(self, variable: str) -> bool: else: return False + def get_atom_property(self, symbol: str, property: str): + if "atom_database" not in self._h5_file: + return ATOMS_DATABASE.get_atom_property(symbol, property) + elif symbol not in self._h5_file["/atom_database"]: + return ATOMS_DATABASE.get_atom_property(symbol, property) + temp = np.where( + self._h5_file["/atom_database/property_labels"][:] + == property.encode("utf-8") + )[0] + if len(temp) == 0: + if property == "dummy": + try: + return ATOMS_DATABASE.get_atom_property(symbol, property) + except KeyError: + if ( + "_" in symbol + ): # this is most likely an artificial atom from a molecule + return 0 # the molecule atoms are not dummy + else: + raise KeyError( + f"Property {property} is not in the trajectory's internal database." + ) + index = temp.flatten()[0] + data_type = self._h5_file["/atom_database/property_types"][index] + value = self._h5_file[f"/atom_database/{symbol}"][index] + if data_type == b"int": + return int(value) + if property == "color": + num1 = round(value // 0x10000) + num2 = round((value - num1 * 0x10000) // 0x100) + num3 = round((value - num1 * 0x10000 - num2 * 0x100)) + return ";".join([str(int(x)) for x in [num1, num2, num3]]) + return value + + def atoms_in_database(self) -> List[str]: + if "atom_database" not in self._h5_file: + return ATOMS_DATABASE.atoms + else: + return list(self._h5_file["/atom_database"].keys()) + + def properties_in_database(self) -> List[str]: + if "atom_database" not in self._h5_file: + return ATOMS_DATABASE.properties + else: + return list( + label.decode("utf-8") + for label in self._h5_file["/atom_database/property_labels"] + ) + @property def chemical_system(self): """Return the chemical system stored in the trajectory. :return: the chemical system - :rtype: MDANSE.Chemistry.ChemicalEntity.ChemicalSystem + :rtype: MDANSE.Chemistry.ChemicalSystem.ChemicalSystem """ return self._chemical_system diff --git a/MDANSE/Src/MDANSE/mdtraj/__init__.py b/MDANSE/Src/MDANSE/mdtraj/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/MDANSE/Src/MDANSE/mdtraj/utils.py b/MDANSE/Src/MDANSE/mdtraj/utils.py deleted file mode 100644 index 75b583f587..0000000000 --- a/MDANSE/Src/MDANSE/mdtraj/utils.py +++ /dev/null @@ -1,188 +0,0 @@ -############################################################################## -# MDTraj: A Python Library for Loading, Saving, and Manipulating -# Molecular Dynamics Trajectories. -# Copyright 2012-2013 Stanford University and the Authors -# -# Authors: Robert McGibbon -# Contributors: -# -# MDTraj is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as -# published by the Free Software Foundation, either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with MDTraj. If not, see . -############################################################################## - - -############################################################################## -# imports -############################################################################## - -import collections -import numbers -import warnings -from itertools import zip_longest - -import numpy as np - - -class TypeCastPerformanceWarning(RuntimeWarning): - pass - - -def ensure_type( - val, - dtype, - ndim, - name, - length=None, - can_be_none=False, - shape=None, - warn_on_cast=True, - add_newaxis_on_deficient_ndim=False, -): - """Typecheck the size, shape and dtype of a numpy array, with optional - casting. - - Parameters - ---------- - val : {np.ndaraay, None} - The array to check - dtype : {nd.dtype, str} - The dtype you'd like the array to have - ndim : int - The number of dimensions you'd like the array to have - name : str - name of the array. This is used when throwing exceptions, so that - we can describe to the user which array is messed up. - length : int, optional - How long should the array be? - can_be_none : bool - Is ``val == None`` acceptable? - shape : tuple, optional - What should be shape of the array be? If the provided tuple has - Nones in it, those will be semantically interpreted as matching - any length in that dimension. So, for example, using the shape - spec ``(None, None, 3)`` will ensure that the last dimension is of - length three without constraining the first two dimensions - warn_on_cast : bool, default=True - Raise a warning when the dtypes don't match and a cast is done. - add_newaxis_on_deficient_ndim : bool, default=True - Add a new axis to the beginining of the array if the number of - dimensions is deficient by one compared to your specification. For - instance, if you're trying to get out an array of ``ndim == 3``, - but the user provides an array of ``shape == (10, 10)``, a new axis will - be created with length 1 in front, so that the return value is of - shape ``(1, 10, 10)``. - - Notes - ----- - The returned value will always be C-contiguous. - - Returns - ------- - typechecked_val : np.ndarray, None - If `val=None` and `can_be_none=True`, then this will return None. - Otherwise, it will return val (or a copy of val). If the dtype wasn't right, - it'll be casted to the right shape. If the array was not C-contiguous, it'll - be copied as well. - - """ - if can_be_none and val is None: - return None - - if not isinstance(val, np.ndarray): - if isinstance(val, collections.abc.Iterable): - # If they give us an iterator, let's try... - if isinstance(val, collections.abc.Sequence): - # sequences are easy. these are like lists and stuff - val = np.array(val, dtype=dtype) - else: - # this is a generator... - val = np.array(list(val), dtype=dtype) - elif np.isscalar(val) and add_newaxis_on_deficient_ndim and ndim == 1: - # special case: if the user is looking for a 1d array, and - # they request newaxis upconversion, and provided a scalar - # then we should reshape the scalar to be a 1d length-1 array - val = np.array([val]) - else: - raise TypeError( - f"{name} must be numpy array. You supplied type {type(val)}" - ) - - if warn_on_cast and val.dtype != dtype: - warnings.warn( - f"Casting {name} dtype={val.dtype} to {dtype} ", - TypeCastPerformanceWarning, - ) - - if not val.ndim == ndim: - if add_newaxis_on_deficient_ndim and val.ndim + 1 == ndim: - val = val[np.newaxis, ...] - else: - raise ValueError(f"{name} must be ndim {ndim}. You supplied {val.ndim}") - - val = np.ascontiguousarray(val, dtype=dtype) - - if length is not None and len(val) != length: - raise ValueError(f"{name} must be length {length}. You supplied {len(val)}") - - if shape is not None: - # the shape specified given by the user can look like (None, None 3) - # which indicates that ANY length is accepted in dimension 0 or - # dimension 1 - sentenel = object() - error = ValueError( - "{} must be shape {}. You supplied " - "{}".format(name, str(shape).replace("None", "Any"), val.shape), - ) - for a, b in zip_longest(val.shape, shape, fillvalue=sentenel): - if a is sentenel or b is sentenel: - # if the sentenel was reached, it means that the ndim didn't - # match or something. this really shouldn't happen - raise error - if b is None: - # if the user's shape spec has a None in it, it matches anything - continue - if a != b: - # check for equality - raise error - - return val - - -def cast_indices(indices): - """Check that ``indices`` are appropriate for indexing an array - - Parameters - ---------- - indices : {None, array_like, slice} - If indices is None or slice, it'll just pass through. Otherwise, it'll - be converted to a numpy array and checked to make sure it contains - unique integers. - - Returns - ------- - value : {slice, np.ndarray} - Either a slice or an array of integers, depending on the input type - """ - if indices is None or isinstance(indices, slice): - return indices - - if not len(indices) == len(set(indices)): - raise ValueError("indices must be unique.") - - out = np.asarray(indices) - if not issubclass(out.dtype.type, np.integer): - raise ValueError( - "indices must be of an integer type. %s is not an integer type" % out.dtype - ) - - return out diff --git a/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py b/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py index ea71193886..23923a54a5 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py @@ -23,11 +23,12 @@ "Ar_mdmc_h5md.h5", ) - -@pytest.fixture(scope="module") -def trajectory(): - trajectory = HDFTrajectoryInputData(short_traj) - yield trajectory +com_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "com_trajectory.mdt", +) @pytest.mark.parametrize( @@ -41,7 +42,7 @@ def trajectory(): (3, False), ], ) -def test_vacf(trajectory, interp_order, normalise): +def test_vacf(interp_order, normalise): temp_name = tempfile.mktemp() parameters = {} parameters["frames"] = (0, 10, 1, 5) @@ -60,7 +61,7 @@ def test_vacf(trajectory, interp_order, normalise): os.remove(temp_name + ".log") -def test_pps(trajectory): +def test_pps(): temp_name = tempfile.mktemp() parameters = {} parameters["frames"] = (0, 10, 1, 5) @@ -98,7 +99,7 @@ def parameters(): }, ) parameters["q_values"] = (0.0, 10.0, 0.1) - parameters["r_values"] = (0.0, 1.0, 0.01) + parameters["r_values"] = (0.0, 0.5, 0.01) parameters["per_axis"] = False parameters["reference_direction"] = (0, 0, 1) parameters["instrument_resolution"] = ("Gaussian", {"sigma": 1.0, "mu": 0.0}) @@ -112,7 +113,7 @@ def parameters(): total_list = [] -for tp in [short_traj, mdmc_traj]: +for tp in [short_traj, mdmc_traj, com_traj]: for jt in [ # "AngularCorrelation", # "GeneralAutoCorrelationFunction", diff --git a/MDANSE/Tests/UnitTests/Analysis/test_infrared.py b/MDANSE/Tests/UnitTests/Analysis/test_infrared.py index b9cfece313..9e74f36a4d 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_infrared.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_infrared.py @@ -24,7 +24,7 @@ def test_dacf_analysis(): parameters["running_mode"] = ("single-core", 1) parameters["trajectory"] = short_traj parameters["atom_charges"] = "{}" - parameters["molecule_name"] = "InChI=1S/CO2/c2-1-3" + parameters["molecule_name"] = "C1_O2" job = IJob.create("DipoleAutoCorrelationFunction") job.run(parameters, status=True) assert path.exists(temp_name + ".mda") @@ -45,7 +45,7 @@ def test_ir_analysis(): parameters["running_mode"] = ("single-core", 1) parameters["trajectory"] = short_traj parameters["atom_charges"] = "{}" - parameters["molecule_name"] = "InChI=1S/CO2/c2-1-3" + parameters["molecule_name"] = "C1_O2" job = IJob.create("Infrared") job.run(parameters, status=True) assert path.exists(temp_name + ".mda") diff --git a/MDANSE/Tests/UnitTests/Analysis/test_molecule_names.py b/MDANSE/Tests/UnitTests/Analysis/test_molecule_names.py index 1d80818a6e..3db2e6f5aa 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_molecule_names.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_molecule_names.py @@ -39,7 +39,7 @@ def parameters(): parameters["q_values"] = (0.0, 10.0, 0.1) parameters["r_values"] = (0.0, 10.0, 0.1) parameters["per_axis"] = False - parameters["molecule_name"] = "InChI=1S/CO2/c2-1-3" + parameters["molecule_name"] = "C1_O2" parameters["axis"] = "ab" parameters["reference_direction"] = (0, 0, 1) parameters["instrument_resolution"] = ("Gaussian", {"sigma": 1.0, "mu": 0.0}) diff --git a/MDANSE/Tests/UnitTests/Analysis/test_qvectors.py b/MDANSE/Tests/UnitTests/Analysis/test_qvectors.py index f6da4b2f35..4f3ed99aa2 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_qvectors.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_qvectors.py @@ -36,7 +36,7 @@ def test_disf(trajectory): parameters["trajectory"] = short_traj parameters["weights"] = "b_incoherent2" for qvector_generator in IQVectors.indirect_subclasses(): - instance = IQVectors.create(qvector_generator, trajectory.chemical_system) + instance = IQVectors.create(qvector_generator, trajectory._data.configuration()) qvector_defaults = { name: value[1]["default"] for name, value in instance.settings.items() } diff --git a/MDANSE/Tests/UnitTests/Analysis/test_scattering.py b/MDANSE/Tests/UnitTests/Analysis/test_scattering.py index 4c1f15609f..b26d37c00d 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_scattering.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_scattering.py @@ -16,15 +16,16 @@ "short_trajectory_after_changes.mdt", ) - -@pytest.fixture(scope="module") -def trajectory(): - trajectory = HDFTrajectoryInputData(short_traj) - yield trajectory +com_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "com_trajectory.mdt", +) @pytest.fixture(scope="module") -def qvector_spherical_lattice(trajectory): +def qvector_spherical_lattice(): return ( "SphericalLatticeQVectors", {"seed": 0, "shells": (5.0, 36, 10.0), "n_vectors": 10, "width": 9.0}, @@ -75,6 +76,10 @@ def disf(): os.remove(temp_name + ".mda") +@pytest.mark.parametrize( + "trajectory", + [short_traj, com_traj], +) def test_dcsf(trajectory, qvector_spherical_lattice): temp_name = tempfile.mktemp() parameters = {} @@ -85,7 +90,7 @@ def test_dcsf(trajectory, qvector_spherical_lattice): parameters["output_files"] = (temp_name, ("MDAFormat", "TextFormat"), "INFO") parameters["q_vectors"] = qvector_spherical_lattice parameters["running_mode"] = ("single-core",) - parameters["trajectory"] = short_traj + parameters["trajectory"] = trajectory parameters["weights"] = "b_coherent" dcsf = IJob.create("DynamicCoherentStructureFactor") dcsf.run(parameters, status=True) @@ -119,6 +124,10 @@ def test_output_axis_preview(qvector_spherical_lattice): assert len(axes) == 3 # two configurators return valid arrays +@pytest.mark.parametrize( + "trajectory", + [short_traj, com_traj], +) def test_disf(trajectory, qvector_spherical_lattice): temp_name = tempfile.mktemp() parameters = {} @@ -129,7 +138,7 @@ def test_disf(trajectory, qvector_spherical_lattice): parameters["output_files"] = (temp_name, ("MDAFormat", "TextFormat"), "INFO") parameters["q_vectors"] = qvector_spherical_lattice parameters["running_mode"] = ("single-core",) - parameters["trajectory"] = short_traj + parameters["trajectory"] = trajectory parameters["weights"] = "b_incoherent2" disf = IJob.create("DynamicIncoherentStructureFactor") disf.run(parameters, status=True) @@ -144,6 +153,10 @@ def test_disf(trajectory, qvector_spherical_lattice): os.remove(temp_name + ".log") +@pytest.mark.parametrize( + "trajectory", + [short_traj, com_traj], +) def test_eisf(trajectory, qvector_spherical_lattice): temp_name = tempfile.mktemp() parameters = {} @@ -153,7 +166,7 @@ def test_eisf(trajectory, qvector_spherical_lattice): parameters["output_files"] = (temp_name, ("MDAFormat", "TextFormat"), "INFO") parameters["q_vectors"] = qvector_spherical_lattice parameters["running_mode"] = ("single-core",) - parameters["trajectory"] = short_traj + parameters["trajectory"] = trajectory parameters["weights"] = "b_incoherent" eisf = IJob.create("ElasticIncoherentStructureFactor") eisf.run(parameters, status=True) @@ -168,6 +181,10 @@ def test_eisf(trajectory, qvector_spherical_lattice): os.remove(temp_name + ".log") +@pytest.mark.parametrize( + "trajectory", + [short_traj, com_traj], +) def test_gdisf(trajectory): temp_name = tempfile.mktemp() parameters = {} @@ -178,7 +195,7 @@ def test_gdisf(trajectory): parameters["output_files"] = (temp_name, ("MDAFormat", "TextFormat"), "INFO") parameters["q_shells"] = (2.0, 12.2, 2.0) parameters["running_mode"] = ("single-core",) - parameters["trajectory"] = short_traj + parameters["trajectory"] = trajectory parameters["weights"] = "b_incoherent2" gdisf = IJob.create("GaussianDynamicIncoherentStructureFactor") gdisf.run(parameters, status=True) diff --git a/MDANSE/Tests/UnitTests/Analysis/test_structure.py b/MDANSE/Tests/UnitTests/Analysis/test_structure.py index e892a90e26..372ecc6272 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_structure.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_structure.py @@ -22,6 +22,13 @@ "Ar_mdmc_h5md.h5", ) +com_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "com_trajectory.mdt", +) + ################################################################ # Job parameters # @@ -58,21 +65,28 @@ def parameters(): total_list = [] -for tp in (short_traj, mdmc_traj): +for tp in (short_traj, mdmc_traj, com_traj): for jt in [ "RadiusOfGyration", - "SolventAccessibleSurface", - "RootMeanSquareDeviation", - "RootMeanSquareFluctuation", "DensityProfile", "MolecularTrace", - "Voronoi", "Eccentricity", ]: for rm in [("single-core", 1), ("multicore", -4)]: for of in ["MDAFormat", "TextFormat"]: total_list.append((tp, jt, rm, of)) +for tp in (short_traj, mdmc_traj): + for jt in [ + "RootMeanSquareDeviation", + "RootMeanSquareFluctuation", + "SolventAccessibleSurface", + "Voronoi", + ]: + for rm in [("single-core", 1)]: + for of in ["MDAFormat"]: + total_list.append((tp, jt, rm, of)) + @pytest.mark.parametrize("traj_path,job_type,running_mode,output_format", total_list) def test_structure_analysis( diff --git a/MDANSE/Tests/UnitTests/Analysis/test_thermodynamics.py b/MDANSE/Tests/UnitTests/Analysis/test_thermodynamics.py index be5cbb00e5..734055a8c9 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_thermodynamics.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_thermodynamics.py @@ -17,16 +17,19 @@ "Data", "short_trajectory_after_changes.mdt", ) +com_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "com_trajectory.mdt", +) -@pytest.fixture(scope="module") -def trajectory(): - trajectory = HDFTrajectoryInputData(short_traj) - yield trajectory - - -@pytest.mark.parametrize("interp_order", [1, 2, 3]) -def test_temperature(trajectory, interp_order): +@pytest.mark.parametrize( + "trajectory_name,interp_order", + [(short_traj, 1), (short_traj, 3), (com_traj, 1), (com_traj, 3)], +) +def test_temperature(trajectory_name, interp_order): temp_name = tempfile.mktemp() parameters = {} parameters["frames"] = (0, 10, 1) @@ -44,8 +47,11 @@ def test_temperature(trajectory, interp_order): os.remove(temp_name + ".log") -@pytest.mark.parametrize("interp_order", [1, 2, 3]) -def test_temperature_nonzero(trajectory, interp_order): +@pytest.mark.parametrize( + "trajectory_name,interp_order", + [(short_traj, 1), (short_traj, 3), (com_traj, 1), (com_traj, 3)], +) +def test_temperature_nonzero(trajectory_name, interp_order): temp_name = tempfile.mktemp() parameters = {} parameters["frames"] = (0, 10, 1) @@ -62,8 +68,16 @@ def test_temperature_nonzero(trajectory, interp_order): assert np.all(temperature > 0.0) -@pytest.mark.parametrize("output_format", ["MDAFormat", "TextFormat"]) -def test_density(trajectory, output_format): +@pytest.mark.parametrize( + "trajectory_name,output_format", + [ + (short_traj, "MDAFormat"), + (com_traj, "MDAFormat"), + (short_traj, "TextFormat"), + (com_traj, "TextFormat"), + ], +) +def test_density(trajectory_name, output_format): temp_name = tempfile.mktemp() parameters = {} parameters["frames"] = (0, 10, 1) diff --git a/MDANSE/Tests/UnitTests/AtomMapping/test_atom_mapping.py b/MDANSE/Tests/UnitTests/AtomMapping/test_atom_mapping.py index 5ab015fcd4..da105638e7 100644 --- a/MDANSE/Tests/UnitTests/AtomMapping/test_atom_mapping.py +++ b/MDANSE/Tests/UnitTests/AtomMapping/test_atom_mapping.py @@ -158,7 +158,10 @@ def test_check_mapping_valid_as_elements_are_correct_5(): def test_check_mapping_valid_as_elements_are_correct_6(): - labels = [AtomLabel("label=1", molecule="mol=1"), AtomLabel("C=1", molecule="mol=2")] + labels = [ + AtomLabel("label=1", molecule="mol=1"), + AtomLabel("C=1", molecule="mol=2"), + ] mapping = {"molecule=mol1": {"label1": "C"}, "molecule=mol2": {"C1": "C"}} assert check_mapping_valid(mapping, labels) diff --git a/MDANSE/Tests/UnitTests/AtomSelector/test_all_selector.py b/MDANSE/Tests/UnitTests/AtomSelector/test_all_selector.py index 186267d514..51c798b98d 100644 --- a/MDANSE/Tests/UnitTests/AtomSelector/test_all_selector.py +++ b/MDANSE/Tests/UnitTests/AtomSelector/test_all_selector.py @@ -1,21 +1,20 @@ import os import pytest -from MDANSE.IO.PDBReader import PDBReader +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.Framework.AtomSelector.all_selector import select_all -pbd_2vb1 = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.pdb" +traj_2vb1 = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.mdt" ) @pytest.fixture(scope="module") -def protein_chemical_system(): - reader = PDBReader(pbd_2vb1) - protein_chemical_system = reader.build_chemical_system() - return protein_chemical_system +def protein_trajectory(): + protein_trajectory = HDFTrajectoryInputData(traj_2vb1) + return protein_trajectory.trajectory -def test_select_all_returns_correct_number_of_atoms_matches(protein_chemical_system): - selection = select_all(protein_chemical_system) +def test_select_all_returns_correct_number_of_atoms_matches(protein_trajectory): + selection = select_all(protein_trajectory) assert len(selection) == 30714 diff --git a/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py b/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py index f5047c2fb6..0269df8528 100644 --- a/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py +++ b/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py @@ -1,6 +1,6 @@ import os import pytest -from MDANSE.IO.PDBReader import PDBReader +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.Framework.AtomSelector.atom_selectors import ( select_element, select_hs_on_heteroatom, @@ -9,83 +9,82 @@ ) -pbd_2vb1 = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.pdb" +traj_2vb1 = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.mdt" ) @pytest.fixture(scope="module") -def protein_chemical_system(): - reader = PDBReader(pbd_2vb1) - protein_chemical_system = reader.build_chemical_system() - return protein_chemical_system +def protein_trajectory(): + protein_trajectory = HDFTrajectoryInputData(traj_2vb1) + return protein_trajectory.trajectory def test_select_element_returns_true_as_match_exist( - protein_chemical_system, + protein_trajectory, ): - exists = select_element(protein_chemical_system, "S", check_exists=True) + exists = select_element(protein_trajectory, "S", check_exists=True) assert exists def test_select_element_returns_false_as_match_does_not_exist( - protein_chemical_system, + protein_trajectory, ): - exists = select_element(protein_chemical_system, "Si", check_exists=True) + exists = select_element(protein_trajectory, "Si", check_exists=True) assert not exists def test_select_element_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_element(protein_chemical_system, "S") + selection = select_element(protein_trajectory, "S") assert len(selection) == 10 def test_select_hs_on_carbon_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_hs_on_element(protein_chemical_system, "C") + selection = select_hs_on_element(protein_trajectory, "C") assert len(selection) == 696 def test_select_hs_on_nitrogen_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_hs_on_element(protein_chemical_system, "N") + selection = select_hs_on_element(protein_trajectory, "N") assert len(selection) == 243 def test_select_hs_on_oxygen_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_hs_on_element(protein_chemical_system, "O") + selection = select_hs_on_element(protein_trajectory, "O") assert len(selection) == 19184 def test_select_hs_on_sulfur_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_hs_on_element(protein_chemical_system, "S") + selection = select_hs_on_element(protein_trajectory, "S") assert len(selection) == 0 def test_select_hs_on_silicon_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_hs_on_element(protein_chemical_system, "Si") + selection = select_hs_on_element(protein_trajectory, "Si") assert len(selection) == 0 def test_select_hs_on_heteroatom_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_hs_on_heteroatom(protein_chemical_system) + selection = select_hs_on_heteroatom(protein_trajectory) assert len(selection) == 19427 def test_select_dummy_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_dummy(protein_chemical_system) + selection = select_dummy(protein_trajectory) assert len(selection) == 0 diff --git a/MDANSE/Tests/UnitTests/AtomSelector/test_group_selectors.py b/MDANSE/Tests/UnitTests/AtomSelector/test_group_selectors.py index 38c359b26d..9fb9e9382e 100644 --- a/MDANSE/Tests/UnitTests/AtomSelector/test_group_selectors.py +++ b/MDANSE/Tests/UnitTests/AtomSelector/test_group_selectors.py @@ -1,42 +1,40 @@ import os import pytest -from MDANSE.IO.PDBReader import PDBReader +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.Framework.AtomSelector.group_selectors import ( select_primary_amine, select_hydroxy, - select_methly, + select_methyl, select_phosphate, select_sulphate, select_thiol, ) -pbd_2vb1 = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.pdb" +traj_2vb1 = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.mdt" ) -pbd_1gip = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "..", "Data", "1gip.pdb" +traj_1gip = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "Data", "1gip.mdt" ) @pytest.fixture(scope="module") -def protein_chemical_system(): - reader = PDBReader(pbd_2vb1) - protein_chemical_system = reader.build_chemical_system() - return protein_chemical_system +def protein_trajectory(): + protein_trajectory = HDFTrajectoryInputData(traj_2vb1) + return protein_trajectory.trajectory @pytest.fixture(scope="module") def nucleic_acid_chemical_system(): - reader = PDBReader(pbd_1gip) - nucleic_acid_chemical_system = reader.build_chemical_system() - return nucleic_acid_chemical_system + protein_trajectory = HDFTrajectoryInputData(traj_1gip) + return protein_trajectory.trajectory def test_select_primary_amine_returns_true_as_match_exists( - protein_chemical_system, + protein_trajectory, ): - exists = select_primary_amine(protein_chemical_system, check_exists=True) + exists = select_primary_amine(protein_trajectory, check_exists=True) assert exists @@ -48,21 +46,21 @@ def test_select_sulphate_returns_false_as_match_does_not_exist( def test_select_primary_amine_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_primary_amine(protein_chemical_system) + selection = select_primary_amine(protein_trajectory) assert len(selection) == 117 def test_select_hydroxy_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_hydroxy(protein_chemical_system) + selection = select_hydroxy(protein_trajectory) assert len(selection) == 28786 -def test_select_methyl_returns_correct_number_of_atom_matches(protein_chemical_system): - selection = select_methly(protein_chemical_system) +def test_select_methyl_returns_correct_number_of_atom_matches(protein_trajectory): + selection = select_methyl(protein_trajectory) assert len(selection) == 244 @@ -80,6 +78,6 @@ def test_select_sulphate_returns_correct_number_of_atom_matches( assert len(selection) == 0 -def test_select_thiol_returns_correct_number_of_atoms_matches(protein_chemical_system): - selection = select_thiol(protein_chemical_system) +def test_select_thiol_returns_correct_number_of_atoms_matches(protein_trajectory): + selection = select_thiol(protein_trajectory) assert len(selection) == 0 diff --git a/MDANSE/Tests/UnitTests/AtomSelector/test_molecule_selectors.py b/MDANSE/Tests/UnitTests/AtomSelector/test_molecule_selectors.py index c2cdf10e1d..786c41d894 100644 --- a/MDANSE/Tests/UnitTests/AtomSelector/test_molecule_selectors.py +++ b/MDANSE/Tests/UnitTests/AtomSelector/test_molecule_selectors.py @@ -1,30 +1,29 @@ import os import pytest -from MDANSE.IO.PDBReader import PDBReader +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.Framework.AtomSelector.molecule_selectors import select_water -pbd_2vb1 = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.pdb" +traj_2vb1 = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.mdt" ) @pytest.fixture(scope="module") -def protein_chemical_system(): - reader = PDBReader(pbd_2vb1) - protein_chemical_system = reader.build_chemical_system() - return protein_chemical_system +def protein_trajectory(): + protein_trajectory = HDFTrajectoryInputData(traj_2vb1) + return protein_trajectory.trajectory def test_select_water_returns_true_as_match_exists( - protein_chemical_system, + protein_trajectory, ): - exists = select_water(protein_chemical_system, check_exists=True) + exists = select_water(protein_trajectory, check_exists=True) assert exists def test_select_water_returns_correct_number_of_atom_matches( - protein_chemical_system, + protein_trajectory, ): - selection = select_water(protein_chemical_system) + selection = select_water(protein_trajectory) assert len(selection) == 28746 diff --git a/MDANSE/Tests/UnitTests/AtomSelector/test_selector.py b/MDANSE/Tests/UnitTests/AtomSelector/test_selector.py index a583af5694..10b09938d8 100644 --- a/MDANSE/Tests/UnitTests/AtomSelector/test_selector.py +++ b/MDANSE/Tests/UnitTests/AtomSelector/test_selector.py @@ -1,31 +1,30 @@ import os import pytest -from MDANSE.IO.PDBReader import PDBReader +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.Framework.AtomSelector.selector import Selector -pbd_2vb1 = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.pdb" +traj_2vb1 = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.mdt" ) @pytest.fixture(scope="module") -def protein_chemical_system(): - reader = PDBReader(pbd_2vb1) - protein_chemical_system = reader.build_chemical_system() - return protein_chemical_system +def protein_trajectory(): + protein_trajectory = HDFTrajectoryInputData(traj_2vb1) + return protein_trajectory.trajectory -def test_selector_returns_all_atom_idxs(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_all_atom_idxs(protein_trajectory): + selector = Selector(protein_trajectory) atm_idxs = selector.get_idxs() assert len(atm_idxs) == 30714 def test_selector_returns_all_atom_idxs_with_all_and_sulfurs_selected( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.settings["all"] = True selector.settings["element"] = {"S": True} atm_idxs = selector.get_idxs() @@ -33,9 +32,9 @@ def test_selector_returns_all_atom_idxs_with_all_and_sulfurs_selected( def test_selector_returns_correct_number_of_atom_idxs_when_sulfur_atoms_are_selected( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.settings["all"] = False selector.settings["element"] = {"S": True} atm_idxs = selector.get_idxs() @@ -43,9 +42,9 @@ def test_selector_returns_correct_number_of_atom_idxs_when_sulfur_atoms_are_sele def test_selector_returns_correct_number_of_atom_idxs_when_sulfur_atoms_are_selected_when_get_idxs_is_called_twice( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.settings["all"] = False selector.settings["element"] = {"S": True} atm_idxs = selector.get_idxs() @@ -55,9 +54,9 @@ def test_selector_returns_correct_number_of_atom_idxs_when_sulfur_atoms_are_sele def test_selector_returns_correct_number_of_atom_idxs_when_waters_are_selected( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.settings["all"] = False selector.settings["water"] = True atm_idxs = selector.get_idxs() @@ -65,9 +64,9 @@ def test_selector_returns_correct_number_of_atom_idxs_when_waters_are_selected( def test_selector_returns_correct_number_of_atom_idxs_when_water_is_turned_on_and_off( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.settings["all"] = False selector.settings["water"] = True atm_idxs = selector.get_idxs() @@ -78,9 +77,9 @@ def test_selector_returns_correct_number_of_atom_idxs_when_water_is_turned_on_an def test_selector_returns_correct_number_of_atom_idxs_when_waters_and_sulfurs_are_selected( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.settings["all"] = False selector.settings["water"] = True selector.settings["element"] = {"S": True} @@ -89,37 +88,37 @@ def test_selector_returns_correct_number_of_atom_idxs_when_waters_and_sulfurs_ar def test_selector_returns_correct_number_of_atom_idxs_when_waters_and_sulfurs_are_selected_with_settings_loaded_as_a_dict( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.update_settings({"all": False, "element": {"S": True}, "water": True}) atm_idxs = selector.get_idxs() assert len(atm_idxs) == 28746 + 10 -def test_selector_json_dump_0(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_0(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False, "element": {"S": True}}) json_dump = selector.settings_to_json() assert json_dump == '{"all": false, "element": ["S"]}' -def test_selector_json_dump_1(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_1(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False, "element": {"S": True}, "water": True}) json_dump = selector.settings_to_json() assert json_dump == '{"all": false, "water": true, "element": ["S"]}' -def test_selector_json_dump_2(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_2(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False, "water": True}) json_dump = selector.settings_to_json() assert json_dump == '{"all": false, "water": true}' -def test_selector_json_dump_3(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_3(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings( {"all": False, "element": {"S": True, "H": True}, "water": True} ) @@ -127,8 +126,8 @@ def test_selector_json_dump_3(protein_chemical_system): assert json_dump == '{"all": false, "water": true, "element": ["H", "S"]}' -def test_selector_json_dump_4(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_4(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings( { "all": False, @@ -144,16 +143,16 @@ def test_selector_json_dump_4(protein_chemical_system): ) -def test_selector_json_dump_with_second_update(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_with_second_update(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False}) selector.update_settings({"element": {"S": True, "O": True}, "water": True}) json_dump = selector.settings_to_json() assert json_dump == '{"all": false, "water": true, "element": ["O", "S"]}' -def test_selector_json_dump_with_third_update(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_with_third_update(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False}) selector.update_settings({"element": {"S": True, "O": True}, "water": True}) selector.update_settings({"element": {"S": False}}) @@ -161,8 +160,8 @@ def test_selector_json_dump_with_third_update(protein_chemical_system): assert json_dump == '{"all": false, "water": true, "element": ["O"]}' -def test_selector_json_dump_with_fourth_update(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_with_fourth_update(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False}) selector.update_settings({"element": {"S": True, "O": True}, "water": True}) selector.update_settings({"element": {"S": False}}) @@ -172,9 +171,9 @@ def test_selector_json_dump_with_fourth_update(protein_chemical_system): def test_selector_returns_correct_number_of_atom_idxs_after_setting_settings_again_with_reset_first( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.update_settings({"all": False, "element": {"S": True}, "water": True}) atm_idxs = selector.get_idxs() assert len(atm_idxs) == 28746 + 10 @@ -190,8 +189,8 @@ def test_selector_returns_correct_number_of_atom_idxs_after_setting_settings_aga assert len(atm_idxs) == 10 -def test_selector_json_dump_and_load_0(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_and_load_0(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False, "index": {0: True, 1: True}}) json_dump = selector.settings_to_json() assert json_dump == '{"all": false, "index": [0, 1]}' @@ -200,8 +199,8 @@ def test_selector_json_dump_and_load_0(protein_chemical_system): assert len(atm_idxs) == 2 -def test_selector_json_dump_and_load_1(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_json_dump_and_load_1(protein_trajectory): + selector = Selector(protein_trajectory) selector.update_settings({"all": False, "element": {"S": True}, "water": True}) json_dump = selector.settings_to_json() assert json_dump == '{"all": false, "water": true, "element": ["S"]}' @@ -211,9 +210,9 @@ def test_selector_json_dump_and_load_1(protein_chemical_system): def test_selector_returns_correct_number_of_atom_idxs_when_indexes_0_and_1_are_selected( - protein_chemical_system, + protein_trajectory, ): - selector = Selector(protein_chemical_system) + selector = Selector(protein_trajectory) selector.update_settings( { "all": False, @@ -224,8 +223,8 @@ def test_selector_returns_correct_number_of_atom_idxs_when_indexes_0_and_1_are_s assert len(atm_idxs) == 2 -def test_selector_returns_true_with_correct_setting_check(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_true_with_correct_setting_check(protein_trajectory): + selector = Selector(protein_trajectory) assert selector.check_valid_setting( { "all": False, @@ -234,8 +233,8 @@ def test_selector_returns_true_with_correct_setting_check(protein_chemical_syste ) -def test_selector_returns_false_with_incorrect_setting_check_0(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_false_with_incorrect_setting_check_0(protein_trajectory): + selector = Selector(protein_trajectory) assert not selector.check_valid_setting( { "alle": False, @@ -244,8 +243,8 @@ def test_selector_returns_false_with_incorrect_setting_check_0(protein_chemical_ ) -def test_selector_returns_false_with_incorrect_setting_check_1(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_false_with_incorrect_setting_check_1(protein_trajectory): + selector = Selector(protein_trajectory) assert not selector.check_valid_setting( { "all": False, @@ -254,8 +253,8 @@ def test_selector_returns_false_with_incorrect_setting_check_1(protein_chemical_ ) -def test_selector_returns_false_with_incorrect_setting_check_2(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_false_with_incorrect_setting_check_2(protein_trajectory): + selector = Selector(protein_trajectory) assert not selector.check_valid_setting( { "all": False, @@ -265,37 +264,40 @@ def test_selector_returns_false_with_incorrect_setting_check_2(protein_chemical_ ) -def test_selector_returns_true_with_correct_json_setting_0(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_true_with_correct_json_setting_0(protein_trajectory): + selector = Selector(protein_trajectory) assert selector.check_valid_json_settings( '{"all": false, "water": true, "element": {"S": true}}' ) -def test_selector_returns_true_with_correct_json_setting_1(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_true_with_correct_json_setting_1(protein_trajectory): + selector = Selector(protein_trajectory) assert selector.check_valid_json_settings('{"all": false, "index": [0, 1]}') -def test_selector_returns_false_with_incorrect_json_setting_0(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_false_with_incorrect_json_setting_0(protein_trajectory): + selector = Selector(protein_trajectory) assert not selector.check_valid_json_settings( '{all: false, "water": true, "element": {"S": true}}' ) -def test_selector_returns_false_with_incorrect_json_setting_1(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_false_with_incorrect_json_setting_1(protein_trajectory): + selector = Selector(protein_trajectory) assert not selector.check_valid_json_settings('{"all": false, "index": [0, "1"]}') -def test_selector_returns_false_with_incorrect_json_setting_2(protein_chemical_system): - selector = Selector(protein_chemical_system) +def test_selector_returns_false_with_incorrect_json_setting_2(protein_trajectory): + selector = Selector(protein_trajectory) assert not selector.check_valid_json_settings('{"all": False, "index": ["0", "1"]}') -def test_selector_with_atom_fullname(protein_chemical_system): - selector = Selector(protein_chemical_system) +@pytest.mark.xfail(reason="see docstring") +def test_selector_with_atom_fullname(protein_trajectory): + """For the moment, full names of atoms are not implemented + in the ChemicalSystem.""" + selector = Selector(protein_trajectory) selector.update_settings( { "all": False, @@ -306,8 +308,13 @@ def test_selector_with_atom_fullname(protein_chemical_system): assert len(atm_idxs) == 2 -def test_selector_with_atom_name(protein_chemical_system): - selector = Selector(protein_chemical_system) +@pytest.mark.xfail(reason="see docstring") +def test_selector_with_atom_name(protein_trajectory): + """At the moment the oxygen in water has the same + atom name as the oxygen in the protein. + We will have to decide if this is acceptable. + """ + selector = Selector(protein_trajectory) selector.update_settings( { "all": False, diff --git a/MDANSE/Tests/UnitTests/AtomTransmutation/test_transmutation.py b/MDANSE/Tests/UnitTests/AtomTransmutation/test_transmutation.py index 9296d86228..cb0ee78868 100644 --- a/MDANSE/Tests/UnitTests/AtomTransmutation/test_transmutation.py +++ b/MDANSE/Tests/UnitTests/AtomTransmutation/test_transmutation.py @@ -1,33 +1,32 @@ import os import pytest -from MDANSE.IO.PDBReader import PDBReader +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.Framework.Configurators.AtomTransmutationConfigurator import AtomTransmuter -pbd_2vb1 = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.pdb" +traj_2vb1 = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "Data", "2vb1.mdt" ) @pytest.fixture(scope="module") -def protein_chemical_system(): - reader = PDBReader(pbd_2vb1) - protein_chemical_system = reader.build_chemical_system() - return protein_chemical_system +def protein_trajectory(): + protein_trajectory = HDFTrajectoryInputData(traj_2vb1) + return protein_trajectory.trajectory def test_atom_transmutation_returns_empty_dictionary_when_no_transmutations_are_made( - protein_chemical_system, + protein_trajectory, ): - atm_transmuter = AtomTransmuter(protein_chemical_system) + atm_transmuter = AtomTransmuter(protein_trajectory) mapping = atm_transmuter.get_setting() assert mapping == {} def test_atom_transmutation_return_dict_with_transmutations_with_incorrect_element_raises_exception( - protein_chemical_system, + protein_trajectory, ): - atm_transmuter = AtomTransmuter(protein_chemical_system) + atm_transmuter = AtomTransmuter(protein_trajectory) with pytest.raises(ValueError): atm_transmuter.apply_transmutation( {"all": False, "element": {"S": True}}, "CCC" @@ -35,9 +34,9 @@ def test_atom_transmutation_return_dict_with_transmutations_with_incorrect_eleme def test_atom_transmutation_return_dict_with_transmutations_with_s_element_transmutation( - protein_chemical_system, + protein_trajectory, ): - atm_transmuter = AtomTransmuter(protein_chemical_system) + atm_transmuter = AtomTransmuter(protein_trajectory) atm_transmuter.apply_transmutation({"all": False, "element": {"S": True}}, "C") mapping = atm_transmuter.get_setting() assert mapping == { @@ -55,9 +54,9 @@ def test_atom_transmutation_return_dict_with_transmutations_with_s_element_trans def test_atom_transmutation_return_dict_with_transmutations_with_s_element_transmutation_and_index_98_transmutation_0( - protein_chemical_system, + protein_trajectory, ): - atm_transmuter = AtomTransmuter(protein_chemical_system) + atm_transmuter = AtomTransmuter(protein_trajectory) atm_transmuter.apply_transmutation({"all": False, "element": {"S": True}}, "C") atm_transmuter.apply_transmutation({"all": False, "index": {98: True}}, "N") mapping = atm_transmuter.get_setting() @@ -76,9 +75,9 @@ def test_atom_transmutation_return_dict_with_transmutations_with_s_element_trans def test_atom_transmutation_return_dict_with_transmutations_with_s_element_transmutation_and_index_98_transmutation_1( - protein_chemical_system, + protein_trajectory, ): - atm_transmuter = AtomTransmuter(protein_chemical_system) + atm_transmuter = AtomTransmuter(protein_trajectory) atm_transmuter.apply_transmutation({"all": False, "element": {"S": True}}, "C") atm_transmuter.apply_transmutation({"all": False, "index": {98: True}}, "S") mapping = atm_transmuter.get_setting() @@ -96,9 +95,9 @@ def test_atom_transmutation_return_dict_with_transmutations_with_s_element_trans def test_atom_transmutation_return_dict_with_transmutations_with_s_element_transmutation_and_index_98_transmutation_2( - protein_chemical_system, + protein_trajectory, ): - atm_transmuter = AtomTransmuter(protein_chemical_system) + atm_transmuter = AtomTransmuter(protein_trajectory) atm_transmuter.apply_transmutation({"all": False, "element": {"S": True}}, "C") atm_transmuter.apply_transmutation( {"all": False, "index": {98: True, 99: True}}, "S" @@ -118,8 +117,8 @@ def test_atom_transmutation_return_dict_with_transmutations_with_s_element_trans } -def test_atom_transmutation_return_empty_dict_after_reset(protein_chemical_system): - atm_transmuter = AtomTransmuter(protein_chemical_system) +def test_atom_transmutation_return_empty_dict_after_reset(protein_trajectory): + atm_transmuter = AtomTransmuter(protein_trajectory) atm_transmuter.apply_transmutation({"all": False, "element": {"S": True}}, "C") atm_transmuter.apply_transmutation( {"all": False, "index": {98: True, 99: True}}, "S" diff --git a/MDANSE/Tests/UnitTests/Data/1gip.mdt b/MDANSE/Tests/UnitTests/Data/1gip.mdt new file mode 100644 index 0000000000..fb2e14d0bc Binary files /dev/null and b/MDANSE/Tests/UnitTests/Data/1gip.mdt differ diff --git a/MDANSE/Tests/UnitTests/Data/2vb1.mdt b/MDANSE/Tests/UnitTests/Data/2vb1.mdt new file mode 100644 index 0000000000..db5a1f650b Binary files /dev/null and b/MDANSE/Tests/UnitTests/Data/2vb1.mdt differ diff --git a/MDANSE/Tests/UnitTests/Data/com_trajectory.mdt b/MDANSE/Tests/UnitTests/Data/com_trajectory.mdt new file mode 100644 index 0000000000..79de460af7 Binary files /dev/null and b/MDANSE/Tests/UnitTests/Data/com_trajectory.mdt differ diff --git a/MDANSE/Tests/UnitTests/TrajectoryEditor/test_editor.py b/MDANSE/Tests/UnitTests/TrajectoryEditor/test_editor.py index 4faef784eb..186e728182 100644 --- a/MDANSE/Tests/UnitTests/TrajectoryEditor/test_editor.py +++ b/MDANSE/Tests/UnitTests/TrajectoryEditor/test_editor.py @@ -6,6 +6,7 @@ import numpy as np import h5py +from MDANSE.MolecularDynamics.Configuration import remove_jumps from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.Framework.Jobs.IJob import IJob @@ -19,6 +20,31 @@ ) +def test_jumps_removed_correctly(): + input_coords = np.array( + [ + [0.8, 0.2, 0.3], + [0.88, 0.22, 0.33], + [0.97, 0.2, 0.3], + [0.05, 0.22, 0.31], + [0.03, 0.2, 0.3], + [0.99, 0.22, 0.32], + ] + ) + expected_coords = np.array( + [ + [0.8, 0.2, 0.3], + [0.88, 0.22, 0.33], + [0.97, 0.2, 0.3], + [1.05, 0.22, 0.31], + [1.03, 0.2, 0.3], + [0.99, 0.22, 0.32], + ] + ) + corrected_coords = remove_jumps(input_coords) + assert np.allclose(corrected_coords, expected_coords) + + def test_editor_null(): temp_name = tempfile.mktemp() parameters = {} @@ -153,8 +179,8 @@ def test_editor_transmute(): original.trajectory.chemical_system.number_of_atoms == changed.trajectory.chemical_system.number_of_atoms ) - old_symbols = [at.symbol for at in original.trajectory.chemical_system.atom_list] - new_symbols = [at.symbol for at in changed.trajectory.chemical_system.atom_list] + old_symbols = [at for at in original.trajectory.chemical_system.atom_list] + new_symbols = [at for at in changed.trajectory.chemical_system.atom_list] assert old_symbols != new_symbols assert "B" not in old_symbols assert "B" in new_symbols diff --git a/MDANSE/Tests/UnitTests/molecules/test_chemistry.py b/MDANSE/Tests/UnitTests/molecules/test_chemistry.py index 53f5e4ebf7..c9938c971f 100644 --- a/MDANSE/Tests/UnitTests/molecules/test_chemistry.py +++ b/MDANSE/Tests/UnitTests/molecules/test_chemistry.py @@ -1,10 +1,6 @@ import os import pytest -import numpy as np -from rdkit.Chem.rdmolops import SanitizeMol -from rdkit.Chem.rdmolops import GetMolFrags from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData -from MDANSE.Chemistry.Structrures import Topology short_traj = os.path.join( @@ -19,35 +15,12 @@ def trajectory(): def test_unit_cell(trajectory: HDFTrajectoryInputData): - chem_system = trajectory.chemical_system - configuration = chem_system.configuration + configuration = trajectory._data.configuration() unit_cell = configuration.unit_cell print(unit_cell.abc_and_angles) def test_molecule_finder(trajectory: HDFTrajectoryInputData): - chem_system = trajectory.chemical_system - configuration = chem_system.configuration + configuration = trajectory._data.configuration() coordinates = configuration._variables["coordinates"] print(coordinates.shape) - - -@pytest.mark.xfail(reason="see docstring") -def test_molecule_assignment(trajectory: HDFTrajectoryInputData): - """As of today (17 Oct 2023) this test does not pass. - Topology class writes out the atom coordinates to a PDB file buffer, - and RDKit scans it for molecules. - Apparently RDKit does not apply the periodic boundary conditions, - and the molecules that were on the edge of the simulation box - end up in several pieces. - At the moment we use the Connectivity class instead. - """ - chem_system = trajectory.chemical_system - configuration = chem_system.configuration - topology = Topology(trajectory, chem_system) - mol = topology.scan_trajectory_frame(0) - SanitizeMol(mol) - gas_bits = GetMolFrags(mol, asMols=True) - all_lengths = np.array([mol.GetNumAtoms() for mol in gas_bits]) - # assert len(gas_bits) == 20 - assert np.all(all_lengths == 3) diff --git a/MDANSE/Tests/UnitTests/molecules/test_connectivity.py b/MDANSE/Tests/UnitTests/molecules/test_connectivity.py index 9420c2517e..72e78fb88d 100644 --- a/MDANSE/Tests/UnitTests/molecules/test_connectivity.py +++ b/MDANSE/Tests/UnitTests/molecules/test_connectivity.py @@ -1,10 +1,8 @@ import os import pytest -import numpy as np from MDANSE.MolecularDynamics.Connectivity import Connectivity from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData from MDANSE.MolecularDynamics.Trajectory import Trajectory -from MDANSE.Chemistry.Structrures import MoleculeTester short_traj = os.path.join( @@ -24,45 +22,25 @@ def test_create_connectivity(trajectory: Trajectory): assert len(conn._unique_elements) == 2 -def test_find_bonds(trajectory: Trajectory): +def test_find_molecules(trajectory: Trajectory): conn = Connectivity(trajectory=trajectory) conn.find_bonds() assert len(conn._unique_bonds) == 40 - - -def test_find_molecules(trajectory: Trajectory): - conn = Connectivity(trajectory=trajectory) - conn.find_molecules() - assert len(conn._molecules) == 20 - - -def test_rebuild_molecules(trajectory: Trajectory): - print(trajectory.chemical_system.atom_list) - conn = Connectivity(trajectory=trajectory) - conn.find_molecules() - atoms_before = int(trajectory.chemical_system.number_of_atoms) - chemical_system = trajectory.chemical_system - print(conn._molecules) - chemical_system.rebuild(conn._molecules) - atoms_after = int(trajectory.chemical_system.number_of_atoms) - assert atoms_before == atoms_after + conn.add_bond_information(trajectory.chemical_system) + molecules_found = 0 + for name in trajectory.chemical_system.unique_molecules(): + molecules_found += trajectory.chemical_system.number_of_molecules(name) + assert molecules_found == 20 def test_identify_molecules(trajectory: Trajectory): conn = Connectivity(trajectory=trajectory) - conn.find_molecules() + conn.find_bonds() chemical_system = trajectory.chemical_system - chemical_system.rebuild(conn._molecules) - configuration = chemical_system.configuration - coords = configuration.contiguous_configuration().coordinates + conn.add_bond_information(chemical_system) molstrings = [] - for entity in chemical_system.chemical_entities: - moltester = MoleculeTester(entity, coords) - inchistring = moltester.identify_molecule() - molstrings.append(inchistring) - if entity.number_of_atoms > 1: - entity.name = inchistring - assert len(molstrings) == 20 + for molname, mollist in chemical_system._clusters.items(): + assert len(mollist) == 20 result = True for ms in molstrings[1:]: result = result and ms == molstrings[0] diff --git a/MDANSE/Tests/UnitTests/molecules/test_rdkit.py b/MDANSE/Tests/UnitTests/molecules/test_rdkit.py index 295c986fa4..7d6913f5a6 100644 --- a/MDANSE/Tests/UnitTests/molecules/test_rdkit.py +++ b/MDANSE/Tests/UnitTests/molecules/test_rdkit.py @@ -5,8 +5,8 @@ from rdkit.Chem.rdmolops import SanitizeMol from rdkit.Chem.rdmolops import GetMolFrags import pytest -from MDANSE.IO.PDBReader import PDBReader -from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem +from MDANSE.IO.MinimalPDBReader import MinimalPDBReader as PDBReader +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem fname = os.path.join( @@ -17,8 +17,7 @@ @pytest.fixture() def chem_from_pdb(): reader = PDBReader(fname) - chem = reader.build_chemical_system() - yield chem + yield reader._chemical_system @pytest.fixture() diff --git a/MDANSE/Tests/UnitTests/test_HDF5Trajectory.py b/MDANSE/Tests/UnitTests/test_HDF5Trajectory.py index a12045423c..e75ae5904f 100644 --- a/MDANSE/Tests/UnitTests/test_HDF5Trajectory.py +++ b/MDANSE/Tests/UnitTests/test_HDF5Trajectory.py @@ -19,7 +19,7 @@ import numpy as np -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Configuration import RealConfiguration from MDANSE.MolecularDynamics.Trajectory import Trajectory, TrajectoryWriter @@ -31,8 +31,7 @@ def chemical_system(): temp = ChemicalSystem("Dummy test system") nAtoms = N_ATOMS - for i in range(nAtoms): - temp.add_chemical_entity(Atom(symbol="H")) + temp.initialise_atoms(nAtoms * ["H"]) return temp @@ -59,8 +58,7 @@ def sample_trajectory(chemical_system, sample_configuration): os.close(fdesc) writer = TrajectoryWriter(fname, chemical_system, n_steps=N_TIMESTEPS) for n, ts in enumerate(np.arange(N_TIMESTEPS)): - writer.chemical_system.configuration = sample_configuration - writer.dump_configuration(ts) + writer.dump_configuration(sample_configuration, ts) return fname @@ -73,8 +71,7 @@ def gzipped_trajectory(chemical_system, sample_configuration): fname, chemical_system, n_steps=N_TIMESTEPS, compression="gzip" ) for n, ts in enumerate(np.arange(N_TIMESTEPS)): - writer.chemical_system.configuration = sample_configuration - writer.dump_configuration(ts) + writer.dump_configuration(sample_configuration, ts) return fname @@ -87,16 +84,14 @@ def lzffed_trajectory(chemical_system, sample_configuration): fname, chemical_system, n_steps=N_TIMESTEPS, compression="lzf" ) for n, ts in enumerate(np.arange(N_TIMESTEPS)): - writer.chemical_system.configuration = sample_configuration - writer.dump_configuration(ts) + writer.dump_configuration(sample_configuration, ts) return fname def test_identity(chemical_system): temp = ChemicalSystem("Dummy test system") nAtoms = N_ATOMS - for i in range(nAtoms): - temp.add_chemical_entity(Atom(symbol="H")) + temp.initialise_atoms(nAtoms * ["H"]) # assert(temp == chemical_system) assert chemical_system == chemical_system @@ -108,7 +103,7 @@ def test_copy(chemical_system): print(original.number_of_atoms) print(copied.atom_list) print(copied.number_of_atoms) - assert repr(original) == repr(copied) + assert original.atom_list == copied.atom_list def test_compression(sample_trajectory, gzipped_trajectory, lzffed_trajectory): diff --git a/MDANSE/Tests/UnitTests/test_chemical_entity.py b/MDANSE/Tests/UnitTests/test_chemical_entity.py deleted file mode 100644 index fc4dfb7ae6..0000000000 --- a/MDANSE/Tests/UnitTests/test_chemical_entity.py +++ /dev/null @@ -1,2917 +0,0 @@ -# This file is part of MDANSE_GUI. -# -# MDANSE_GUI is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -import collections -import pickle -from typing import Union -import unittest -import sys - -import numpy as np - -from MDANSE.Chemistry import MOLECULES_DATABASE, RESIDUES_DATABASE, NUCLEOTIDES_DATABASE -import MDANSE.Chemistry.ChemicalEntity as ce -from MDANSE.Mathematics.LinearAlgebra import Quaternion, Vector, Tensor -from MDANSE.Mathematics.Transformation import RotationTranslation - - -class TestAtom(unittest.TestCase): - def test_empty_instantiation(self): - atom = ce.Atom() - - self.assertEqual("H", atom.symbol) - self.assertEqual("H", atom.name) - self.assertEqual([], atom.bonds) - self.assertEqual([], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(None, atom.parent) - - def test_instantiation_overwriting_default_values(self): - bond = ce.Atom() - atom = ce.Atom( - symbol="C", - name="carbon12", - bonds=[bond], - groups=["backbone"], - ghost=True, - index=0, - parent=bond, - atomic_mass=12, - random="12345", - ) - - self.assertEqual("C", atom.symbol) - self.assertEqual("carbon12", atom.name) - self.assertEqual([bond], atom.bonds) - self.assertEqual(["backbone"], atom._groups) - self.assertEqual(True, atom.ghost) - self.assertEqual(0, atom.index) - self.assertEqual(bond, atom.parent) - self.assertEqual(12, atom.atomic_mass) - self.assertEqual("12345", atom.random) - - def test_instantiation_undefined_element(self): - with self.assertRaises(ce.UnknownAtomError): - ce.Atom(symbol="CC") - - def test_copy(self): - atom = ce.Atom() - copy = atom.copy() - self.assertEqual(repr(atom), repr(copy)) - - def test_pickling(self): - atom = ce.Atom() - pickled = pickle.dumps(atom) - unpickled = pickle.loads(pickled) - self.assertEqual(repr(atom), repr(unpickled)) - - def test_dunder_str(self): - atom = ce.Atom(name="Hydrogen") - self.assertEqual("Hydrogen", str(atom)) - - def test_dunder_repr(self): - atom = ce.Atom(name="Hydrogen", bonds=[ce.Atom(name="H5")]) - self.assertEqual( - "MDANSE.Chemistry.ChemicalEntity.Atom(parent=None, name='Hydrogen', symbol='H', " - "bonds=[Atom(H5)], groups=[], ghost=False, index=None, element='hydrogen')", - repr(atom), - ) - - def test_atom_list_ghost_true(self): - atom = ce.Atom(ghost=True) - atom_list = atom.atom_list - self.assertEqual([], atom_list) - - def test_atom_list_ghost_false(self): - atom = ce.Atom(ghost=False) - atom_list = atom.atom_list - self.assertEqual([atom], atom_list) - - def test_total_number_of_atoms(self): - atom = ce.Atom(ghost=False) - ghost = ce.Atom(ghost=True) - self.assertEqual(1, atom.total_number_of_atoms) - self.assertEqual(1, ghost.total_number_of_atoms) - - def test_number_of_atoms(self): - atom = ce.Atom(ghost=False) - ghost = ce.Atom(ghost=True) - - self.assertEqual(0, atom.number_of_atoms) - self.assertEqual(1, ghost.number_of_atoms) - - def test_bonds_setter(self): - atom = ce.Atom() - bond = ce.Atom(symbol="C") - atom.bonds = [bond] - self.assertEqual([bond], atom.bonds) - - def test_ghost_setter(self): - atom = ce.Atom(ghost=False) - atom.ghost = True - self.assertTrue(atom.ghost) - - def test_index_setter_index_not_set(self): - atom = ce.Atom() - atom.index = 0 - self.assertEqual(0, atom.index) - - def test_index_setter_index_set(self): - atom = ce.Atom(index=0) - atom.index = 1 - self.assertEqual(0, atom.index) - - def test_name_setter(self): - atom = ce.Atom(name="name") - atom.name = "Hydrogen" - self.assertEqual("Hydrogen", atom.name) - - def test_symbol_setter(self): - atom = ce.Atom(symbol="H") - atom.symbol = "C" - - self.assertEqual("C", atom.symbol) - with self.assertRaises(ce.UnknownAtomError): - atom.symbol = "CC" - - def test_build(self): - atom = ce.Atom.build(None, "H", "H1", "0", False) - self.assertEqual( - repr(ce.Atom(symbol="H", name="H1", ghost=False, index=0)), repr(atom) - ) - - def test_serialize(self): - atom = ce.Atom() - dictionary = {} - result = atom.serialize(dictionary) - - self.assertEqual(("atoms", 0), result) - self.assertEqual( - {"atoms": [[repr("H"), repr("H"), "None", "False"]]}, dictionary - ) - - -class TestAtomGroup(unittest.TestCase): - def setUp(self): - self.atom1 = ce.Atom(name="H1") - self.atom2 = ce.Atom(name="H2") - - self.system = ce.ChemicalSystem("name") - self.system.add_chemical_entity(self.atom1) - self.system.add_chemical_entity(self.atom2) - - self.group = ce.AtomGroup([self.atom1, self.atom2]) - - def test_instantiation_valid(self): - self.assertEqual("", self.group.name) - self.assertEqual(None, self.group.parent) - self.assertEqual([self.atom1, self.atom2], self.group._atoms) - self.assertEqual(self.system, self.group._chemical_system) - - def test_instantiation_invalid(self): - system2 = ce.ChemicalSystem("name") - system2.add_chemical_entity(self.atom2) - - with self.assertRaises(ce.ChemicalEntityError): - ce.AtomGroup([self.atom1, self.atom2]) - - def test_pickling(self): - pickled = pickle.dumps(self.group) - unpickled = pickle.loads(pickled) - - self.assertEqual("", unpickled.name) - self.assertEqual(None, unpickled.parent) - self.assertEqual(2, len(unpickled._atoms)) - self.assertEqual(repr(self.atom1), repr(unpickled._atoms[0])) - self.assertEqual(repr(self.atom2), repr(unpickled._atoms[1])) - self.assertEqual(repr(self.system), repr(unpickled._chemical_system)) - - def test_duner_repr(self): - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.AtomGroup(parent=None, name='', atoms=[MDANSE." - "Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.ChemicalSystem(name), " - "name='H1', symbol='H', bonds=[], groups=[], ghost=False, index=0, element='hydrogen'), MDANSE.Chemistry." - "ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.ChemicalSystem(name), name='H2', " - "symbol='H', bonds=[], groups=[], ghost=False, index=1, element='hydrogen')], chemical_system=MDANSE.Chemistry." - "ChemicalEntity.ChemicalSystem(name))", - repr(self.group), - ) - - def test_dunder_str(self): - self.assertEqual("AtomGroup consisting of 2 atoms", str(self.group)) - - def test_atom_list(self): - self.atom2._ghost = True - self.assertEqual([self.atom1], self.group.atom_list) - - def test_copy(self): - self.assertEqual(None, self.group.copy()) - - def test_number_of_atoms(self): - self.atom2._ghost = True - self.assertEqual(1, self.group.number_of_atoms) - self.assertEqual(2, self.group.total_number_of_atoms) - - def test_root_chemical_system(self): - self.assertEqual(repr(self.system), repr(self.group.root_chemical_system)) - - def test_serialize(self): - dictionary = {} - result = self.group.serialize(dictionary) - - self.assertEqual(None, result) - self.assertDictEqual({}, dictionary) - - -class TestAtomCluster(unittest.TestCase): - def test_valid_instantiation_parentful(self): - atom1 = ce.Atom() - atom2 = ce.Atom() - cluster = ce.AtomCluster("Cluster1", [atom1, atom2]) - - self.assertEqual(None, cluster.parent) - self.assertEqual("Cluster1", cluster.name) - self.assertEqual(False, cluster._parentless) - self.assertEqual([atom1, atom2], cluster._atoms) - self.assertEqual(cluster, cluster._atoms[0].parent) - - def test_valid_instantiation_parentless(self): - atom1 = ce.Atom() - atom2 = ce.Atom() - cluster = ce.AtomCluster("Cluster1", [atom1, atom2], parentless=True) - - self.assertEqual(None, cluster.parent) - self.assertEqual("Cluster1", cluster.name) - self.assertEqual(True, cluster._parentless) - self.assertEqual([atom1, atom2], cluster._atoms) - self.assertEqual(None, cluster._atoms[0].parent) - - def test_pickling(self): - cluster = ce.AtomCluster("Cluster1", [ce.Atom(), ce.Atom()], parentless=True) - - pickled = pickle.dumps(cluster) - unpickled = pickle.loads(pickled) - - self.assertEqual(cluster.parent, unpickled.parent) - self.assertEqual(cluster.name, unpickled.name) - self.assertEqual(cluster._parentless, unpickled._parentless) - self.assertEqual(repr(cluster._atoms), repr(unpickled._atoms)) - - def test_dunder_getitem(self): - atom = ce.Atom() - cluster = ce.AtomCluster("Cluster1", [atom, ce.Atom()], parentless=True) - self.assertEqual(atom, cluster[0]) - - def test_dunder_repr(self): - cluster = ce.AtomCluster("Cluster1", [ce.Atom(), ce.Atom()], parentless=True) - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.AtomCluster(parent=None, name='Cluster1', " - "parentless=True, atoms=[MDANSE.Chemistry.ChemicalEntity.Atom(parent=None, name='H', " - "symbol='H', bonds=[], groups=[], ghost=False, index=None, element='hydrogen'), MDANSE.Chemistry.ChemicalEntity." - "Atom(parent=None, name='H', symbol='H', bonds=[], groups=[], ghost=False, index=None, element='hydrogen')])", - repr(cluster), - ) - - def test_dunder_str(self): - cluster = ce.AtomCluster("Cluster1", [ce.Atom(), ce.Atom()], parentless=True) - self.assertEqual("AtomCluster consisting of 2 atoms", str(cluster)) - - def test_atom_list(self): - atom = ce.Atom() - ghost = ce.Atom(ghost=True) - cluster = ce.AtomCluster("Cluster1", [atom, ghost], parentless=True) - self.assertEqual([atom], cluster.atom_list) - - def test_copy_parentful(self): - cluster = ce.AtomCluster("Cluster1", [ce.Atom(), ce.Atom()], parentless=False) - copy = cluster.copy() - self.assertEqual(repr(cluster), repr(copy)) - - def test_copy_parentless(self): - cluster = ce.AtomCluster("Cluster1", [ce.Atom(), ce.Atom()], parentless=True) - copy = cluster.copy() - self.assertEqual(repr(cluster), repr(copy)) - - def test_number_of_atoms(self): - cluster = ce.AtomCluster( - "Cluster1", [ce.Atom(), ce.Atom(ghost=True)], parentless=True - ) - self.assertEqual(1, cluster.number_of_atoms) - self.assertEqual(2, cluster.total_number_of_atoms) - - def test_reorder_atoms_exception(self): - cluster = ce.AtomCluster( - "Cluster1", [ce.Atom(symbol="H"), ce.Atom(symbol="C")], parentless=True - ) - with self.assertRaises(ce.InconsistentAtomNamesError): - cluster.reorder_atoms(["H", "H"]) - with self.assertRaises(ce.InconsistentAtomNamesError): - cluster.reorder_atoms(["C", "H", "H"]) - - def test_reorder_atoms_valid(self): - h = ce.Atom(symbol="H") - c = ce.Atom(symbol="C") - cluster = ce.AtomCluster("Cluster1", [h, c], parentless=True) - cluster.reorder_atoms(["C", "H"]) - - self.assertEqual([c, h], cluster._atoms) - - def test_build(self): - h5 = { - "atom_clusters": [[b"[0, 1]", repr("Cluster1").encode("UTF-8")]], - "atoms": [ - [repr("H").encode("UTF-8"), repr("H").encode("UTF-8"), b"0", b"False"], - [repr("H").encode("UTF-8"), repr("H").encode("UTF-8"), b"1", b"False"], - ], - } - ac = ce.AtomCluster.build(h5, [0, 1], "Cluster1") - cs = ce.ChemicalSystem() - cs.add_chemical_entity(ac) - - self.assertEqual(repr(cs.chemical_entities[0]), repr(ac)) - - def test_serialize_empty_dict(self): - cluster = ce.AtomCluster("Cluster1", [ce.Atom(), ce.Atom()], parentless=True) - dictionary = {} - result = cluster.serialize(dictionary) - - self.assertEqual(("atom_clusters", 0), result) - self.assertDictEqual( - { - "atom_clusters": [["[0, 1]", repr("Cluster1")]], - "atoms": [ - [repr("H"), repr("H"), "None", "False"], - [repr("H"), repr("H"), "None", "False"], - ], - }, - dictionary, - ) - - def test_serialize_nonempty_dict(self): - cluster = ce.AtomCluster("Cluster1", [ce.Atom(), ce.Atom()], parentless=True) - dictionary = {"atom_clusters": [[], [], []], "atoms": [[], [], []]} - result = cluster.serialize(dictionary) - - self.assertEqual(("atom_clusters", 3), result) - self.assertDictEqual( - { - "atom_clusters": [[], [], [], ["[3, 4]", repr("Cluster1")]], - "atoms": [ - [], - [], - [], - [repr("H"), repr("H"), "None", "False"], - [repr("H"), repr("H"), "None", "False"], - ], - }, - dictionary, - ) - - -class TestMolecule(unittest.TestCase): - def setUp(self): - self.molecule = ce.Molecule("WAT", "water") - - def compare_two_molecules(self, molecule: ce.Molecule): - self.assertEqual(None, molecule.parent) - self.assertEqual("water", molecule.name) - self.assertEqual("WAT", molecule.code) - - for name, reference_name in zip(molecule._atoms.keys(), ["OW", "HW2", "HW1"]): - self.assertEqual(reference_name, name) - - self.compare_atoms(molecule._atoms, molecule) - - def compare_atoms(self, atom_list, parent: ce.Molecule): - try: - atom = atom_list["OW"] - except TypeError: - atom = atom_list[0] - self.assertEqual("O", atom.symbol) - self.assertEqual("OW", atom.name) - self.assertEqual(2, len(atom.bonds)) - self.assertEqual(parent._atoms["HW1"], atom.bonds[0]) - self.assertEqual(parent._atoms["HW2"], atom.bonds[1]) - self.assertEqual([], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["HW1"] - except TypeError: - atom = atom_list[2] - self.assertEqual("H", atom.symbol) - self.assertEqual("HW1", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual(parent._atoms["OW"], atom.bonds[0]) - self.assertEqual([], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["HW2"] - except TypeError: - atom = atom_list[1] - self.assertEqual("H", atom.symbol) - self.assertEqual("HW2", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual(parent._atoms["OW"], atom.bonds[0]) - self.assertEqual([], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - def test_valid_molecule_instantiation(self): - self.compare_two_molecules(self.molecule) - - def test_unregistered_molecule_instantiation(self): - with self.assertRaises(ce.UnknownMoleculeError): - ce.Molecule("000000", "000000") - - def test_dunder_getitem(self): - self.assertEqual(self.molecule._atoms["OW"], self.molecule["OW"]) - - def test_pickling(self): - pickled = pickle.dumps(self.molecule) - unpickled = pickle.loads(pickled) - - self.compare_two_molecules(unpickled) - - def test_dunder_repr(self): - if sys.version_info.minor < 12: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.Molecule(parent=None, name='water', " - "atoms=OrderedDict([('OW', MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry." - "ChemicalEntity.Molecule(water), name='OW', symbol='O', bonds=[Atom(HW1), Atom(HW2)], " - "groups=[], ghost=False, index=None, element='oxygen', alternatives=['O', 'OH2'])), ('HW2', MDANSE.Chemistry." - "ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Molecule(water), name='HW2', " - "symbol='H', bonds=[Atom(OW)], groups=[], ghost=False, index=None, element='hydrogen', alternatives=['H2'])), " - "('HW1', MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity." - "Molecule(water), name='HW1', symbol='H', bonds=[Atom(OW)], groups=[], ghost=False, index=None, element='hydrogen'" - ", alternatives=['H1']))]), code='WAT')", - repr(self.molecule), - ) - else: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.Molecule(parent=None, name='water', " - "atoms=OrderedDict({'OW': MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry." - "ChemicalEntity.Molecule(water), name='OW', symbol='O', bonds=[Atom(HW1), Atom(HW2)], " - "groups=[], ghost=False, index=None, element='oxygen', alternatives=['O', 'OH2']), 'HW2': MDANSE.Chemistry." - "ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Molecule(water), name='HW2', " - "symbol='H', bonds=[Atom(OW)], groups=[], ghost=False, index=None, element='hydrogen', alternatives=['H2']), " - "'HW1': MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity." - "Molecule(water), name='HW1', symbol='H', bonds=[Atom(OW)], groups=[], ghost=False, index=None, element='hydrogen'" - ", alternatives=['H1'])}), code='WAT')", - repr(self.molecule), - ) - - def test_dunder_str(self): - self.assertEqual('Molecule of water (database code "WAT")', str(self.molecule)) - - def test_atom_list(self): - self.assertEqual(3, len(self.molecule.atom_list)) - self.compare_atoms(self.molecule.atom_list, self.molecule) - - def test_copy(self): - copy = self.molecule.copy() - self.compare_two_molecules(copy) - - def test_number_of_atoms(self): - self.assertEqual(3, self.molecule.number_of_atoms) - - def test_total_number_of_atoms(self): - self.assertEqual(3, self.molecule.total_number_of_atoms) - - def test_reorder_atoms_invalid_input(self): - with self.assertRaises(ce.InconsistentAtomNamesError): - self.molecule.reorder_atoms(["O", "H", "H"]) - - def test_reorder_atoms_valid_input(self): - self.molecule.reorder_atoms(["HW1", "HW2", "OW"]) - self.assertEqual("HW1", self.molecule.atom_list[0].name) - self.assertEqual("HW2", self.molecule.atom_list[1].name) - self.assertEqual("OW", self.molecule.atom_list[2].name) - - def test_build(self): - h5 = { - "molecules": [ - [ - b"[0, 1, 2]", - repr("WAT").encode("UTF-8"), - repr("water").encode("UTF-8"), - ] - ], - "atoms": [ - [ - repr("O").encode("UTF-8"), - repr("OW").encode("UTF-8"), - "None", - b"False", - ], - [ - repr("H").encode("UTF-8"), - repr("HW2").encode("UTF-8"), - "None", - b"False", - ], - [ - repr("H").encode("UTF-8"), - repr("HW1").encode("UTF-8"), - "None", - b"False", - ], - ], - } - m = ce.Molecule.build(h5, [0, 1, 2], "WAT", "water") - - self.assertEqual(repr(ce.Molecule("WAT", "water")), repr(m)) - - def test_serialize_from_empty_dict(self): - dictionary = {} - result = self.molecule.serialize(dictionary) - - self.assertEqual(("molecules", 0), result) - self.assertDictEqual( - { - "molecules": [["[0, 1, 2]", repr("WAT"), repr("water")]], - "atoms": [ - [repr("O"), repr("OW"), "None", "False"], - [repr("H"), repr("HW2"), "None", "False"], - [repr("H"), repr("HW1"), "None", "False"], - ], - }, - dictionary, - ) - - def test_serialize_from_nonempty_dict(self): - dictionary = {"atoms": [[], []], "molecules": [[], []]} - result = self.molecule.serialize(dictionary) - - self.assertEqual(("molecules", 2), result) - self.assertDictEqual( - { - "atoms": [ - [], - [], - [repr("O"), repr("OW"), "None", "False"], - [repr("H"), repr("HW2"), "None", "False"], - [repr("H"), repr("HW1"), "None", "False"], - ], - "molecules": [[], [], ["[2, 3, 4]", repr("WAT"), repr("water")]], - }, - dictionary, - ) - - -class TestResidue(unittest.TestCase): - def test_valid_residue_initialisation_without_variant(self): - residue = ce.Residue("GLY", "glycine", None) - - self.compare_base_residues(residue, True) - - def compare_base_residues(self, residue: ce.Residue, compare_atoms: bool): - self.assertEqual("glycine", residue.name) - self.assertEqual(None, residue.parent) - self.assertEqual("GLY", residue.code) - self.assertEqual(None, residue._variant) - self.assertEqual(None, residue._selected_variant) - if compare_atoms: - self.assertEqual(collections.OrderedDict(), residue._atoms) - - def test_valid_residue_initialisation_with_valid_variant(self): - residue = ce.Residue("GLY", "glycine", "CT1") - - self.assertEqual("glycine", residue.name) - self.assertEqual(None, residue.parent) - self.assertEqual("GLY", residue.code) - self.assertEqual("CT1", residue._variant) - self.assertDictEqual(RESIDUES_DATABASE["CT1"], residue._selected_variant) - self.assertEqual(collections.OrderedDict(), residue._atoms) - - def test_invalid_residue_initialisation(self): - with self.assertRaises(ce.UnknownResidueError): - ce.Residue("00000", "00000") - - def test_valid_residue_initialisation_with_nonexistent_variant(self): - with self.assertRaises(ce.InvalidVariantError): - ce.Residue("GLY", "glycine", "00000") - - def test_valid_residue_initialisation_with_invalid_variant(self): - with self.assertRaises(ce.InvalidVariantError): - ce.Residue("GLY", "glycine", "GLY") - - def test_set_atoms_valid(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - - self.assertEqual(7, len(residue._atoms)) - self.compare_atoms(residue._atoms, residue) - - def compare_atoms( - self, atom_list: Union[list, dict, ce.Residue], parent: ce.Residue - ): - try: - atom = atom_list["H"] - except TypeError: - atom = atom_list[0] - self.assertEqual("H", atom.symbol) - self.assertEqual("H", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual(parent._atoms["N"], atom.bonds[0]) - self.assertEqual(["backbone", "peptide"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["HA3"] - except TypeError: - atom = atom_list[1] - self.assertEqual("H", atom.symbol) - self.assertEqual("HA3", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual(parent._atoms["CA"], atom.bonds[0]) - self.assertEqual(["sidechain"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["O"] - except TypeError: - atom = atom_list[2] - self.assertEqual("O", atom.symbol) - self.assertEqual("O", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual(parent._atoms["C"], atom.bonds[0]) - self.assertEqual(["backbone", "peptide"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["N"] - except TypeError: - atom = atom_list[3] - self.assertEqual("N", atom.symbol) - self.assertEqual("N", atom.name) - self.assertEqual(3, len(atom.bonds)) - self.assertEqual(parent._atoms["CA"], atom.bonds[0]) - self.assertEqual(parent._atoms["H"], atom.bonds[1]) - self.assertEqual("-R", atom.bonds[2]) - self.assertEqual(["backbone", "peptide"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["CA"] - except TypeError: - atom = atom_list[4] - self.assertEqual("C", atom.symbol) - self.assertEqual("CA", atom.name) - self.assertEqual(4, len(atom.bonds)) - self.assertEqual(parent._atoms["C"], atom.bonds[0]) - self.assertEqual(parent._atoms["HA2"], atom.bonds[1]) - self.assertEqual(parent._atoms["HA3"], atom.bonds[2]) - self.assertEqual(parent._atoms["N"], atom.bonds[3]) - self.assertEqual(["backbone"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["HA2"] - except TypeError: - atom = atom_list[5] - self.assertEqual("H", atom.symbol) - self.assertEqual("HA2", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual(parent._atoms["CA"], atom.bonds[0]) - self.assertEqual(["backbone"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - try: - atom = atom_list["C"] - except TypeError: - atom = atom_list[6] - self.assertEqual("C", atom.symbol) - self.assertEqual("C", atom.name) - self.assertEqual(3, len(atom.bonds)) - self.assertEqual(parent._atoms["CA"], atom.bonds[0]) - self.assertEqual(parent._atoms["O"], atom.bonds[1]) - self.assertEqual("+R", atom.bonds[2]) - self.assertEqual(["backbone", "peptide"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(parent, atom.parent) - - def test_set_atoms_invalid(self): - residue = ce.Residue("GLY", "glycine", None) - with self.assertRaises(ce.InconsistentAtomNamesError): - residue.set_atoms([]) - - def test_set_atoms_variant(self): - residue = ce.Residue("GLY", "glycine", "CT1") - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C", "OXT"]) - selected_variant = RESIDUES_DATABASE["CT1"] - selected_variant["atoms"]["OXT"]["bonds"] = [residue._atoms["C"]] - - self.maxDiff = None - self.assertEqual("glycine", residue.name) - self.assertEqual(None, residue.parent) - self.assertEqual("GLY", residue.code) - self.assertEqual("CT1", residue._variant) - self.assertDictEqual(selected_variant, residue._selected_variant) - - self.compare_atoms(residue._atoms, residue) - - atom = residue._atoms["OXT"] - self.assertEqual("O", atom.symbol) - self.assertEqual("OXT", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual(residue._atoms["C"], atom.bonds[0]) - self.assertEqual(["backbone"], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(residue, atom.parent) - - def test_pickling(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - - pickled = pickle.dumps(residue) - unpickled = pickle.loads(pickled) - - self.compare_base_residues(unpickled, False) - self.compare_atoms(unpickled._atoms, unpickled) - - def test_dunder_getitem(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - self.compare_atoms(residue, residue) - - def test_dunder_repr(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - if sys.version_info.minor < 12: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.Residue(parent=None, name='glycine', code='GLY', " - "variant=None, selected_variant=None, atoms=OrderedDict([('H', MDANSE.Chemistry.ChemicalEntity" - ".Atom(parent=MDANSE.Chemistry.ChemicalEntity.Residue(glycine), name='H', symbol='H', bonds=" - "[Atom(N)], groups=['backbone', 'peptide'], ghost=False, index=None, element='hydrogen', alternatives=['HN'])), " - "('HA3', MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Residue" - "(glycine), name='HA3', symbol='H', bonds=[Atom(CA)], groups=['sidechain'], ghost=False, " - "index=None, element='hydrogen', alternatives=['HA1'])), ('O', MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE" - ".Chemistry.ChemicalEntity.Residue(glycine), name='O', symbol='O', bonds=[Atom(C)], groups=" - "['backbone', 'peptide'], ghost=False, index=None, element='oxygen', alternatives=['OT1'])), ('N', MDANSE." - "Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Residue(glycine), name=" - "'N', symbol='N', bonds=[Atom(CA), Atom(H), Atom(-R)], groups=['backbone', 'peptide'], ghost=" - "False, index=None, element='nitrogen', alternatives=[])), ('CA', MDANSE.Chemistry.ChemicalEntity.Atom(parent=" - "MDANSE.Chemistry.ChemicalEntity.Residue(glycine), name='CA', symbol='C', bonds=[Atom(C), " - "Atom(HA2), Atom(HA3), Atom(N)], groups=['backbone'], ghost=False, index=None, element='carbon', alternatives=" - "[])), ('HA2', MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity." - "Residue(glycine), name='HA2', symbol='H', bonds=[Atom(CA)], groups=['backbone'], ghost=False," - " index=None, element='hydrogen', alternatives=['HA'])), ('C', MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE" - ".Chemistry.ChemicalEntity.Residue(glycine), name='C', symbol='C', bonds=[Atom(CA), Atom(O), " - "Atom(+R)], groups=['backbone', 'peptide'], ghost=False, index=None, element='carbon', alternatives=[]))]))", - repr(residue), - ) - else: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.Residue(parent=None, name='glycine', code='GLY', " - "variant=None, selected_variant=None, atoms=OrderedDict({'H': MDANSE.Chemistry.ChemicalEntity" - ".Atom(parent=MDANSE.Chemistry.ChemicalEntity.Residue(glycine), name='H', symbol='H', bonds=" - "[Atom(N)], groups=['backbone', 'peptide'], ghost=False, index=None, element='hydrogen', alternatives=['HN']), " - "'HA3': MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Residue" - "(glycine), name='HA3', symbol='H', bonds=[Atom(CA)], groups=['sidechain'], ghost=False, " - "index=None, element='hydrogen', alternatives=['HA1']), 'O': MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE" - ".Chemistry.ChemicalEntity.Residue(glycine), name='O', symbol='O', bonds=[Atom(C)], groups=" - "['backbone', 'peptide'], ghost=False, index=None, element='oxygen', alternatives=['OT1']), 'N': MDANSE." - "Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Residue(glycine), name=" - "'N', symbol='N', bonds=[Atom(CA), Atom(H), Atom(-R)], groups=['backbone', 'peptide'], ghost=" - "False, index=None, element='nitrogen', alternatives=[]), 'CA': MDANSE.Chemistry.ChemicalEntity.Atom(parent=" - "MDANSE.Chemistry.ChemicalEntity.Residue(glycine), name='CA', symbol='C', bonds=[Atom(C), " - "Atom(HA2), Atom(HA3), Atom(N)], groups=['backbone'], ghost=False, index=None, element='carbon', alternatives=" - "[]), 'HA2': MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity." - "Residue(glycine), name='HA2', symbol='H', bonds=[Atom(CA)], groups=['backbone'], ghost=False," - " index=None, element='hydrogen', alternatives=['HA']), 'C': MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE" - ".Chemistry.ChemicalEntity.Residue(glycine), name='C', symbol='C', bonds=[Atom(CA), Atom(O), " - "Atom(+R)], groups=['backbone', 'peptide'], ghost=False, index=None, element='carbon', alternatives=[])}))", - repr(residue), - ) - - def test_dunder_str(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - self.assertEqual( - 'Amino acid Residue glycine (database code "GLY")', str(residue) - ) - - def test_atom_list(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - self.compare_atoms(residue.atom_list, residue) - - def test_number_of_atoms(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - self.assertEqual(7, residue.number_of_atoms) - self.assertEqual(7, residue.total_number_of_atoms) - - def test_copy(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - copy = residue.copy() - - self.compare_atoms(copy, copy) - - def test_build(self): - h5 = { - "residues": [ - [ - b"[0, 1, 2, 3, 4, 5, 6]", - repr("GLY").encode("UTF-8"), - repr("glycine").encode("UTF-8"), - b"None", - ] - ], - "atoms": [ - [repr("H").encode("UTF-8"), repr("H").encode("UTF-8"), b"False"], - [repr("H").encode("UTF-8"), repr("HA3").encode("UTF-8"), b"False"], - [repr("O").encode("UTF-8"), repr("O").encode("UTF-8"), b"False"], - [repr("N").encode("UTF-8"), repr("N").encode("UTF-8"), b"False"], - [repr("C").encode("UTF-8"), repr("CA").encode("UTF-8"), b"False"], - [repr("H").encode("UTF-8"), repr("HA2").encode("UTF-8"), b"False"], - [repr("C").encode("UTF-8"), repr("C").encode("UTF-8"), b"False"], - ], - } - r = ce.Residue.build(h5, [0, 1, 2, 3, 4, 5, 6], "GLY", "glycine", None) - - expected = ce.Residue("GLY", "glycine", None) - expected.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - - self.assertEqual(repr(expected), repr(r)) - - def test_serialize_empty_dict(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - dictionary = {} - result = residue.serialize(dictionary) - - self.maxDiff = None - self.assertEqual(("residues", 0), result) - self.assertDictEqual( - { - "residues": [ - ["[0, 1, 2, 3, 4, 5, 6]", repr("GLY"), repr("glycine"), "None"] - ], - "atoms": [ - [repr("H"), repr("H"), "None", "False"], - [repr("H"), repr("HA3"), "None", "False"], - [repr("O"), repr("O"), "None", "False"], - [repr("N"), repr("N"), "None", "False"], - [repr("C"), repr("CA"), "None", "False"], - [repr("H"), repr("HA2"), "None", "False"], - [repr("C"), repr("C"), "None", "False"], - ], - }, - dictionary, - ) - - def test_serialize_nonempty_dict(self): - residue = ce.Residue("GLY", "glycine", None) - residue.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C"]) - dictionary = {"atoms": [[], [], []], "residues": [[], [], []]} - result = residue.serialize(dictionary) - - self.maxDiff = None - self.assertEqual(("residues", 3), result) - self.assertDictEqual( - { - "residues": [ - [], - [], - [], - ["[3, 4, 5, 6, 7, 8, 9]", repr("GLY"), repr("glycine"), "None"], - ], - "atoms": [ - [], - [], - [], - [repr("H"), repr("H"), "None", "False"], - [repr("H"), repr("HA3"), "None", "False"], - [repr("O"), repr("O"), "None", "False"], - [repr("N"), repr("N"), "None", "False"], - [repr("C"), repr("CA"), "None", "False"], - [repr("H"), repr("HA2"), "None", "False"], - [repr("C"), repr("C"), "None", "False"], - ], - }, - dictionary, - ) - - -class TestNucleotide(unittest.TestCase): - def test_valid_residue_initialisation_without_variant(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - - self.compare_base_residues(nucleotide, True) - - def compare_base_residues(self, nucleotide: ce.Nucleotide, compare_atoms: bool): - self.assertEqual("5T1", nucleotide.name) - self.assertEqual(None, nucleotide.parent) - self.assertEqual("5T1", nucleotide.code) - self.assertEqual(None, nucleotide._variant) - self.assertEqual(None, nucleotide._selected_variant) - if compare_atoms: - self.assertEqual(collections.OrderedDict(), nucleotide._atoms) - - def test_valid_residue_initialisation_with_valid_variant(self): - nucleotide = ce.Nucleotide("5T1", "5T1", "3T1") - - self.assertEqual("5T1", nucleotide.name) - self.assertEqual(None, nucleotide.parent) - self.assertEqual("5T1", nucleotide.code) - self.assertEqual("3T1", nucleotide._variant) - self.assertDictEqual(NUCLEOTIDES_DATABASE["3T1"], nucleotide._selected_variant) - self.assertEqual(collections.OrderedDict(), nucleotide._atoms) - - def test_invalid_residue_initialisation(self): - with self.assertRaises(ce.UnknownResidueError): - ce.Nucleotide("00000", "00000") - - def test_valid_residue_initialisation_with_nonexistent_variant(self): - with self.assertRaises(ce.InvalidVariantError): - ce.Nucleotide("5T1", "5T1", "00000") - - def test_valid_residue_initialisation_with_invalid_variant(self): - with self.assertRaises(ce.InvalidVariantError): - ce.Nucleotide("5T1", "5T1", "A") - - def test_set_atoms_none(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - - self.compare_atoms_in_5t1(nucleotide._atoms, nucleotide) - - def compare_atoms_in_5t1( - self, atom_list: Union[dict, ce.Nucleotide, list], nucleotide: ce.Nucleotide - ): - try: - atom = atom_list["HO5'"] - except TypeError: - atom = atom_list[0] - self.assertEqual("H", atom.symbol) - self.assertEqual("HO5'", atom.name) - self.assertEqual(1, len(atom.bonds)) - self.assertEqual("O5'", atom.bonds[0]) - self.assertEqual([], atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(nucleotide, atom.parent) - - def test_set_atoms_variant(self): - nucleotide = ce.Nucleotide("A", "adenine", "5T1") - names = [ - "C3'", - "C1'", - "C5'", - "H2'", - "H5'", - "H3'", - "O4'", - "C8", - "C2", - "H1'", - "C6", - "C5", - "C4", - "H5''", - "HO2'", - "N9", - "C4'", - "C2'", - "O2'", - "N1", - "N3", - "N6", - "N7", - "H4'", - "H8", - "H2", - "O5'", - "H61", - "H62", - "O3'", - "HO5'", - ] - nucleotide.set_atoms(names) - - symbols = [ - "C", - "C", - "C", - "H", - "H", - "H", - "O", - "C", - "C", - "H", - "C", - "C", - "C", - "H", - "H", - "N", - "C", - "C", - "O", - "N", - "N", - "N", - "N", - "H", - "H", - "H", - "O", - "H", - "H", - "O", - "H", - ] - bond_atoms = [ - ["C2'", "C4'", "H3'", "O3'"], - ["C2'", "H1'", "N9", "O4'"], - ["C4'", "H5'", "H5''", "O5'"], - ["C2'"], - ["C5'"], - ["C3'"], - ["C1'", "C3'"], - ["H8", "N7", "N9"], - ["N1", "N3", "H2"], - ["C1'"], - ["C5", "N1", "N6"], - ["C4", "C6", "N7"], - ["C5", "N3", "N9"], - ["C5'"], - ["O2'"], - ["C1'", "C4", "C8"], - ["C3'", "C5'", "H4'", "O4'"], - ["C1'", "C3'", "H2'", "O2'"], - ["C2'", "HO2'"], - ["C2", "C6"], - ["C2", "C4"], - ["C6", "H61", "H62"], - ["C5", "C8"], - ["C4'"], - ["C8"], - ["C2"], - ["C5'"], - ["N6"], - ["N6"], - ["C3'", "+R"], - ["O5'"], - ] - groups = ( - [["sugar"]] * 7 - + [["base"], ["base"], ["sugar"]] - + [["base"]] * 3 - + [["sugar"], ["sugar"], ["base"]] - + [["sugar"]] * 3 - + [["base"]] * 4 - + [ - ["sugar"], - ["base"], - ["base"], - ["phosphate"], - ["base"], - ["base"], - ["phosphate"], - ] - ) - - for atom, symbol, name, bonds, group in zip( - nucleotide._atoms.values(), symbols, names, bond_atoms, groups - ): - self.assertEqual(symbol, atom.symbol) - self.assertEqual(name, atom.name) - self.assertEqual(len(bonds), len(atom.bonds)) - for i, bond in enumerate(bonds): - if bond[0] != "+": - self.assertEqual(nucleotide._atoms[bond], atom.bonds[i]) - else: - self.assertEqual(bond, atom.bonds[i]) - self.assertEqual(group, atom._groups) - self.assertEqual(False, atom.ghost) - self.assertEqual(None, atom.index) - self.assertEqual(nucleotide, atom.parent) - - def test_set_atoms_invalid_input(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - with self.assertRaises(ce.InconsistentAtomNamesError): - nucleotide.set_atoms(["HO5"]) - - def test_dunder_getitem(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - self.compare_atoms_in_5t1(nucleotide, nucleotide) - - def test_pickling(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - - pickled = pickle.dumps(nucleotide) - unpickled = pickle.loads(pickled) - - self.compare_base_residues(unpickled, False) - self.compare_atoms_in_5t1(unpickled._atoms, unpickled) - - def test_dunder_repr(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - - if sys.version_info.minor < 12: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.Nucleotide(parent=None, name='5T1', resname='5T1'" - ", code='5T1', variant=None, selected_variant=None, atoms=OrderedDict([(\"HO5'\", MDANSE." - "Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Nucleotide(5T1), name=" - "\"HO5'\", symbol='H', bonds=[Atom(O5')], groups=[], ghost=False, index=None, element='hydrogen', replaces=" - "['OP1', 'OP2', 'P'], o5prime_connected=True, alternatives=[]))]))", - repr(nucleotide), - ) - else: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.Nucleotide(parent=None, name='5T1', resname='5T1'" - ", code='5T1', variant=None, selected_variant=None, atoms=OrderedDict({\"HO5'\": MDANSE." - "Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Nucleotide(5T1), name=" - "\"HO5'\", symbol='H', bonds=[Atom(O5')], groups=[], ghost=False, index=None, element='hydrogen', replaces=" - "['OP1', 'OP2', 'P'], o5prime_connected=True, alternatives=[])}))", - repr(nucleotide), - ) - - def test_dunder_str(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - self.assertEqual('Nucleotide 5T1 (database code "5T1")', str(nucleotide)) - - def test_copy(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - copy = nucleotide.copy() - - self.compare_base_residues(copy, False) - self.compare_atoms_in_5t1(copy._atoms, copy) - - def test_atom_list(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - self.compare_atoms_in_5t1(nucleotide.atom_list, nucleotide) - - def test_number_of_atoms(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - self.assertEqual(1, nucleotide.number_of_atoms) - self.assertEqual(1, nucleotide.total_number_of_atoms) - - def test_build(self): - h5 = { - "nucleotides": [ - [ - b"[0]", - repr("5T1").encode("UTF-8"), - repr("5T1").encode("UTF-8"), - b"None", - ] - ], - "atoms": [ - [repr("H").encode("UTF-8"), repr("HO5'").encode("UTF-8"), b"False"] - ], - } - n = ce.Nucleotide.build(h5, [0], "5T1", "5T1", None) - - expected = ce.Nucleotide("5T1", "5T1", None) - expected.set_atoms(["HO5'"]) - - self.assertEqual(repr(expected), repr(n)) - - def test_serialize_empty_dict(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - dictionary = {} - result = nucleotide.serialize(dictionary) - - self.assertEqual(("nucleotides", 0), result) - self.assertDictEqual( - { - "nucleotides": [["[0]", repr("5T1"), repr("5T1"), "None"]], - "atoms": [[repr("H"), repr("HO5'"), "None", "False"]], - }, - dictionary, - ) - - def test_serialize_nonempty_dict(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - dictionary = {"atoms": [[], [], []], "nucleotides": [[], [], []]} - result = nucleotide.serialize(dictionary) - - self.assertEqual(("nucleotides", 3), result) - self.assertDictEqual( - { - "nucleotides": [[], [], [], ["[3]", repr("5T1"), repr("5T1"), "None"]], - "atoms": [[], [], [], [repr("H"), repr("HO5'"), "None", "False"]], - }, - dictionary, - ) - - -class TestNucleotideChain(unittest.TestCase): - def setUp(self): - self.chain = ce.NucleotideChain("name") - - def test_instantiation(self): - self.assertEqual("name", self.chain.name) - self.assertEqual(None, self.chain.parent) - self.assertEqual([], self.chain._nucleotides) - - def test_set_nucleotides(self): - n1, n2 = self.prepare_nucleotides() - - self.chain.set_nucleotides([n1, n2]) - - self.assertEqual(2, len(self.chain._nucleotides)) - self.assertEqual(self.chain, self.chain._nucleotides[0].parent) - self.assertEqual(self.chain, self.chain._nucleotides[1].parent) - - self.assertEqual([n1, n2], self.chain._nucleotides) - self.assertEqual(n2["P"], n1["O3'"].bonds[1]) - self.assertEqual(n1["O3'"], n2["P"].bonds[3]) - - @staticmethod - def prepare_nucleotides(): - names5 = [ - "C3'", - "C1'", - "C5'", - "H2'", - "H5'", - "H3'", - "O4'", - "C8", - "C2", - "H1'", - "C6", - "C5", - "C4", - "H5''", - "HO2'", - "N9", - "C4'", - "C2'", - "O2'", - "N1", - "N3", - "N6", - "N7", - "H4'", - "H8", - "H2", - "O5'", - "H61", - "H62", - "O3'", - "HO5'", - ] - n1 = ce.Nucleotide("A", "adenine", "5T1") - n1.set_atoms(names5) - - names3 = [ - "C3'", - "C1'", - "C5'", - "H2'", - "H5'", - "H3'", - "O4'", - "C8", - "C2", - "H1'", - "C6", - "C5", - "C4", - "H5''", - "HO2'", - "N9", - "C4'", - "C2'", - "O2'", - "N1", - "N3", - "N6", - "N7", - "H4'", - "H8", - "H2", - "O5'", - "H61", - "H62", - "O3'", - "HO3'", - "OP1", - "OP2", - "P", - ] - n2 = ce.Nucleotide("A", "adenine", "3T1") - n2.set_atoms(names3) - - return n1, n2 - - def test_set_nucleotides_no_atoms_on_5prime_oxygen(self): - n1, n2 = self.prepare_nucleotides() - - with self.assertRaises(ce.InvalidNucleotideChainError) as e: - self.chain.set_nucleotides([n2, n1]) - self.assertEqual( - "The first nucleotide in the chain must contain an atom that is connected to the 5' terminal" - " oxygen (O5').", - str(e.exception)[:105], - ) - - def test_set_nucleotides_first_no_5prime_oxygen(self): - nucleotide = ce.Nucleotide("5T1", "5T1", None) - nucleotide.set_atoms(["HO5'"]) - - with self.assertRaises(ce.InvalidNucleotideChainError) as e: - self.chain.set_nucleotides([nucleotide, nucleotide]) - self.assertEqual( - "The first nucleotide in the chain must contain 5' terminal oxygen atom (O5').", - str(e.exception)[:77], - ) - - def test_set_nucleotides_last_no_ho3prime(self): - names5 = [ - "C3'", - "C1'", - "C5'", - "H2'", - "H5'", - "H3'", - "O4'", - "C8", - "C2", - "H1'", - "C6", - "C5", - "C4", - "H5''", - "HO2'", - "N9", - "C4'", - "C2'", - "O2'", - "N1", - "N3", - "N6", - "N7", - "H4'", - "H8", - "H2", - "O5'", - "H61", - "H62", - "O3'", - "HO5'", - ] - n1 = ce.Nucleotide("A", "adenine", "5T1") - n1.set_atoms(names5) - n2 = ce.Nucleotide("5T1", "5T1", None) - - with self.assertRaises(ce.InvalidNucleotideChainError) as e: - self.chain.set_nucleotides([n1, n2]) - self.assertEqual( - "The last nucleotide in the chain must contain an atom that is connected to the 3' terminal" - " oxygen (O3').", - str(e.exception)[:104], - ) - - def test_set_nucleotides_last_no_o3prime(self): - names5 = [ - "C3'", - "C1'", - "C5'", - "H2'", - "H5'", - "H3'", - "O4'", - "C8", - "C2", - "H1'", - "C6", - "C5", - "C4", - "H5''", - "HO2'", - "N9", - "C4'", - "C2'", - "O2'", - "N1", - "N3", - "N6", - "N7", - "H4'", - "H8", - "H2", - "O5'", - "H61", - "H62", - "O3'", - "HO5'", - ] - n1 = ce.Nucleotide("A", "adenine", "5T1") - n1.set_atoms(names5) - n2 = ce.Nucleotide("3T1", "3T1", None) - n2.set_atoms(["HO3'"]) - - with self.assertRaises(ce.InvalidNucleotideChainError) as e: - self.chain.set_nucleotides([n1, n2]) - self.assertEqual( - "The last nucleotide in the chain must contain 3' terminal oxygen atom (O3').", - str(e.exception)[:76], - ) - - def test_dunder_getitem(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.assertEqual(n1, self.chain[0]) - self.assertEqual(n2, self.chain[1]) - - def test_pickling(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - pickled = pickle.dumps(self.chain) - unpickled = pickle.loads(pickled) - - self.assertEqual("name", unpickled.name) - self.assertEqual(None, unpickled.parent) - - self.assertEqual(2, len(unpickled._nucleotides)) - self.assertEqual(unpickled[1]["P"], unpickled[0]["O3'"].bonds[1]) - self.assertEqual(unpickled[0]["O3'"], unpickled[1]["P"].bonds[3]) - - def test_dunder_str(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.assertEqual("NucleotideChain of 2 nucleotides", str(self.chain)) - - def test_dunder_repr(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.maxDiff = None - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.NucleotideChain(parent=None, name='name', " - "nucleotides=[MDANSE.MolecularDynamics.ChemicalEntity.Nucleotide(parent=MDANSE.Chemistry." - "ChemicalEntity.NucleotideChain(name=name), name='adenine', resname='A', code='A', " - "variant='5T1', selected_variant={'is_3ter_terminus': False, 'atoms':", - repr(self.chain)[:320], - ) - - def test_bases(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.assertEqual( - [ - n1["C8"], - n1["C2"], - n1["C6"], - n1["C5"], - n1["C4"], - n1["N9"], - n1["N1"], - n1["N3"], - n1["N6"], - n1["N7"], - n1["H8"], - n1["H2"], - n1["H61"], - n1["H62"], - n2["C8"], - n2["C2"], - n2["C6"], - n2["C5"], - n2["C4"], - n2["N9"], - n2["N1"], - n2["N3"], - n2["N6"], - n2["N7"], - n2["H8"], - n2["H2"], - n2["H61"], - n2["H62"], - ], - self.chain.bases, - ) - - def test_copy(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - copy = self.chain.copy() - - self.assertEqual(repr(self.chain), repr(copy)) - - def test_residues(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.assertEqual([n1, n2], self.chain.residues) - - def test_number_of_atoms(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.assertEqual(65, self.chain.number_of_atoms) - self.assertEqual(65, self.chain.total_number_of_atoms) - - def test_build(self): - h5 = { - "nucleotides": [ - [ - b"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 2" - b"8, 29, 30]", - b"'A'", - b"'adenine'", - b"'5T1'", - ], - [ - b"[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]", - b"'A'", - b"'adenine'", - b"'3T1'", - ], - ], - "atoms": [ - [b"'C'", b'"C3\'"', b"False"], - [b"'C'", b'"C1\'"', b"False"], - [b"'C'", b'"C5\'"', b"False"], - [b"'H'", b'"H2\'"', b"False"], - [b"'H'", b'"H5\'"', b"False"], - [b"'H'", b'"H3\'"', b"False"], - [b"'O'", b'"O4\'"', b"False"], - [b"'C'", b"'C8'", b"False"], - [b"'C'", b"'C2'", b"False"], - [b"'H'", b'"H1\'"', b"False"], - [b"'C'", b"'C6'", b"False"], - [b"'C'", b"'C5'", b"False"], - [b"'C'", b"'C4'", b"False"], - [b"'H'", b"\"H5''\"", b"False"], - [b"'H'", b'"HO2\'"', b"False"], - [b"'N'", b"'N9'", b"False"], - [b"'C'", b'"C4\'"', b"False"], - [b"'C'", b'"C2\'"', b"False"], - [b"'O'", b'"O2\'"', b"False"], - [b"'N'", b"'N1'", b"False"], - [b"'N'", b"'N3'", b"False"], - [b"'N'", b"'N6'", b"False"], - [b"'N'", b"'N7'", b"False"], - [b"'H'", b'"H4\'"', b"False"], - [b"'H'", b"'H8'", b"False"], - [b"'H'", b"'H2'", b"False"], - [b"'O'", b'"O5\'"', b"False"], - [b"'H'", b"'H61'", b"False"], - [b"'H'", b"'H62'", b"False"], - [b"'O'", b'"O3\'"', b"False"], - [b"'H'", b'"HO5\'"', b"False"], - [b"'C'", b'"C3\'"', b"False"], - [b"'C'", b'"C1\'"', b"False"], - [b"'C'", b'"C5\'"', b"False"], - [b"'H'", b'"H2\'"', b"False"], - [b"'H'", b'"H5\'"', b"False"], - [b"'H'", b'"H3\'"', b"False"], - [b"'O'", b'"O4\'"', b"False"], - [b"'C'", b"'C8'", b"False"], - [b"'C'", b"'C2'", b"False"], - [b"'H'", b'"H1\'"', b"False"], - [b"'C'", b"'C6'", b"False"], - [b"'C'", b"'C5'", b"False"], - [b"'C'", b"'C4'", b"False"], - [b"'H'", b"\"H5''\"", b"False"], - [b"'H'", b'"HO2\'"', b"False"], - [b"'N'", b"'N9'", b"False"], - [b"'C'", b'"C4\'"', b"False"], - [b"'C'", b'"C2\'"', b"False"], - [b"'O'", b'"O2\'"', b"False"], - [b"'N'", b"'N1'", b"False"], - [b"'N'", b"'N3'", b"False"], - [b"'N'", b"'N6'", b"False"], - [b"'N'", b"'N7'", b"False"], - [b"'H'", b'"H4\'"', b"False"], - [b"'H'", b"'H8'", b"False"], - [b"'H'", b"'H2'", b"False"], - [b"'O'", b'"O5\'"', b"False"], - [b"'H'", b"'H61'", b"False"], - [b"'H'", b"'H62'", b"False"], - [b"'O'", b'"O3\'"', b"False"], - [b"'H'", b'"HO3\'"', b"False"], - [b"'O'", b"'OP1'", b"False"], - [b"'O'", b"'OP2'", b"False"], - [b"'P'", b"'P'", b"False"], - ], - "nucleotide_chains": [[b"'name'", b"[0, 1]"]], - } - - nc = ce.NucleotideChain.build(h5, "name", [0, 1]) - - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.maxDiff = None - self.assertEqual(repr(self.chain), repr(nc)) - - def test_serialize_empty_dict(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - dictionary = {} - result = self.chain.serialize(dictionary) - - self.maxDiff = None - self.assertEqual(("nucleotide_chains", 0), result) - self.assertEqual( - ["nucleotides", "atoms", "nucleotide_chains"], list(dictionary.keys()) - ) - self.assertEqual([[repr("name"), "[0, 1]"]], dictionary["nucleotide_chains"]) - self.assertEqual( - [ - [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, " - "25, 26, 27, 28, 29, 30]", - repr("A"), - repr("adenine"), - repr("5T1"), - ], - [ - "[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, " - "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]", - repr("A"), - repr("adenine"), - repr("3T1"), - ], - ], - dictionary["nucleotides"], - ) - - def test_serialize_nonempty_dict(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - dictionary = {"nucleotides": [[], [], []], "nucleotide_chains": [[]]} - result = self.chain.serialize(dictionary) - - self.assertEqual(("nucleotide_chains", 1), result) - self.assertEqual( - ["nucleotides", "nucleotide_chains", "atoms"], list(dictionary.keys()) - ) - self.assertEqual( - [[], [repr("name"), "[3, 4]"]], dictionary["nucleotide_chains"] - ) - self.assertEqual( - [ - [], - [], - [], - [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, " - "22, 23, 24, 25, 26, 27, 28, 29, 30]", - repr("A"), - repr("adenine"), - repr("5T1"), - ], - [ - "[31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, " - "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64]", - repr("A"), - repr("adenine"), - repr("3T1"), - ], - ], - dictionary["nucleotides"], - ) - - def test_sugars(self): - n1, n2 = self.prepare_nucleotides() - self.chain.set_nucleotides([n1, n2]) - - self.assertEqual( - [ - n1["C3'"], - n1["C1'"], - n1["C5'"], - n1["H2'"], - n1["H5'"], - n1["H3'"], - n1["O4'"], - n1["H1'"], - n1["H5''"], - n1["HO2'"], - n1["C4'"], - n1["C2'"], - n1["O2'"], - n1["H4'"], - n2["C3'"], - n2["C1'"], - n2["C5'"], - n2["H2'"], - n2["H5'"], - n2["H3'"], - n2["O4'"], - n2["H1'"], - n2["H5''"], - n2["HO2'"], - n2["C4'"], - n2["C2'"], - n2["O2'"], - n2["H4'"], - ], - self.chain.sugars, - ) - - -class TestPeptideChain(unittest.TestCase): - def setUp(self): - self.chain = ce.PeptideChain("name") - - def test_instantiation(self): - self.assertEqual("name", self.chain.name) - self.assertEqual(None, self.chain.parent) - self.assertEqual([], self.chain._residues) - - def test_set_residues_valid(self): - self.populate_chain() - - self.assertEqual(2, len(self.chain._residues)) - self.assertEqual(self.chain, self.chain._residues[0].parent) - self.assertEqual(self.chain, self.chain._residues[1].parent) - - self.assertEqual( - self.chain._residues[1]["N"], self.chain._residues[0]["C"].bonds[2] - ) - self.assertEqual( - self.chain._residues[0]["C"], self.chain._residues[1]["N"].bonds[2] - ) - - def populate_chain(self): - r1 = ce.Residue("GLY", "glycine1", "NT1") - r1.set_atoms(["HA3", "O", "N", "CA", "HA2", "C", "HT1", "HT2", "HT3"]) - - r2 = ce.Residue("GLY", "glycine2", "CT1") - r2.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C", "OXT"]) - self.chain.set_residues([r1, r2]) - - return r1, r2 - - def test_set_residues_no_atoms_connected_to_terminal_nitrogen(self): - r1 = ce.Residue("GLY", "glycine1", None) - r1.set_atoms(["HA3", "O", "N", "CA", "HA2", "C", "H"]) - - r2 = ce.Residue("GLY", "glycine2", "CT1") - r2.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C", "OXT"]) - - with self.assertRaises(ce.InvalidPeptideChainError) as e: - self.chain.set_residues([r1, r2]) - - self.assertEqual( - "The first residue in the chain must contain an atom that is connected to the terminal " - "nitrogen.", - str(e.exception)[:95], - ) - - def test_set_residues_no_terminal_nitrogen(self): - r = ce.Residue("NT1", "NT1", None) - r.set_atoms(["HT1", "HT2", "HT3"]) - - with self.assertRaises(ce.InvalidPeptideChainError) as e: - self.chain.set_residues([r, r]) - - self.assertEqual( - "The first residue in the chain must contain the terminal nitrogen atom. ", - str(e.exception)[:72], - ) - - def test_set_residues_no_atoms_connected_to_terminal_carbon(self): - r1 = ce.Residue("GLY", "glycine1", "NT1") - r1.set_atoms(["HA3", "O", "N", "CA", "HA2", "C", "HT1", "HT2", "HT3"]) - - with self.assertRaises(ce.InvalidPeptideChainError) as e: - self.chain.set_residues([r1, r1]) - - self.assertEqual( - "The last residue in the chain must contain an atom that is connected to the terminal carbon.", - str(e.exception)[:92], - ) - - def test_set_residues_no_terminal_carbon(self): - r1 = ce.Residue("GLY", "glycine1", "NT1") - r1.set_atoms(["HA3", "O", "N", "CA", "HA2", "C", "HT1", "HT2", "HT3"]) - - r2 = ce.Residue("CT1", "CT1", "CT1") - r2.set_atoms(["OXT"]) - - with self.assertRaises(ce.InvalidPeptideChainError) as e: - self.chain.set_residues([r1, r2]) - - self.assertEqual( - "The last residue in the chain must contain the terminal carbon atom. ", - str(e.exception)[:69], - ) - - def test_dunder_getitem(self): - r1, r2 = self.populate_chain() - - self.assertEqual(r1, self.chain[0]) - self.assertEqual(r2, self.chain[1]) - - def test_pickling(self): - self.populate_chain() - - pickled = pickle.dumps(self.chain) - unpickled = pickle.loads(pickled) - - self.assertEqual("name", unpickled.name) - self.assertEqual(None, unpickled.parent) - self.assertEqual(2, len(unpickled._residues)) - - def test_dunder_str(self): - self.populate_chain() - self.assertEqual("PeptideChain of 2 residues", str(self.chain)) - - def test_dunder_repr(self): - self.populate_chain() - - self.maxDiff = None - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.PeptideChain(parent=None, name='name', " - "residues=[MDANSE.MolecularDynamics.ChemicalEntity.Residue(parent=MDANSE.Chemistry." - "ChemicalEntity.PeptideChain(name), name='glycine1', code='GLY', variant='NT1', " - "selected_variant={'is_n_terminus': True, ", - repr(self.chain)[:281], - ) - - def test_atom_list(self): - r1, r2 = self.populate_chain() - - self.assertEqual( - [ - r1["HA3"], - r1["O"], - r1["N"], - r1["CA"], - r1["HA2"], - r1["C"], - r1["HT1"], - r1["HT2"], - r1["HT3"], - r2["H"], - r2["HA3"], - r2["O"], - r2["N"], - r2["CA"], - r2["HA2"], - r2["C"], - r2["OXT"], - ], - self.chain.atom_list, - ) - - def test_backbone(self): - r1, r2 = self.populate_chain() - - self.assertEqual( - [ - r1["O"], - r1["N"], - r1["CA"], - r1["HA2"], - r1["C"], - r1["HT1"], - r1["HT2"], - r1["HT3"], - r2["H"], - r2["O"], - r2["N"], - r2["CA"], - r2["HA2"], - r2["C"], - r2["OXT"], - ], - self.chain.backbone, - ) - - def test_copy(self): - self.populate_chain() - copy = self.chain.copy() - - self.maxDiff = None - self.assertEqual(repr(self.chain), repr(copy)) - - def test_number_of_atoms(self): - self.populate_chain() - self.assertEqual(17, self.chain.number_of_atoms) - self.assertEqual(17, self.chain.total_number_of_atoms) - - def test_peptide_chains(self): - self.populate_chain() - self.assertEqual([self.chain], self.chain.peptide_chains) - - def test_peptides(self): - r1, r2 = self.populate_chain() - self.assertEqual( - [r1["O"], r1["N"], r1["C"], r2["H"], r2["O"], r2["N"], r2["C"]], - self.chain.peptides, - ) - - def test_residues(self): - r1, r2 = self.populate_chain() - self.assertEqual([r1, r2], self.chain.residues) - - def test_build(self): - h5 = { - "residues": [ - [b"[0, 1, 2, 3, 4, 5, 6, 7, 8]", b"'GLY'", b"'glycine1'", b"'NT1'"], - [b"[9, 10, 11, 12, 13, 14, 15, 16]", b"'GLY'", b"'glycine2'", b"'CT1'"], - ], - "atoms": [ - [b"'H'", b"'HA3'", b"False"], - [b"'O'", b"'O'", b"False"], - [b"'N'", b"'N'", b"False"], - [b"'C'", b"'CA'", b"False"], - [b"'H'", b"'HA2'", b"False"], - [b"'C'", b"'C'", b"False"], - [b"'H'", b"'HT1'", b"False"], - [b"'H'", b"'HT2'", b"False"], - [b"'H'", b"'HT3'", b"False"], - [b"'H'", b"'H'", b"False"], - [b"'H'", b"'HA3'", b"False"], - [b"'O'", b"'O'", b"False"], - [b"'N'", b"'N'", b"False"], - [b"'C'", b"'CA'", b"False"], - [b"'H'", b"'HA2'", b"False"], - [b"'C'", b"'C'", b"False"], - [b"'O'", b"'OXT'", b"False"], - ], - "peptide_chains": [[b"'name'", b"[0, 1]"]], - } - pc = ce.PeptideChain.build(h5, "name", [0, 1]) - - self.populate_chain() - - self.maxDiff = None - self.assertEqual(repr(self.chain), repr(pc)) - - def test_serialize_empty_dict(self): - self.populate_chain() - dictionary = {} - result = self.chain.serialize(dictionary) - - self.maxDiff = None - self.assertEqual(("peptide_chains", 0), result) - self.assertEqual( - ["residues", "atoms", "peptide_chains"], list(dictionary.keys()) - ) - self.assertEqual([[repr("name"), "[0, 1]"]], dictionary["peptide_chains"]) - self.assertEqual( - [ - [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8]", - repr("GLY"), - repr("glycine1"), - repr("NT1"), - ], - [ - "[9, 10, 11, 12, 13, 14, 15, 16]", - repr("GLY"), - repr("glycine2"), - repr("CT1"), - ], - ], - dictionary["residues"], - ) - - def test_serialize_nonempty_dict(self): - self.populate_chain() - dictionary = {"residues": [[], [], []], "peptide_chains": [[], [], []]} - result = self.chain.serialize(dictionary) - - self.assertEqual(("peptide_chains", 3), result) - self.assertEqual( - ["residues", "peptide_chains", "atoms"], list(dictionary.keys()) - ) - self.assertEqual( - [[], [], [], [repr("name"), "[3, 4]"]], dictionary["peptide_chains"] - ) - self.assertEqual( - [ - [], - [], - [], - [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8]", - repr("GLY"), - repr("glycine1"), - repr("NT1"), - ], - [ - "[9, 10, 11, 12, 13, 14, 15, 16]", - repr("GLY"), - repr("glycine2"), - repr("CT1"), - ], - ], - dictionary["residues"], - ) - - def test_sidechains(self): - r1, r2 = self.populate_chain() - self.assertEqual([r1["HA3"], r2["HA3"]], self.chain.sidechains) - - -class TestProtein(unittest.TestCase): - def setUp(self): - self.protein = ce.Protein("name") - - def test_instantiation(self): - self.assertEqual("name", self.protein.name) - self.assertEqual(None, self.protein.parent) - self.assertEqual([], self.protein._peptide_chains) - - def test_set_peptide_chains(self): - chain = self.populate_protein() - - self.assertEqual([chain], self.protein._peptide_chains) - self.assertEqual(self.protein, self.protein._peptide_chains[0].parent) - - def populate_protein(self): - r1 = ce.Residue("GLY", "glycine1", "NT1") - r1.set_atoms(["HA3", "O", "N", "CA", "HA2", "C", "HT1", "HT2", "HT3"]) - - r2 = ce.Residue("GLY", "glycine2", "CT1") - r2.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C", "OXT"]) - - chain = ce.PeptideChain("name") - chain.set_residues([r1, r2]) - self.protein.set_peptide_chains([chain]) - - return chain - - def test_pickling(self): - chain = self.populate_protein() - - pickled = pickle.dumps(self.protein) - unpickled = pickle.loads(pickled) - - self.maxDiff = None - self.assertEqual("name", unpickled.name) - self.assertEqual(None, unpickled.parent) - self.assertEqual(1, len(unpickled._peptide_chains)) - self.assertEqual(repr(chain), repr(unpickled._peptide_chains[0])) - - def test_dunder_getitem(self): - chain = self.populate_protein() - self.assertEqual(chain, self.protein[0]) - - def test_dunder_repr(self): - self.populate_protein() - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.Protein(parent=None, name='name', peptide_chains=" - "[MDANSE.MolecularDynamics.ChemicalEntity.PeptideChain(parent=MDANSE.Chemistry.ChemicalEntity." - "Protein(name=name), name='name'", - repr(self.protein)[:213], - ) - - def test_dunder_str(self): - self.populate_protein() - self.assertEqual( - "Protein name consisting of 1 peptide chains", str(self.protein) - ) - - def test_atom_list(self): - chain = self.populate_protein() - - self.assertEqual( - [ - chain[0]["HA3"], - chain[0]["O"], - chain[0]["N"], - chain[0]["CA"], - chain[0]["HA2"], - chain[0]["C"], - chain[0]["HT1"], - chain[0]["HT2"], - chain[0]["HT3"], - chain[1]["H"], - chain[1]["HA3"], - chain[1]["O"], - chain[1]["N"], - chain[1]["CA"], - chain[1]["HA2"], - chain[1]["C"], - chain[1]["OXT"], - ], - self.protein.atom_list, - ) - - def test_backbone(self): - chain = self.populate_protein() - self.assertEqual( - [ - chain[0]["O"], - chain[0]["N"], - chain[0]["CA"], - chain[0]["HA2"], - chain[0]["C"], - chain[0]["HT1"], - chain[0]["HT2"], - chain[0]["HT3"], - chain[1]["H"], - chain[1]["O"], - chain[1]["N"], - chain[1]["CA"], - chain[1]["HA2"], - chain[1]["C"], - chain[1]["OXT"], - ], - self.protein.backbone, - ) - - def test_copy(self): - chain = self.populate_protein() - copy = self.protein.copy() - - self.maxDiff = None - self.assertEqual("name", copy.name) - self.assertEqual(None, copy.parent) - self.assertEqual(1, len(copy._peptide_chains)) - self.assertEqual(repr(chain), repr(copy._peptide_chains[0])) - - def test_number_of_atoms(self): - self.populate_protein() - - self.assertEqual(17, self.protein.number_of_atoms) - self.assertEqual(17, self.protein.total_number_of_atoms) - - def test_peptide_chains(self): - chain = self.populate_protein() - self.assertEqual([chain], self.protein.peptide_chains) - - def test_peptides(self): - chain = self.populate_protein() - self.assertEqual( - [ - chain[0]["O"], - chain[0]["N"], - chain[0]["C"], - chain[1]["H"], - chain[1]["O"], - chain[1]["N"], - chain[1]["C"], - ], - self.protein.peptides, - ) - - def test_residues(self): - chain = self.populate_protein() - self.assertEqual(chain.residues, self.protein.residues) - - def test_build(self): - h5 = { - "proteins": [["'name'", "[0]"]], - "residues": [ - [b"[0, 1, 2, 3, 4, 5, 6, 7, 8]", b"'GLY'", b"'glycine1'", b"'NT1'"], - [b"[9, 10, 11, 12, 13, 14, 15, 16]", b"'GLY'", b"'glycine2'", b"'CT1'"], - ], - "atoms": [ - [b"'H'", b"'HA3'", b"False"], - [b"'O'", b"'O'", b"False"], - [b"'N'", b"'N'", b"False"], - [b"'C'", b"'CA'", b"False"], - [b"'H'", b"'HA2'", b"False"], - [b"'C'", b"'C'", b"False"], - [b"'H'", b"'HT1'", b"False"], - [b"'H'", b"'HT2'", b"False"], - [b"'H'", b"'HT3'", b"False"], - [b"'H'", b"'H'", b"False"], - [b"'H'", b"'HA3'", b"False"], - [b"'O'", b"'O'", b"False"], - [b"'N'", b"'N'", b"False"], - [b"'C'", b"'CA'", b"False"], - [b"'H'", b"'HA2'", b"False"], - [b"'C'", b"'C'", b"False"], - [b"'O'", b"'OXT'", b"False"], - ], - "peptide_chains": [[b"'name'", b"[0, 1]"]], - } - p = ce.Protein.build(h5, "name", [0]) - - self.populate_protein() - - self.maxDiff = 0 - self.assertEqual(repr(self.protein), repr(p)) - - def test_serialize_empty_dict(self): - self.populate_protein() - dictionary = {} - result = self.protein.serialize(dictionary) - - self.assertEqual(("proteins", 0), result) - self.assertEqual( - ["proteins", "residues", "atoms", "peptide_chains"], list(dictionary.keys()) - ) - self.assertEqual([[repr("name"), "[0]"]], dictionary["proteins"]) - self.assertEqual([[repr("name"), "[0, 1]"]], dictionary["peptide_chains"]) - - def test_serialize_nonempty_dict(self): - self.populate_protein() - dictionary = {"proteins": [[], [], []], "peptide_chains": [[], [], []]} - result = self.protein.serialize(dictionary) - - self.assertEqual(("proteins", 3), result) - self.assertEqual( - ["proteins", "peptide_chains", "residues", "atoms"], list(dictionary.keys()) - ) - self.assertEqual([[], [], [], [repr("name"), "[3]"]], dictionary["proteins"]) - self.assertEqual( - [[], [], [], [repr("name"), "[0, 1]"]], dictionary["peptide_chains"] - ) - - def test_sidechains(self): - chain = self.populate_protein() - self.assertEqual([chain[0]["HA3"], chain[1]["HA3"]], self.protein.sidechains) - - -class TestTranslateAtomNames(unittest.TestCase): - def test_valid(self): - result = ce.translate_atom_names(MOLECULES_DATABASE, "WAT", ["O", "HW2", "H1"]) - self.assertEqual(["OW", "HW2", "HW1"], result) - - def test_subset_of_all_atoms(self): - result = ce.translate_atom_names(MOLECULES_DATABASE, "WAT", ["O", "HW2"]) - self.assertEqual(["OW", "HW2"], result) - - def test_multiple_of_one_atom(self): - result = ce.translate_atom_names( - MOLECULES_DATABASE, "WAT", ["O", "HW2", "HW2", "HW2", "HW2"] - ) - self.assertEqual(["OW", "HW2", "HW2", "HW2", "HW2"], result) - - def test_invalid_molname(self): - with self.assertRaises(ce.UnknownMoleculeError): - ce.translate_atom_names(MOLECULES_DATABASE, "00000", ["OW", "HW1", "HW2"]) - - def test_invalid_atom(self): - with self.assertRaises(ce.UnknownAtomError): - ce.translate_atom_names(MOLECULES_DATABASE, "WAT", [""]) - - -class TestChemicalSystem(unittest.TestCase): - def setUp(self): - self.system = ce.ChemicalSystem("name") - - def test_instantiation(self): - self.assertEqual("name", self.system.name) - self.assertEqual(None, self.system.parent) - self.assertEqual([], self.system.chemical_entities) - self.assertEqual(None, self.system._configuration) - self.assertEqual(0, self.system._number_of_atoms) - self.assertEqual(0, self.system._total_number_of_atoms) - self.assertEqual(None, self.system._atoms) - - def test_add_chemical_entity_valid(self): - cluster = ce.AtomCluster("name", [ce.Atom(ghost=False), ce.Atom(ghost=True)]) - self.system.add_chemical_entity(cluster) - - self.assertEqual(1, self.system._number_of_atoms) - self.assertEqual(2, self.system._total_number_of_atoms) - self.assertEqual(self.system, cluster.parent) - self.assertEqual([cluster], self.system.chemical_entities) - self.assertEqual(None, self.system._configuration) - self.assertEqual(None, self.system._atoms) - - def test_add_chemical_entity_invalid_input(self): - with self.assertRaises(ce.InvalidChemicalEntityError): - self.system.add_chemical_entity([]) - - def test_pickling(self): - molecule = ce.Molecule("WAT", "name") - self.system.add_chemical_entity(molecule) - - pickled = pickle.dumps(self.system) - unpickled = pickle.loads(pickled) - - self.assertEqual("name", unpickled.name) - self.assertEqual(None, unpickled.parent) - self.assertEqual(3, unpickled._number_of_atoms) - self.assertEqual(3, unpickled._total_number_of_atoms) - self.assertEqual(repr(molecule), repr(unpickled.chemical_entities[0])) - self.assertEqual(None, unpickled._configuration) - self.assertEqual(None, unpickled._atoms) - - def test_dunder_repr(self): - molecule = ce.Molecule("WAT", "name") - self.system.add_chemical_entity(molecule) - - self.maxDiff = None - if sys.version_info.minor < 12: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.ChemicalSystem(parent=None, name='name', " - "chemical_entities=[MDANSE.MolecularDynamics.ChemicalEntity.Molecule(parent=MDANSE.Chemistry." - "ChemicalEntity.ChemicalSystem(name), name='name', atoms=OrderedDict([('OW', MDANSE.Chemistry." - "ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Molecule(name), name='OW', " - "symbol='O', bonds=[Atom(HW1), Atom(HW2)], groups=[], ghost=False, index=0, element='oxygen', alternatives=" - "['O', 'OH2'])), ('HW2', MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry." - "ChemicalEntity.Molecule(name), name='HW2', symbol='H', bonds=[Atom(OW)], groups=[], " - "ghost=False, index=1, element='hydrogen', alternatives=['H2'])), ('HW1', MDANSE.Chemistry.ChemicalEntity." - "Atom(parent=MDANSE.Chemistry.ChemicalEntity.Molecule(name), name='HW1', symbol='H', bonds=" - "[Atom(OW)], groups=[], ghost=False, index=2, element='hydrogen', alternatives=['H1']))]), code='WAT')], " - "configuration=None, number_of_atoms=3, total_number_of_atoms=3, bonds=[], atoms=None)", - repr(self.system), - ) - else: - self.assertEqual( - "MDANSE.MolecularDynamics.ChemicalEntity.ChemicalSystem(parent=None, name='name', " - "chemical_entities=[MDANSE.MolecularDynamics.ChemicalEntity.Molecule(parent=MDANSE.Chemistry." - "ChemicalEntity.ChemicalSystem(name), name='name', atoms=OrderedDict({'OW': MDANSE.Chemistry." - "ChemicalEntity.Atom(parent=MDANSE.Chemistry.ChemicalEntity.Molecule(name), name='OW', " - "symbol='O', bonds=[Atom(HW1), Atom(HW2)], groups=[], ghost=False, index=0, element='oxygen', alternatives=" - "['O', 'OH2']), 'HW2': MDANSE.Chemistry.ChemicalEntity.Atom(parent=MDANSE.Chemistry." - "ChemicalEntity.Molecule(name), name='HW2', symbol='H', bonds=[Atom(OW)], groups=[], " - "ghost=False, index=1, element='hydrogen', alternatives=['H2']), 'HW1': MDANSE.Chemistry.ChemicalEntity." - "Atom(parent=MDANSE.Chemistry.ChemicalEntity.Molecule(name), name='HW1', symbol='H', bonds=" - "[Atom(OW)], groups=[], ghost=False, index=2, element='hydrogen', alternatives=['H1'])}), code='WAT')], " - "configuration=None, number_of_atoms=3, total_number_of_atoms=3, bonds=[], atoms=None)", - repr(self.system), - ) - - def test_dunder_str(self): - atom1 = ce.Atom(ghost=False) - atom2 = ce.Atom(ghost=False) - cluster = ce.AtomCluster("name", [atom1, atom2]) - self.system.add_chemical_entity(cluster) - self.assertEqual( - "ChemicalSystem name consisting of 1 chemical entities", str(self.system) - ) - - def test_atom_list(self): - atom1 = ce.Atom(ghost=False) - atom2 = ce.Atom(ghost=False) - cluster = ce.AtomCluster("name", [atom1, atom2]) - self.system.add_chemical_entity(cluster) - - self.assertEqual([atom1, atom2], self.system.atom_list) - - def test_atoms(self): - atom1 = ce.Atom(ghost=False) - atom2 = ce.Atom(ghost=False) - cluster = ce.AtomCluster("name", [atom1, atom2]) - self.system.add_chemical_entity(cluster) - - atom1._index = 1 - atom2._index = 0 - - self.maxDiff = None - self.assertEqual([atom2, atom1], self.system.atoms) - - def test_configuration_setter_valid(self): - config = DummyConfiguration(self.system) - self.system.configuration = config - - self.assertEqual(config, self.system.configuration) - - def test_configuration_setter_invalid(self): - other_system = ce.ChemicalSystem("another_name") - config = DummyConfiguration(self.system) - - with self.assertRaises(ce.InconsistentChemicalSystemError): - other_system.configuration = config - - def test_copy(self): - molecule = ce.Molecule("WAT", "name") - self.system.add_chemical_entity(molecule) - copy = self.system.copy() - - self.assertEqual("name", copy.name) - self.assertEqual(None, copy.parent) - self.assertEqual(repr(molecule), repr(copy.chemical_entities[0])) - self.assertEqual(None, copy._configuration) - self.assertEqual(3, copy._number_of_atoms) - self.assertEqual(3, copy._total_number_of_atoms) - self.assertEqual(self.system.atoms, copy.atoms) - - def test_load_valid(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("atoms".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["atoms"] = [ - [repr("H").encode("UTF-8"), repr("H1").encode("UTF-8"), b"0", b"False"] - ] - self.system.load(file) - - self.assertEqual(None, self.system._h5_file) - self.assertEqual("new", self.system.name) - self.assertEqual(None, self.system.parent) - self.assertEqual( - repr(ce.Atom(name="H1", parent=self.system, index=0)), - repr(self.system.chemical_entities[0]), - ) - self.assertEqual(None, self.system._configuration) - self.assertEqual(1, self.system._number_of_atoms) - self.assertEqual(1, self.system._total_number_of_atoms) - self.assertEqual(None, self.system._atoms) - - def test_load_corrupt_file_skeleton_invalid_entity_type(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("INVALID".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["atoms"] = [[b"H", b"H1", b"0", b"False"]] - - with self.assertRaises(ce.CorruptedFileError): - self.system.load(file) - - def test_load_corrupt_file_skeleton_entity_not_in_system(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("atoms".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["INVALID"] = [[b"H", b"H1", b"0", b"False"]] - - with self.assertRaises(ce.CorruptedFileError): - self.system.load(file) - - def test_load_corrupt_file_skeleton_index_out_of_range(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("atoms".encode(encoding="UTF-8", errors="strict"), 1) - ] - file["/chemical_system"]["atoms"] = [[b"H", b"H1", b"0", b"False"]] - - with self.assertRaises(ce.CorruptedFileError): - self.system.load(file) - - def test_load_corrupt_file_ast_error(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("atoms".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["atoms"] = [[b'"', b"H1", b"0", b"False"]] - - with self.assertRaises(ce.CorruptedFileError): - self.system.load(file) - - def test_load_corrupt_file_inconsistent_atoms(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("molecules".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["atoms"] = [ - [ - repr("H").encode("UTF-8"), - repr("H1").encode("UTF-8"), - str(i).encode("UTF-8"), - b"False", - ] - for i in range(3) - ] - file["/chemical_system"]["molecules"] = [ - [b"[0, 1, 2]", repr("WAT").encode("UTF-8"), repr("water").encode("UTF-8")] - ] - - with self.assertRaises(ce.CorruptedFileError) as e: - self.system.load(file) - self.assertEqual( - "Could not reconstruct from the HDF5 " - "Trajectory because its constituent atoms recorded in the trajectory are different", - str(e.exception)[:168], - ) - - def test_load_corrupt_file_index_out_of_bounds(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("molecules".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["atoms"] = [ - [repr("H").encode("UTF-8"), repr("H1").encode("UTF-8"), b"0", b"False"] - ] - file["/chemical_system"]["molecules"] = [ - [b"[0, 1, 2]", repr("WAT").encode("UTF-8"), repr("water").encode("UTF-8")] - ] - - with self.assertRaises(ce.CorruptedFileError) as e: - self.system.load(file) - self.assertEqual( - "Could not reconstruct from the HDF5 " - "Trajectory because one or more of its constituent atoms are missing", - str(e.exception)[:154], - ) - - def test_load_corrupt_file_missing_entity(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("molecules".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["molecules"] = [ - [b"[0, 1, 2]", repr("WAT").encode("UTF-8"), repr("water").encode("UTF-8")] - ] - - with self.assertRaises(ce.CorruptedFileError) as e: - self.system.load(file) - self.assertEqual( - "Could not reconstruct from the HDF5 " - "Trajectory because one of its constituent parts could not be found in the trajectory", - str(e.exception)[:171], - ) - - def test_load_corrupt_file_incorrect_arguments(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("proteins".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["peptide_chains"] = [ - [ - repr("name").encode("UTF-8"), - b"[0, 1]", - repr("INVALID_ARGUMENT").encode("UTF-8"), - ], - ] - file["/chemical_system"]["proteins"] = [ - [repr("protein").encode("UTF-8"), b"[0, 1, 2]"] - ] - - with self.assertRaises(ce.CorruptedFileError) as e: - self.system.load(file) - self.assertEqual( - "Could not reconstruct from the HDF5 " - "Trajectory because the data associated with it does not match the expected arguments", - str(e.exception)[:170], - ) - - def test_load_corrupt_file_ast_error_when_building(self): - file = StubHDFFile() - file["/chemical_system"] = StubHDFFile() - file["/chemical_system"].attrs["name"] = "new" - - file["/chemical_system/contents"] = [ - ("molecules".encode(encoding="UTF-8", errors="strict"), 0) - ] - file["/chemical_system"]["atoms"] = [ - [repr("H").encode("UTF-8"), b"H1", b"0", b"False"] - ] - file["/chemical_system"]["molecules"] = [ - [b"[0, 1, 2]", repr("WAT").encode("UTF-8"), repr("water").encode("UTF-8")] - ] - - with self.assertRaises(ce.CorruptedFileError) as e: - self.system.load(file) - self.assertEqual( - "Could not reconstruct from the HDF5 " - "Trajectory because the data associated with it is in an incorrect format.", - str(e.exception)[:160], - ) - - def test_serialize(self): - molecule = ce.Molecule("WAT", "water") - self.system.add_chemical_entity(molecule) - file = StubHDFFile() - self.system.serialize(file) - - self.assertEqual("name", file["/chemical_system"].attrs["name"]) - self.assertEqual( - [["[0, 1, 2]", repr("WAT"), repr("water")]], - file["/chemical_system"]["molecules"], - ) - self.assertEqual( - [ - [repr("O"), repr("OW"), "0", "False"], - [repr("H"), repr("HW2"), "1", "False"], - [repr("H"), repr("HW1"), "2", "False"], - ], - file["/chemical_system"]["atoms"], - ) - self.assertEqual([("molecules", "0")], file["/chemical_system"]["contents"]) - - -class DummyConfiguration: - def __init__(self, system: ce.ChemicalSystem): - self.chemical_system = system - - -class StubHDFFile(dict): - attrs = {} - - def close(self): - pass - - def create_group(self, value: str): - self[value] = StubHDFFile() - return self[value] - - def create_dataset(self, name: str, data, dtype): - self[name] = data - - -class TestChemicalEntity(unittest.TestCase): - def setUp(self): - self.r1 = ce.Residue("GLY", "glycine1", "NT1") - self.r1.set_atoms(["HA3", "O", "N", "CA", "HA2", "C", "HT1", "HT2", "HT3"]) - - self.r2 = ce.Residue("GLY", "glycine2", "CT1") - self.r2.set_atoms(["H", "HA3", "O", "N", "CA", "HA2", "C", "OXT"]) - - self.chain = ce.PeptideChain("chain") - self.chain.set_residues([self.r1, self.r2]) - - self.protein = ce.Protein("protein") - self.protein.set_peptide_chains([self.chain]) - - def test_full_name(self): - self.assertEqual("protein.chain.glycine1.N", self.r1["N"].full_name) - self.assertEqual("protein.chain.glycine2", self.r2.full_name) - - def test_group(self): - self.assertEqual( - [self.r1["HA3"], self.r2["HA3"]], self.protein.group("sidechain") - ) - - def test_center_of_mass(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - group = ce.AtomGroup([self.r1["HT3"], self.r1["HA2"], self.r2["O"]]) - - configuration = {"coordinates": np.ones((17, 3))} - self.assertTrue( - np.allclose(np.array([1, 1, 1]), self.protein.center_of_mass(configuration)) - ) - self.assertTrue( - np.allclose(np.array([1, 1, 1]), group.center_of_mass(configuration)) - ) - - def test_mass(self): - self.assertAlmostEqual(132.1176, self.protein.mass) - self.assertAlmostEqual(1.0079, self.r1["HA3"].mass) - - def test_masses(self): - for i, j in zip( - [ - 1.0079, - 15.9994, - 14.0067, - 12.0107, - 1.0079, - 12.0107, - 1.0079, - 1.0079, - 1.0079, - 1.0079, - 1.0079, - 15.9994, - 14.0067, - 12.0107, - 1.0079, - 12.0107, - 15.9994, - ], - self.protein.masses, - ): - self.assertAlmostEqual(i, j) - - for i, j in zip([1.0079], self.r1["HA3"].masses): - self.assertAlmostEqual(i, j) - - def test_find_transformation_as_quaternion_valid(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - conf = StubConfiguration(False, system, coordinates=np.ones((17, 3))) - system.configuration = conf - - self.assertTupleEqual( - ( - Quaternion([1.0, 0.0, 0.0, 0.0]), - Vector(1.0, 1.0, 1.0), - Vector(1.0, 1.0, 1.0), - 0.0, - ), - self.protein.find_transformation_as_quaternion(conf), - ) - - def test_find_transformation_as_quaternion_entity_not_part_of_system(self): - conf = StubConfiguration(False, None, coordinates=np.ones((17, 3))) - with self.assertRaises(ce.ChemicalEntityError): - self.protein.find_transformation_as_quaternion(conf) - - def test_find_transformation_as_quaternion_periodic_configuration(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - conf = StubConfiguration(True, system) - system.configuration = conf - - with self.assertRaises(ValueError) as e: - self.protein.find_transformation_as_quaternion(conf) - self.assertEqual( - "superposition in periodic configurations is not defined, therefore the configuration of the " - "root chemical system of this chemical entity must not be periodic.", - str(e.exception), - ) - - def test_find_transformation_as_quaternion_conf1_incompatible(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - conf = StubConfiguration(False, system) - system.configuration = conf - - conf.chemical_system = ce.ChemicalSystem("different system") - with self.assertRaises(ValueError) as e: - self.protein.find_transformation_as_quaternion(conf) - self.assertEqual( - "conformations come from different chemical systems: the root chemical system of this " - 'chemical entity is "name" but the chemical system registered with the provided configuration ' - '(conf1) is "different system".', - str(e.exception)[:208], - ) - - def test_find_transformation_as_quaternion_conf2_incompatible(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - conf = StubConfiguration(False, system) - system.configuration = conf - - with self.assertRaises(ValueError) as e: - self.protein.find_transformation_as_quaternion( - conf, StubConfiguration(False, ce.ChemicalSystem("diff")) - ) - self.assertEqual( - "conformations come from different chemical systems: the root chemical system of this " - 'chemical entity is "name" but the chemical system registered with the provided configuration ' - '(conf2) is "diff".', - str(e.exception)[:196], - ) - - def test_find_transformation(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - conf = StubConfiguration(False, system, coordinates=np.ones((17, 3))) - system.configuration = conf - - transformation, rms = self.protein.find_transformation(conf) - self.assertTrue(isinstance(transformation, RotationTranslation)) - self.assertEqual( - Tensor([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], False), - transformation.tensor, - ) - self.assertEqual(Vector([0.0, 0.0, 0.0]), transformation.vector) - self.assertEqual(0, rms) - - def test_center_and_moment_of_inertia(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - group = ce.AtomGroup([self.r1["HT3"], self.r1["HA2"], self.r2["O"]]) - - configuration = {"coordinates": np.ones((50, 3))} - self.assertEqual( - ( - Vector(1.0, 1.0, 1.0), - Tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]), - ), - self.protein.center_and_moment_of_inertia(configuration), - ) - self.assertEqual( - ( - Vector(1.0, 1.0, 1.0), - Tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]), - ), - self.protein.centre_and_moment_of_inertia(configuration), - ) - self.assertEqual( - ( - Vector(1.0, 1.0, 1.0), - Tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]), - ), - group.center_and_moment_of_inertia(configuration), - ) - - def test_normalizing_transformation_valid(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - configuration = {"coordinates": np.ones((50, 3))} - - no_representation = self.protein.normalizing_transformation(configuration, None) - ir = self.protein.normalizing_transformation(configuration, "Ir") - iir = self.protein.normalizing_transformation(configuration, "IIr") - iiir = self.protein.normalizing_transformation(configuration, "IIIr") - il = self.protein.normalizing_transformation(configuration, "Il") - iil = self.protein.normalizing_transformation(configuration, "IIl") - iiil = self.protein.normalizing_transformation(configuration, "IIIl") - - tensors = [ - [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], - [0.0, 0.0, 1.0], - [0, 1, 0], - [1, 0, 0], - [0, 0, 1], - [1, 0, 0], - [0, 1, 0], - ] - vectors = [ - Vector([-1.0, -1.0, -1.0]), - Tensor(-1), - Tensor(-1), - Tensor(-1), - Tensor(-1), - Tensor(-1), - Tensor(-1), - ] - self.assertTrue(isinstance(no_representation, RotationTranslation)) - for expected_tensor, expected_vector, result in zip( - tensors, vectors, [no_representation, ir, iir, iiir, il, iil, iiil] - ): - self.assertEqual(Tensor(expected_tensor), result.tensor) - self.assertEqual(expected_vector, result.vector) - - def test_normalizing_transformation_invalid_representation(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - configuration = {"coordinates": np.ones((50, 3))} - with self.assertRaises(ValueError): - self.protein.normalizing_transformation(configuration, "INVALID INPUT") - - def test_root_chemical_system(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - atom = ce.Atom() - - self.assertEqual(system, self.protein.root_chemical_system) - self.assertEqual(system, self.r1["O"].root_chemical_system) - self.assertEqual(None, atom.root_chemical_system) - - def test_top_level_chemical_entity(self): - system = ce.ChemicalSystem("name") - system.add_chemical_entity(self.protein) - atom = ce.Atom() - self.assertEqual(self.protein, self.protein.top_level_chemical_entity) - self.assertEqual(self.protein, self.r1["O"].top_level_chemical_entity) - self.assertEqual(atom, atom.top_level_chemical_entity) - - -class StubConfiguration(dict): - def __init__(self, is_periodic: bool, chemical_system, **kwargs): - self.is_periodic = is_periodic - self.chemical_system = chemical_system - super().__init__(**kwargs) diff --git a/MDANSE/Tests/UnitTests/test_configuration.py b/MDANSE/Tests/UnitTests/test_configuration.py index 945bb3de5c..bd39310c4d 100644 --- a/MDANSE/Tests/UnitTests/test_configuration.py +++ b/MDANSE/Tests/UnitTests/test_configuration.py @@ -17,7 +17,7 @@ import numpy as np -from MDANSE.Chemistry.ChemicalEntity import Atom, AtomCluster, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.Mathematics.Transformation import Translation, Rotation from MDANSE.Mathematics.LinearAlgebra import Vector from MDANSE.MolecularDynamics.Configuration import ( @@ -40,11 +40,12 @@ def setUp(self): self._nAtoms = 4 atoms = [] + cluster = [] for i in range(self._nAtoms): - atoms.append(Atom(symbol="H")) - ac = AtomCluster("", atoms) - - self.chem_system.add_chemical_entity(ac) + atoms.append("H") + cluster.append(i) + self.chem_system.initialise_atoms(atoms) + self.chem_system.add_clusters([cluster]) def test_dunder_init_valid(self): coords = np.random.uniform(0, 1, (self._nAtoms, 3)) @@ -182,11 +183,12 @@ def setUp(self): self._nAtoms = 4 atoms = [] + cluster = [] for i in range(self._nAtoms): - atoms.append(Atom(symbol="H")) - ac = AtomCluster("", atoms) - - self.chem_system.add_chemical_entity(ac) + atoms.append("H") + cluster.append(i) + self.chem_system.initialise_atoms(atoms) + self.chem_system.add_clusters([cluster]) self.coords = np.random.uniform(0, 1, (self._nAtoms, 3)) self.unit_cell = UnitCell(np.random.uniform(0, 1, (3, 3))) @@ -229,11 +231,12 @@ def setUp(self): self._nAtoms = 4 atoms = [] + cluster = [] for i in range(self._nAtoms): - atoms.append(Atom(symbol="H")) - ac = AtomCluster("", atoms) - - self.chem_system.add_chemical_entity(ac) + atoms.append("H") + cluster.append(i) + self.chem_system.initialise_atoms(atoms) + self.chem_system.add_clusters([cluster]) def test_fold_coordinates(self): coords = np.array( @@ -320,16 +323,6 @@ def test_to_real_configuration(self): ) self.assertEqual(unit_cell, real._unit_cell) - def test_atoms_in_shell(self): - unit_cell = UnitCell(np.array([[10, 0, 0], [0, 10, 0], [0, 0, 10]])) - coords = np.array( - ([0.1, 0.1, 0.1], [0.2, 0.1, 0.1], [0.5, 0.1, 0.1], [0.9, 0.1, 0.1]) - ) - conf = PeriodicBoxConfiguration(self.chem_system, coords, unit_cell) - atoms = conf.atoms_in_shell(0, 0.0, 0.3) - - self.assertEqual([1, 3], [at.index for at in atoms]) - def test_contiguous_configuration(self): unit_cell = UnitCell(np.array([[2, 1, 0], [-3, 2, 0], [2, 1, -4]])) coords = [[0.1, 0.1, 0.1], [0.3, 0.2, 0.4], [-1.3, -1.1, -1.3], [1.9, 1.5, 1.9]] @@ -343,76 +336,15 @@ def test_contiguous_configuration(self): np.allclose( [ [0.1, 0.1, 0.1], - [0.8, 1.1, -1.6], - [-0.9, -0.8, 1.2], - [-1.9, 0.8, 0.4], + [0.3, 0.2, 0.4], + [-0.3, -0.1, -0.3], + [-0.1, 0.5, -0.1], ], contiguous_conf["coordinates"], ), f'\nactual = {contiguous_conf["coordinates"]}', ) - def test_contiguous_offsets_valid_no_input(self): - self.chem_system.add_chemical_entity(AtomCluster("2", [Atom(), Atom()])) - - unit_cell = UnitCell(np.array([[2, 1, 0], [-3, 2, 0], [2, 1, -4]])) - coords = [ - [0.1, 0.1, 0.1], - [0.3, 0.2, 0.4], - [-1.3, -1.1, -1.3], - [1.9, 1.5, 1.9], - [1, 1.5, 1], - [0.1, 1.5, 1.1], - ] - conf = PeriodicBoxConfiguration(self.chem_system, coords, unit_cell) - offsets = conf.contiguous_offsets() - - self.assertTrue( - np.allclose( - [ - [0, 0, 0], - [0, 0, 0], - [1, 1, 1], - [-2, -1, -2], - [0, 0, 0], - [1, 0, 0], - ], - offsets, - ), - f"\nactual = {offsets}", - ) - - def test_contiguous_offsets_valid_specified_list(self): - self.chem_system.add_chemical_entity(AtomCluster("2", [Atom(), Atom()])) - - unit_cell = UnitCell(np.array([[2, 1, 0], [-3, 2, 0], [2, 1, -4]])) - coords = [ - [0.1, 0.1, 0.1], - [0.3, 0.2, 0.4], - [-1.3, -1.1, -1.3], - [1.9, 1.5, 1.9], - [1, 1.5, 1], - [0.1, 1.5, 1.1], - ] - conf = PeriodicBoxConfiguration(self.chem_system, coords, unit_cell) - offsets = conf.contiguous_offsets([self.chem_system.chemical_entities[0]]) - - self.assertTrue( - np.allclose( - [[0, 0, 0], [0, 0, 0], [1, 1, 1], [-2, -1, -2]], - offsets, - ), - f"\nactual = {offsets}", - ) - - def test_contiguous_offsets_invalid(self): - unit_cell = UnitCell(np.array([[2, 1, 0], [-3, 2, 0], [2, 1, -4]])) - coords = [[0.1, 0.1, 0.1], [0.3, 0.2, 0.4], [-1.3, -1.1, -1.3], [1.9, 1.5, 1.9]] - conf = PeriodicBoxConfiguration(self.chem_system, coords, unit_cell) - - with self.assertRaises(ConfigurationError): - conf.contiguous_offsets([Atom(parent=ChemicalSystem())]) - class TestPeriodicRealConfiguration(unittest.TestCase): def setUp(self): @@ -420,11 +352,12 @@ def setUp(self): self._nAtoms = 4 atoms = [] + cluster = [] for i in range(self._nAtoms): - atoms.append(Atom(symbol="H")) - ac = AtomCluster("", atoms) - - self.chem_system.add_chemical_entity(ac) + atoms.append("H") + cluster.append(i) + self.chem_system.initialise_atoms(atoms) + self.chem_system.add_clusters([cluster]) def test_fold_coordinates(self): coords = np.array( @@ -516,14 +449,6 @@ def test_to_real_coordinates(self): real = conf.to_real_coordinates() self.assertTrue(np.allclose(coords, real), f"\nactual = {real}") - def test_atoms_in_shell(self): - coords = np.array([[1, 1, 1], [2, 1, 1], [5, 1, 1], [9, 1, 1]]) - unit_cell = UnitCell(np.array([[10, 0, 0], [0, 10, 0], [0, 0, 10]])) - conf = PeriodicRealConfiguration(self.chem_system, coords, unit_cell) - atoms = conf.atoms_in_shell(0, 0, 3) - - self.assertEqual([1, 3], [at.index for at in atoms]) - def test_contiguous_configuration(self): coords = np.array( [ @@ -556,7 +481,30 @@ def test_contiguous_configuration(self): ) self.assertEqual(unit_cell, result._unit_cell) - def test_continuous_configuration(self): + def test_contiguous_configuration_does_not_move_atoms_to_edges(self): + unit_cell = UnitCell( + np.array( + [ + [4.15821342e00, 0.00000000e00, 0.00000000e00], + [2.54617138e-16, 4.15821342e00, 0.00000000e00], + [2.85252415e-16, 2.85252415e-16, 4.65852547e00], + ] + ) + ) + coords = np.array( + [ + [-1.8630077362060546, -0.4748522758483886, -0.5351669311523437], + [-1.9116626739501952, -0.4739928722381591, -0.6350426197052002], + [-1.7785566329956053, -0.5469871520996094, -0.5323768615722656], + [-1.8123033523559569, -0.3780877590179443, -0.5149454593658447], + ] + ) + conf = PeriodicRealConfiguration(self.chem_system, coords.copy(), unit_cell) + result = conf.contiguous_configuration() + new_coords = result.coordinates + assert np.allclose(coords, new_coords) + + def test_continuous_configuration_without_bonds(self): coords = np.array( [ [14.0, 3.0, 6.0], @@ -588,82 +536,38 @@ def test_continuous_configuration(self): ) self.assertEqual(unit_cell, result._unit_cell) - def test_contiguous_offsets_valid_no_input(self): - coords = np.array( - [ - [14.0, 3.0, 6.0], - [32.0, 9.0, 15.0], - [50.0, 15.0, 24.0], - [68.0, 21.0, 33.0], - ] - ) - unit_cell = UnitCell( - np.array([[1.0, 2.0, 1.0], [2.0, -1.0, 1.0], [3.0, 1.0, 1.0]]) - ) - conf = PeriodicRealConfiguration(self.chem_system, coords, unit_cell) - - offsets = conf.contiguous_offsets() - self.assertTrue( - np.allclose( - [ - [0.0, 0.0, 0.0], - [-3.0, -3.0, -3.0], - [-6.0, -6.0, -6.0], - [-9.0, -9.0, -9.0], - ], - offsets, - ), - f"\nactual = {offsets}", - ) - - def test_contiguous_offsets_valid_specified_list(self): - self.chem_system.add_chemical_entity(AtomCluster("2", [Atom(), Atom()])) - + def test_continuous_configuration_with_bonds(self): coords = np.array( [ [14.0, 3.0, 6.0], [32.0, 9.0, 15.0], [50.0, 15.0, 24.0], [68.0, 21.0, 33.0], - [15.0, 15.0, 15.0], - [10.0, 10.0, 10.0], ] ) unit_cell = UnitCell( np.array([[1.0, 2.0, 1.0], [2.0, -1.0, 1.0], [3.0, 1.0, 1.0]]) ) + self.chem_system.add_bonds([(0, 1), (2, 3)]) conf = PeriodicRealConfiguration(self.chem_system, coords, unit_cell) - offsets = conf.contiguous_offsets([self.chem_system.chemical_entities[0]]) + result = conf.continuous_configuration() + self.assertTrue(isinstance(result, PeriodicRealConfiguration)) + self.assertEqual(repr(self.chem_system), repr(result._chemical_system)) + self.assertEqual(["coordinates"], list(result._variables.keys())) self.assertTrue( np.allclose( [ - [0.0, 0.0, 0.0], - [-3.0, -3.0, -3.0], - [-6.0, -6.0, -6.0], - [-9.0, -9.0, -9.0], + [14.0, 3.0, 6.0], + [14.0, 3.0, 6.0], + [50.0, 15.0, 24.0], + [50.0, 15.0, 24.0], ], - offsets, + result["coordinates"], ), - f"\nactual = {offsets}", - ) - - def test_contiguous_offsets_invalid(self): - coords = np.array( - [ - [14.0, 3.0, 6.0], - [32.0, 9.0, 15.0], - [50.0, 15.0, 24.0], - [68.0, 21.0, 33.0], - ] - ) - unit_cell = UnitCell( - np.array([[1.0, 2.0, 1.0], [2.0, -1.0, 1.0], [3.0, 1.0, 1.0]]) + f'\nactual = {result["coordinates"]}', ) - conf = PeriodicRealConfiguration(self.chem_system, coords, unit_cell) - - with self.assertRaises(ConfigurationError): - conf.contiguous_offsets([Atom(parent=ChemicalSystem())]) + self.assertEqual(unit_cell, result._unit_cell) class TestRealConfiguration(unittest.TestCase): @@ -672,11 +576,12 @@ def setUp(self): self._nAtoms = 4 atoms = [] + cluster = [] for i in range(self._nAtoms): - atoms.append(Atom(symbol="H")) - ac = AtomCluster("", atoms) - - self.chem_system.add_chemical_entity(ac) + atoms.append("H") + cluster.append(i) + self.chem_system.initialise_atoms(atoms) + self.chem_system.add_clusters([cluster]) def test_clone_valid_no_input(self): coordinates = np.random.uniform(0, 1, (self._nAtoms, 3)) @@ -727,13 +632,6 @@ def test_to_real_coordinates(self): self.assertTrue(np.allclose(coordinates, real), f"\nactual = {real}") - def test_atoms_in_shell(self): - coords = np.array([[1, 1, 1], [2, 1, 1], [5, 1, 1], [9, 1, 1]]) - conf = RealConfiguration(self.chem_system, coords) - atoms = conf.atoms_in_shell(0, 0, 5) - - self.assertEqual([1, 2], [at.index for at in atoms]) - def test_contiguous_configuration(self): coords = np.random.uniform(0, 1, (self._nAtoms, 3)) conf = RealConfiguration(self.chem_system, coords) @@ -745,26 +643,3 @@ def test_continuous_configuration(self): conf = RealConfiguration(self.chem_system, coords) self.assertEqual(conf, conf.continuous_configuration()) - - def test_contiguous_offsets_valid_none(self): - self.chem_system.add_chemical_entity(AtomCluster("", [Atom(), Atom()])) - coords = np.random.uniform(0, 1, (self._nAtoms + 2, 3)) - conf = RealConfiguration(self.chem_system, coords) - - offsets = conf.contiguous_offsets() - self.assertTrue(np.allclose(np.zeros((6, 3)), offsets), f"\nactual = {offsets}") - - def test_contiguous_offsets_valid_system_input(self): - self.chem_system.add_chemical_entity(AtomCluster("", [Atom(), Atom()])) - coords = np.random.uniform(0, 1, (self._nAtoms + 2, 3)) - conf = RealConfiguration(self.chem_system, coords) - - offsets = conf.contiguous_offsets(self.chem_system.chemical_entities[0]) - self.assertTrue(np.allclose(np.zeros((4, 3)), offsets), f"\nactual = {offsets}") - - def test_contiguous_offsets_invalid_system(self): - coords = np.random.uniform(0, 1, (self._nAtoms, 3)) - conf = RealConfiguration(self.chem_system, coords) - - with self.assertRaises(ConfigurationError): - conf.contiguous_offsets([Atom(parent=ChemicalSystem())]) diff --git a/MDANSE/Tests/UnitTests/test_converter.py b/MDANSE/Tests/UnitTests/test_converter.py index 7baf3f27b6..06fc085b26 100644 --- a/MDANSE/Tests/UnitTests/test_converter.py +++ b/MDANSE/Tests/UnitTests/test_converter.py @@ -57,7 +57,10 @@ def test_lammps_mdt_conversion_file_exists_and_loads_up_successfully(compression lammps = Converter.create("LAMMPS") lammps.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -86,7 +89,10 @@ def test_lammps_mdt_conversion_unit_system(unit_system): lammps = Converter.create("LAMMPS") lammps.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -117,7 +123,10 @@ def test_lammps_mdt_conversion_trajectory_format(trajectory_file, trajectory_for lammps = Converter.create("LAMMPS") lammps.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -157,7 +166,10 @@ def test_vasp_mdt_conversion_file_exists_and_loads_up_successfully(compression): vasp = Converter.create("VASP") vasp.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -181,7 +193,10 @@ def test_discover_mdt_conversion_file_exists_and_loads_up_successfully(compressi vasp = Converter.create("discover") vasp.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -205,7 +220,10 @@ def test_cp2k_mdt_conversion_file_exists_and_loads_up_successfully(velocity): vasp = Converter.create("cp2k") vasp.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -230,7 +248,10 @@ def test_charmm_mdt_conversion_file_exists_and_loads_up_successfully(compression vasp = Converter.create("charmm") vasp.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -256,7 +277,10 @@ def test_ase_mdt_conversion_file_exists_and_loads_up_successfully(compression): ase_conv = Converter.create("ase") ase_conv.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -282,7 +306,10 @@ def test_improvedase_mdt_conversion_file_exists_and_loads_up_successfully(trajec ase_conv = Converter.create("improvedase") ase_conv.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -309,7 +336,10 @@ def test_improvedase_lammps_two_files(): ase_conv = Converter.create("improvedase") ase_conv.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -335,7 +365,10 @@ def test_xyz_mdt_conversion_file_exists_and_loads_up_successfully(compression): ase_conv = Converter.create("ase") ase_conv.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -361,7 +394,10 @@ def test_dlp_mdt_conversion_file_exists_and_loads_up_successfully_with_dlp_versi dl_poly = Converter.create("DL_POLY") dl_poly.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -387,7 +423,10 @@ def test_dlp_mdt_conversion_file_exists_and_loads_up_successfully_with_dlp_versi dl_poly = Converter.create("DL_POLY") dl_poly.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -468,7 +507,10 @@ def test_castep_md_conversion_file_exists_and_loads_up_successfully(compression) castep = Converter.create("CASTEP") castep.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -493,7 +535,10 @@ def test_dftb_conversion_file_exists_and_loads_up_successfully(compression): dftb = Converter.create("DFTB") dftb.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -518,7 +563,10 @@ def test_forcite_conversion_file_exists_and_loads_up_successfully(compression): forcite = Converter.create("Forcite") forcite.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -542,7 +590,10 @@ def test_gromacs_conversion_file_exists_and_loads_up_successfully(compression): gromacs = Converter.create("Gromacs") gromacs.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") @@ -565,7 +616,10 @@ def test_mdanalysis_conversion_file_exists_and_loads_up_successfully(compression mdanalysis = Converter.create("MDAnalysis") mdanalysis.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() # remove offset files generated by mdanalysis os.remove(os.path.join(file_wd, "Data", ".md.xtc_offsets.lock")) @@ -592,7 +646,10 @@ def test_mdtraj_conversion_file_exists_and_loads_up_successfully(compression): mdanalysis = Converter.create("MDTraj") mdanalysis.run(parameters, status=True) - HDFTrajectoryConfigurator("trajectory").configure(temp_name + ".mdt") + traj_conf = HDFTrajectoryConfigurator("trajectory") + traj_conf.configure(temp_name + ".mdt") + traj_conf.get_information() + traj_conf["hdf_trajectory"].close() assert os.path.exists(temp_name + ".mdt") assert os.path.isfile(temp_name + ".mdt") diff --git a/MDANSE/Tests/UnitTests/test_databases.py b/MDANSE/Tests/UnitTests/test_databases.py index f7c52de3d1..c508e255a6 100644 --- a/MDANSE/Tests/UnitTests/test_databases.py +++ b/MDANSE/Tests/UnitTests/test_databases.py @@ -20,16 +20,10 @@ from MDANSE.Chemistry import ( ATOMS_DATABASE, - MOLECULES_DATABASE, - RESIDUES_DATABASE, - NUCLEOTIDES_DATABASE, ) import MDANSE.Chemistry.Databases as Databases from MDANSE.Chemistry.Databases import ( AtomsDatabaseError, - MoleculesDatabaseError, - ResiduesDatabaseError, - NucleotidesDatabaseError, ) @@ -335,360 +329,3 @@ def test_save(self): dump.assert_called_with( {"properties": self.properties, "atoms": self.data}, ANY ) - - -class TestMoleculesDatabase(unittest.TestCase): - def setUp(self): - self.data = { - "WAT": { - "alternatives": ["H2O", "water", "TIP3"], - "atoms": { - "OW": { - "symbol": "O", - "alternatives": ["O", "OH2"], - "groups": [], - "bonds": ["HW1", "HW2"], - }, - "HW2": { - "symbol": "H", - "alternatives": ["H2"], - "groups": [], - "bonds": ["OW"], - }, - }, - } - } - self.overwrite_database() - - @classmethod - def tearDownClass(cls): - MOLECULES_DATABASE._load() - - def overwrite_database(self): - MOLECULES_DATABASE._data = self.data - - def test__load_default_database(self): - with patch( - "builtins.open", new_callable=mock_open, read_data=json.dumps(self.data) - ) as m: - MOLECULES_DATABASE._load("INVALID.json", "molecules.json") - m.assert_called_with("molecules.json", "r") - self.assertDictEqual(self.data, MOLECULES_DATABASE._data) - - def test__load_user_database(self): - with ( - patch( - "builtins.open", new_callable=mock_open, read_data=json.dumps(self.data) - ) as m, - patch("os.path.exists", spec=True), - ): - MOLECULES_DATABASE._load("user.json", "default.json") - m.assert_called_with("user.json", "r") - self.assertDictEqual(self.data, MOLECULES_DATABASE._data) - - def test___contains__(self): - self.assertFalse("fhsdjfsd" in MOLECULES_DATABASE) - self.assertTrue("WAT" in MOLECULES_DATABASE) - self.assertTrue("H2O" in MOLECULES_DATABASE) - - def test___getitem__(self): - self.assertDictEqual(self.data["WAT"], MOLECULES_DATABASE["WAT"]) - with self.assertRaises(MoleculesDatabaseError): - a = MOLECULES_DATABASE["INVALID"] - # TODO: Look into the not implemented description of __getitem__ - - def test___iter__(self): - generator = iter(MOLECULES_DATABASE) - self.assertDictEqual(self.data["WAT"], next(generator)) - - def test_add_molecule_existing_molecule(self): - with self.assertRaises(MoleculesDatabaseError): - MOLECULES_DATABASE.add_molecule("WAT") - - def test_add_molecule_valid(self): - with ( - patch("json.dump") as m, - patch("MDANSE.Chemistry.Databases.MoleculesDatabase.save") as n, - ): - MOLECULES_DATABASE.add_molecule("new_molecule") - self.assertDictEqual( - {"alternatives": [], "atoms": {}}, MOLECULES_DATABASE["new_molecule"] - ) - assert not m.called - assert not n.called - - def test_items(self): - for (expected_atom, expected_data), (atom, data) in zip( - self.data.items(), MOLECULES_DATABASE.items() - ): - self.assertEqual(expected_atom, atom) - self.assertDictEqual(expected_data, data) - - def test_molecules(self): - self.assertEqual(["WAT"], MOLECULES_DATABASE.molecules) - - def test_n_molecules(self): - self.assertEqual(1, MOLECULES_DATABASE.n_molecules) - - def test__reset(self): - MOLECULES_DATABASE._reset() - self.assertDictEqual({}, MOLECULES_DATABASE._data) - - def test_save(self): - with ( - patch("builtins.open", new_callable=mock_open) as op, - patch("json.dump") as dump, - ): - MOLECULES_DATABASE.save() - op.assert_called_with(MOLECULES_DATABASE._USER_DATABASE, "w") - dump.assert_called_with(self.data, ANY) - - -class TestNucleotidesDatabase(unittest.TestCase): - def setUp(self): - self.data = { - "5T1": { - "is_3ter_terminus": False, - "atoms": { - "HO5'": { - "replaces": ["OP1", "OP2", "P"], - "o5prime_connected": True, - "groups": [], - "bonds": ["O5'"], - "symbol": "H", - "alternatives": [], - } - }, - "alternatives": ["5-terminus", "5T"], - "is_5ter_terminus": True, - } - } - NUCLEOTIDES_DATABASE._data = self.data - NUCLEOTIDES_DATABASE._residue_map = { - "5T1": "5T1", - "5-terminus": "5T1", - "5T": "5T1", - } - - @classmethod - def tearDownClass(cls): - NUCLEOTIDES_DATABASE._load() - - def test__load_default_database(self): - with patch( - "builtins.open", new_callable=mock_open, read_data=json.dumps(self.data) - ) as m: - NUCLEOTIDES_DATABASE._load("INVALID.json", "molecules.json") - m.assert_called_with("molecules.json", "r") - self.assertDictEqual(self.data, NUCLEOTIDES_DATABASE._data) - self.assertDictEqual( - {"5T1": "5T1", "5-terminus": "5T1", "5T": "5T1"}, - NUCLEOTIDES_DATABASE._residue_map, - ) - - def test__load_user_database(self): - with ( - patch( - "builtins.open", new_callable=mock_open, read_data=json.dumps(self.data) - ) as m, - patch("os.path.exists", spec=True), - ): - NUCLEOTIDES_DATABASE._load("user.json", "default.json") - m.assert_called_with("user.json", "r") - self.assertDictEqual(self.data, NUCLEOTIDES_DATABASE._data) - self.assertDictEqual( - {"5T1": "5T1", "5-terminus": "5T1", "5T": "5T1"}, - NUCLEOTIDES_DATABASE._residue_map, - ) - - def test___contains__(self): - self.assertFalse("fhsdjfsd" in NUCLEOTIDES_DATABASE) - self.assertTrue("5T1" in NUCLEOTIDES_DATABASE) - self.assertTrue("5T" in NUCLEOTIDES_DATABASE) - - def test___getitem__(self): - self.assertDictEqual(self.data["5T1"], NUCLEOTIDES_DATABASE["5T1"]) - self.assertDictEqual(self.data["5T1"], NUCLEOTIDES_DATABASE["5T"]) - with self.assertRaises(NucleotidesDatabaseError): - a = NUCLEOTIDES_DATABASE["INVALID"] - # TODO: Look into the not implemented description of __getitem__ - - def test___iter__(self): - generator = iter(NUCLEOTIDES_DATABASE) - self.assertDictEqual(self.data["5T1"], next(generator)) - - def test_add_nucleotide_existing_nucleotide(self): - with self.assertRaises(NucleotidesDatabaseError): - NUCLEOTIDES_DATABASE.add_nucleotide("5T") - - def test_add_nucleotide_valid(self): - with ( - patch("json.dump") as m, - patch("MDANSE.Chemistry.Databases.NucleotidesDatabase.save") as n, - ): - NUCLEOTIDES_DATABASE.add_nucleotide("new_nucleotide", True, True) - self.assertDictEqual( - { - "alternatives": [], - "atoms": {}, - "is_5ter_terminus": True, - "is_3ter_terminus": True, - }, - NUCLEOTIDES_DATABASE["new_nucleotide"], - ) - assert not m.called - assert not n.called - - def test_items(self): - for (expected_atom, expected_data), (atom, data) in zip( - self.data.items(), NUCLEOTIDES_DATABASE.items() - ): - self.assertEqual(expected_atom, atom) - self.assertDictEqual(expected_data, data) - - def test_nucleotides(self): - self.assertEqual(["5T1"], NUCLEOTIDES_DATABASE.nucleotides) - - def test_n_nucleotides(self): - self.assertEqual(1, NUCLEOTIDES_DATABASE.n_nucleotides) - - def test__reset(self): - NUCLEOTIDES_DATABASE._reset() - self.assertDictEqual({}, NUCLEOTIDES_DATABASE._data) - - def test_save(self): - with ( - patch("builtins.open", new_callable=mock_open) as op, - patch("json.dump") as dump, - ): - NUCLEOTIDES_DATABASE.save() - op.assert_called_with(NUCLEOTIDES_DATABASE._USER_DATABASE, "w") - dump.assert_called_with(self.data, ANY) - - -class TestResiduesDatabase(unittest.TestCase): - def setUp(self): - self.data = { - "GLY": { - "is_n_terminus": False, - "atoms": { - "C": { - "symbol": "C", - "alternatives": [], - "groups": ["backbone", "peptide"], - "bonds": ["CA", "O", "+R"], - }, - "H": { - "symbol": "H", - "alternatives": ["HN"], - "groups": ["backbone", "peptide"], - "bonds": ["N"], - }, - "CA": { - "symbol": "C", - "alternatives": [], - "groups": ["backbone"], - "bonds": ["C", "HA2", "HA3", "N"], - }, - }, - "alternatives": ["glycine", "G"], - "is_c_terminus": False, - } - } - RESIDUES_DATABASE._data = self.data - RESIDUES_DATABASE._residue_map = {"GLY": "GLY", "glycine": "GLY", "G": "GLY"} - - @classmethod - def tearDownClass(cls): - RESIDUES_DATABASE._load() - - def test__load_default_database(self): - with patch( - "builtins.open", new_callable=mock_open, read_data=json.dumps(self.data) - ) as m: - RESIDUES_DATABASE._load("INVALID.json", "residues.json") - m.assert_called_with("residues.json", "r") - self.assertDictEqual(self.data, RESIDUES_DATABASE._data) - self.assertDictEqual( - {"GLY": "GLY", "glycine": "GLY", "G": "GLY"}, - RESIDUES_DATABASE._residue_map, - ) - - def test__load_user_database(self): - with ( - patch( - "builtins.open", new_callable=mock_open, read_data=json.dumps(self.data) - ) as m, - patch("os.path.exists", spec=True), - ): - RESIDUES_DATABASE._load("user.json", "default.json") - m.assert_called_with("user.json", "r") - self.assertDictEqual(self.data, RESIDUES_DATABASE._data) - self.assertDictEqual( - {"GLY": "GLY", "glycine": "GLY", "G": "GLY"}, - RESIDUES_DATABASE._residue_map, - ) - - def test___contains__(self): - self.assertFalse("fhsdjfsd" in RESIDUES_DATABASE) - self.assertTrue("GLY" in RESIDUES_DATABASE) - self.assertTrue("G" in RESIDUES_DATABASE) - - def test___getitem__(self): - self.assertDictEqual(self.data["GLY"], RESIDUES_DATABASE["GLY"]) - self.assertDictEqual(self.data["GLY"], RESIDUES_DATABASE["G"]) - with self.assertRaises(ResiduesDatabaseError): - a = RESIDUES_DATABASE["INVALID"] - # TODO: Look into the not implemented description of __getitem__ - - def test___iter__(self): - generator = iter(RESIDUES_DATABASE) - self.assertDictEqual(self.data["GLY"], next(generator)) - - def test_add_residue_existing_nucleotide(self): - with self.assertRaises(ResiduesDatabaseError): - RESIDUES_DATABASE.add_residue("G") - - def test_add_residue_valid(self): - with ( - patch("json.dump") as m, - patch("MDANSE.Chemistry.Databases.ResiduesDatabase.save") as n, - ): - RESIDUES_DATABASE.add_residue("new_residues", True, True) - self.assertDictEqual( - { - "alternatives": [], - "atoms": {}, - "is_c_terminus": True, - "is_n_terminus": True, - }, - RESIDUES_DATABASE["new_residues"], - ) - assert not m.called - assert not n.called - - def test_items(self): - for (expected_atom, expected_data), (atom, data) in zip( - self.data.items(), RESIDUES_DATABASE.items() - ): - self.assertEqual(expected_atom, atom) - self.assertDictEqual(expected_data, data) - - def test_residues(self): - self.assertEqual(["GLY"], RESIDUES_DATABASE.residues) - - def test_n_residues(self): - self.assertEqual(1, RESIDUES_DATABASE.n_residues) - - def test__reset(self): - RESIDUES_DATABASE._reset() - self.assertDictEqual({}, RESIDUES_DATABASE._data) - - def test_save(self): - with ( - patch("builtins.open", new_callable=mock_open) as op, - patch("json.dump") as dump, - ): - RESIDUES_DATABASE.save() - op.assert_called_with(RESIDUES_DATABASE._USER_DATABASE, "w") - dump.assert_called_with(self.data, ANY) diff --git a/MDANSE/Tests/UnitTests/test_ijob.py b/MDANSE/Tests/UnitTests/test_ijob.py index 598ecfaf3a..d7fb081a99 100644 --- a/MDANSE/Tests/UnitTests/test_ijob.py +++ b/MDANSE/Tests/UnitTests/test_ijob.py @@ -27,7 +27,6 @@ "McStasVirtualInstrument", "MeanSquareDisplacement", "MolecularTrace", - "MoleculeFinder", "NeutronDynamicTotalStructureFactor", "OrderParameter", "PositionAutoCorrelationFunction", diff --git a/MDANSE/Tests/UnitTests/test_mock_trajectory.py b/MDANSE/Tests/UnitTests/test_mock_trajectory.py index b6263d3353..82e8e2aa5c 100644 --- a/MDANSE/Tests/UnitTests/test_mock_trajectory.py +++ b/MDANSE/Tests/UnitTests/test_mock_trajectory.py @@ -14,7 +14,6 @@ # along with this program. If not, see . # import os -import tempfile import pytest import numpy as np @@ -72,7 +71,7 @@ def test_com_trajectory(static_trajectory): """Centre of Mass (COM) trajectory should be identical to the static trajectory, since the atoms never moved""" com_trajectory = static_trajectory.read_com_trajectory( - static_trajectory._chemicalSystem.atoms, 0, 10, 1 + list(range(static_trajectory._num_atoms_in_box)), 0, 10, 1 ) conf_static = static_trajectory[3] conf_com = com_trajectory[3] diff --git a/MDANSE/Tests/UnitTests/test_pdb_reader.py b/MDANSE/Tests/UnitTests/test_pdb_reader.py index 14756c6cd8..6ba3bd1659 100644 --- a/MDANSE/Tests/UnitTests/test_pdb_reader.py +++ b/MDANSE/Tests/UnitTests/test_pdb_reader.py @@ -15,8 +15,7 @@ # import os import unittest -import numpy -from MDANSE.IO.PDBReader import PDBReader +from MDANSE.IO.MinimalPDBReader import MinimalPDBReader as PDBReader pbd_2vb1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), "Data", "2vb1.pdb") @@ -33,21 +32,13 @@ def test_reader(self): reader = PDBReader(pbd_2vb1) - chemicalSystem = reader.build_chemical_system() + chemical_system = reader._chemical_system - atomList = chemicalSystem.atom_list + atomList = chemical_system.atom_list - self.assertEqual(atomList[4].symbol, "C") - self.assertEqual(atomList[7].name, "HB2") - self.assertEqual(atomList[10].full_name, "...LYS1.HG2") - self.assertEqual(atomList[28].parent.name, "VAL2") - - conf = chemicalSystem.configuration - - self.assertAlmostEqual(conf.variables["coordinates"][0, 0], 4.6382) - self.assertAlmostEqual(conf.variables["coordinates"][0, 1], 3.0423) - self.assertAlmostEqual(conf.variables["coordinates"][0, 2], 2.6918) - - self.assertAlmostEqual(conf.variables["coordinates"][-1, 0], 2.4937) - self.assertAlmostEqual(conf.variables["coordinates"][-1, 1], 3.9669) - self.assertAlmostEqual(conf.variables["coordinates"][-1, 2], -0.5209) + self.assertEqual(atomList[4], "C") + print(chemical_system._labels.keys()) + self.assertTrue(10 in chemical_system._labels["LYS"]) + self.assertEqual(chemical_system.name_list[7], "HB2") + # self.assertEqual(atomList[10].full_name, "...LYS1.HG2") + # self.assertEqual(atomList[28].parent.name, "VAL2") diff --git a/MDANSE/Tests/UnitTests/test_trajectory.py b/MDANSE/Tests/UnitTests/test_trajectory.py index 5272969968..b5025e56e9 100644 --- a/MDANSE/Tests/UnitTests/test_trajectory.py +++ b/MDANSE/Tests/UnitTests/test_trajectory.py @@ -16,7 +16,7 @@ import tempfile import unittest import numpy as np -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Configuration import PeriodicRealConfiguration from MDANSE.MolecularDynamics.Trajectory import Trajectory, TrajectoryWriter from MDANSE.MolecularDynamics.UnitCell import UnitCell @@ -26,15 +26,13 @@ class TestTrajectory(unittest.TestCase): """ """ def setUp(self): - self._chemicalSystem = ChemicalSystem() + self._chemical_system = ChemicalSystem() self._nAtoms = 4 - - for i in range(self._nAtoms): - self._chemicalSystem.add_chemical_entity(Atom(symbol="H")) + self._chemical_system.initialise_atoms(self._nAtoms * ["H"]) def test_write_trajectory(self): tf = tempfile.NamedTemporaryFile().name - tw = TrajectoryWriter(tf, self._chemicalSystem, 10) + tw = TrajectoryWriter(tf, self._chemical_system, 10) allCoordinates = [] allUnitCells = [] @@ -44,10 +42,9 @@ def test_write_trajectory(self): allUnitCells.append(np.random.uniform(0, 10, (3, 3))) allCoordinates.append(np.random.uniform(0, 10, (self._nAtoms, 3))) conf = PeriodicRealConfiguration( - self._chemicalSystem, allCoordinates[-1], UnitCell(allUnitCells[-1]) + self._chemical_system, allCoordinates[-1], UnitCell(allUnitCells[-1]) ) - tw.chemical_system.configuration = conf - tw.dump_configuration(i) + tw.dump_configuration(conf, i) tw.close() @@ -66,7 +63,7 @@ def test_write_trajectory(self): def test_write_trajectory_with_velocities(self): tf = tempfile.NamedTemporaryFile().name - tw = TrajectoryWriter(tf, self._chemicalSystem, 10) + tw = TrajectoryWriter(tf, self._chemical_system, 10) allCoordinates = [] allUnitCells = [] @@ -78,11 +75,10 @@ def test_write_trajectory_with_velocities(self): allCoordinates.append(np.random.uniform(0, 10, (self._nAtoms, 3))) allVelocities.append(np.random.uniform(0, 10, (self._nAtoms, 3))) conf = PeriodicRealConfiguration( - self._chemicalSystem, allCoordinates[-1], UnitCell(allUnitCells[-1]) + self._chemical_system, allCoordinates[-1], UnitCell(allUnitCells[-1]) ) conf.variables["velocities"] = allVelocities[-1] - self._chemicalSystem.configuration = conf - tw.dump_configuration(i) + tw.dump_configuration(conf, i) tw.close() @@ -104,7 +100,7 @@ def test_write_trajectory_with_velocities(self): def test_write_trajectory_with_gradients(self): tf = tempfile.NamedTemporaryFile().name - tw = TrajectoryWriter(tf, self._chemicalSystem, 10) + tw = TrajectoryWriter(tf, self._chemical_system, 10) allCoordinates = [] allUnitCells = [] @@ -118,12 +114,11 @@ def test_write_trajectory_with_gradients(self): allVelocities.append(np.random.uniform(0, 10, (self._nAtoms, 3))) allGradients.append(np.random.uniform(0, 10, (self._nAtoms, 3))) conf = PeriodicRealConfiguration( - self._chemicalSystem, allCoordinates[-1], UnitCell(allUnitCells[-1]) + self._chemical_system, allCoordinates[-1], UnitCell(allUnitCells[-1]) ) conf.variables["velocities"] = allVelocities[-1] conf.variables["gradients"] = allGradients[-1] - self._chemicalSystem.configuration = conf - tw.dump_configuration(i) + tw.dump_configuration(conf, i) tw.close() @@ -148,7 +143,7 @@ def test_write_trajectory_with_gradients(self): def test_read_com_trajectory(self): tf = tempfile.NamedTemporaryFile().name - tw = TrajectoryWriter(tf, self._chemicalSystem, 1) + tw = TrajectoryWriter(tf, self._chemical_system, 1) allCoordinates = [] allUnitCells = [] @@ -160,14 +155,16 @@ def test_read_com_trajectory(self): [[0.0, 0.0, 0.0], [8.0, 8.0, 8.0], [4.0, 4.0, 4.0], [2.0, 2.0, 2.0]] ) conf = PeriodicRealConfiguration( - self._chemicalSystem, allCoordinates[-1], UnitCell(allUnitCells[-1]) + self._chemical_system, allCoordinates[-1], UnitCell(allUnitCells[-1]) ) - self._chemicalSystem.configuration = conf - tw.dump_configuration(i) + tw.dump_configuration(conf, i) tw.close() t = Trajectory(tf) - com_trajectory = t.read_com_trajectory(self._chemicalSystem.atoms, 0, 1, 1) + com_trajectory = t.read_com_trajectory( + list(range(self._chemical_system.total_number_of_atoms)), 0, 1, 1 + ) + print(com_trajectory) self.assertTrue(np.allclose(com_trajectory, [[3.5, 3.5, 3.5]], rtol=1.0e-6)) t.close() diff --git a/MDANSE/Tests/UnitTests/test_trajectory_utils.py b/MDANSE/Tests/UnitTests/test_trajectory_utils.py deleted file mode 100644 index ca24bcc0b8..0000000000 --- a/MDANSE/Tests/UnitTests/test_trajectory_utils.py +++ /dev/null @@ -1,199 +0,0 @@ -import unittest - -from MDANSE.Chemistry.ChemicalEntity import Molecule, Protein, ChemicalSystem -from MDANSE.MolecularDynamics.Configuration import RealConfiguration -from MDANSE.MolecularDynamics.TrajectoryUtils import * - - -class TestTrajectoryUtils(unittest.TestCase): - def test_atom_index_to_molecule_index(self): - m1 = Molecule("WAT", "w1") - m2 = Molecule("WAT", "w2") - cs = ChemicalSystem() - cs.add_chemical_entity(m1) - cs.add_chemical_entity(m2) - - result = atom_index_to_molecule_index(cs) - self.assertDictEqual({0: 0, 1: 0, 2: 0, 3: 1, 4: 1, 5: 1}, result) - - def test_brute_formula(self): - m = Molecule("WAT", "w1") - self.assertEqual("H2_O1", brute_formula(m)) - self.assertEqual("H2O1", brute_formula(m, "")) - self.assertEqual("H2O", brute_formula(m, "", True)) - - def test_build_connectivity(self): - cs = ChemicalSystem() - - m = Molecule("WAT", "w") - ac = AtomCluster("ac", [Atom(), Atom(), Atom(), Atom()]) - a = Atom() - ag = AtomGroup([Atom(parent=cs)]) - - for ce in [m, ac, a, ag]: - cs.add_chemical_entity(ce) - - coords = np.array( - [ - [1, 1.05, 1], - [1, 0.98, 1], - [1, 1.12, 1], - [1, 1, 1], - [1.044, 1, 1], - [0.95, 1, 1], - [1.055, 1, 1], - [1, 1, 1.02], - [1, 1, 1.06], - ] - ) - conf = RealConfiguration(cs, coords) - cs.configuration = conf - - build_connectivity(cs, 0.005) - - self.assertEqual([m["OW"]], m["HW1"].bonds) - self.assertEqual([m["HW1"], m["HW2"]], m["OW"].bonds) - self.assertEqual([m["OW"]], m["HW2"].bonds) - - self.assertEqual([ac[1].index, ac[2].index], [at.index for at in ac[0].bonds]) - self.assertEqual([ac[0].index, ac[3].index], [at.index for at in ac[1].bonds]) - self.assertEqual([ac[0].index], [at.index for at in ac[2].bonds]) - self.assertEqual([ac[1].index], [at.index for at in ac[3].bonds]) - - self.assertEqual([ag._atoms[0].index], [at.index for at in a.bonds]) - self.assertEqual([a.index], [at.index for at in ag._atoms[0].bonds]) - - def test_find_atoms_in_molecule(self): - m1 = Molecule("WAT", "water") - m2 = Molecule("WAT", "water") - m3 = Molecule("WAT", "water") - p = Protein("protein") - p.set_peptide_chains([m3]) - - cs = ChemicalSystem() - for ce in [m1, m2, p]: - cs.add_chemical_entity(ce) - - water_hydrogens = find_atoms_in_molecule(cs, "water", ["HW2", "HW1"]) - protein_oxygens = find_atoms_in_molecule(cs, "protein", ["OW"]) - empty = find_atoms_in_molecule(cs, "INVALID", ["OW"]) - empty2 = find_atoms_in_molecule(cs, "water", ["INVALID"]) - water_hydrogen_indices = find_atoms_in_molecule( - cs, "water", ["HW2", "HW1"], True - ) - - self.assertEqual( - [[m1["HW2"], m1["HW1"]], [m2["HW2"], m2["HW1"]]], water_hydrogens - ) - self.assertEqual([[m3["OW"]]], protein_oxygens) - self.assertEqual([], empty) - self.assertEqual([[], []], empty2) - self.assertEqual([[1, 2], [4, 5]], water_hydrogen_indices) - - def test_get_chemical_objects_dict(self): - m1 = Molecule("WAT", "water") - m2 = Molecule("WAT", "water") - m4 = Molecule("WAT", "dihydrogen oxide") - - m3 = Molecule("WAT", "water") - p = Protein("protein") - p.set_peptide_chains([m3]) - - cs = ChemicalSystem() - for ce in [m1, m2, m4, p]: - cs.add_chemical_entity(ce) - - self.assertDictEqual( - {"water": [m1, m2], "dihydrogen oxide": [m4], "protein": [p]}, - get_chemical_objects_dict(cs), - ) - - def test_group_atoms(self): - atoms = [Atom() for _ in range(10)] - molecules = [Molecule("WAT", "") for _ in range(5)] - atoms.extend(molecules) - - cs = ChemicalSystem() - for ce in atoms: - cs.add_chemical_entity(ce) - - groups = group_atoms(cs, [[0, 1, 2], [], [1, 5], [10], [11, 12, 13, 11, 20]]) - - self.assertEqual(4, len(groups)) - self.assertEqual([atoms[0], atoms[1], atoms[2]], groups[0]._atoms) - self.assertEqual([atoms[1], atoms[5]], groups[1]._atoms) - self.assertEqual([molecules[0]["OW"]], groups[2]._atoms) - self.assertEqual( - [ - molecules[0]["HW2"], - molecules[0]["HW1"], - molecules[1]["OW"], - molecules[0]["HW2"], - molecules[3]["HW2"], - ], - groups[3]._atoms, - ) - - def test_resolve_undefined_molecules_name(self): - m1 = Molecule("WAT", "") - m2 = Molecule("WAT", " water ") - m4 = Molecule("WAT", " ") - - m3 = Molecule("WAT", "") - p = Protein("protein") - p.set_peptide_chains([m3]) - - cs = ChemicalSystem() - for ce in [m1, m2, m4, p]: - cs.add_chemical_entity(ce) - - resolve_undefined_molecules_name(cs) - - self.assertEqual("H2O1", m1.name) - self.assertEqual(" water ", m2.name) - self.assertEqual("H2O1", m4.name) - self.assertEqual("", m3.name) - self.assertEqual("protein", p.name) - - def test_sorted_atoms_normal(self): - atoms = [Atom() for _ in range(10)] - for i, atom in enumerate(atoms): - atom.index = i - - result = sorted_atoms( - [ - atoms[1], - atoms[2], - atoms[5], - atoms[9], - atoms[0], - atoms[3], - atoms[8], - atoms[4], - atoms[7], - atoms[6], - ] - ) - self.assertEqual(atoms, result) - - def test_sorted_atoms_return_names(self): - atoms = [Atom() for _ in range(10)] - for i, atom in enumerate(atoms): - atom.index = i - - result = sorted_atoms( - [ - atoms[1], - atoms[2], - atoms[5], - atoms[9], - atoms[0], - atoms[3], - atoms[8], - atoms[4], - atoms[7], - atoms[6], - ], - "name", - ) - self.assertEqual([at.name for at in atoms], result) diff --git a/MDANSE/pyproject.toml b/MDANSE/pyproject.toml index 34a2d630fa..661ba00e82 100644 --- a/MDANSE/pyproject.toml +++ b/MDANSE/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools", "cython", "numpy", "wheel"] +requires = ["setuptools", "numpy", "wheel"] build-backend = "setuptools.build_meta" [project] name = "MDANSE" -version = "2.0.0b2" +version = "2.0.0b3" description = 'MDANSE Core package - Molecular Dynamics trajectory handling and analysis code' readme = "README.md" requires-python = ">=3.9" @@ -36,7 +36,8 @@ dependencies = [ "ase", "rdkit", "MDAnalysis", - "mdtraj" + "mdtraj", + "networkx" ] # dynamic = ["version", "description"] diff --git a/MDANSE/setup.py b/MDANSE/setup.py index 9958bb922b..4b6c7e87b0 100644 --- a/MDANSE/setup.py +++ b/MDANSE/setup.py @@ -1,323 +1,258 @@ -import fnmatch -import glob -import os -import sys - -import numpy - -from setuptools import setup, Extension, find_packages -from Cython.Distutils import build_ext as cython_build_ext - -from distutils.sysconfig import get_config_vars -from distutils.util import convert_path - -try: - import sphinx -except ImportError: - sphinx = None - - -################################# -# Modules variables -################################# -EXCLUDE = ["*.py", "*.pyc", "*$py.class", "*~", ".*", "*.bak", "*.so", "*.pyd"] - -EXCLUDE_DIRECTORIES = ( - ".*", - "CVS", - "_darcs", - "./build", - "*svn", - "./dist", - "EGG-INFO", - "*.egg-info", -) - -EXTENSIONS_PATH = "Extensions" - -INCLUDE_DIR = [numpy.get_include()] - -################################# -# Helper function -################################# - - -def is_package(path): - return os.path.isdir(path) and os.path.isfile(os.path.join(path, "__init__.py")) - - -def find_package_data( - where=".", - package="", - exclude=EXCLUDE, - exclude_directories=EXCLUDE_DIRECTORIES, - only_in_packages=True, - show_ignored=False, -): - out = {} - stack = [(convert_path(where), "", package, only_in_packages)] - while stack: - where, prefix, package, only_in_packages = stack.pop(0) - for name in os.listdir(where): - fn = os.path.join(where, name) - if os.path.isdir(fn): - bad_name = False - for pattern in exclude_directories: - if ( - fnmatch.fnmatchcase(name, pattern) - or fn.lower() == pattern.lower() - ): - bad_name = True - if show_ignored: - print >> sys.stderr, ( - "Directory %s ignored by pattern %s" % (fn, pattern) - ) - break - if bad_name: - continue - if os.path.isfile(os.path.join(fn, "__init__.py")) and not prefix: - if not package: - new_package = name - else: - new_package = package + "." + name - stack.append((fn, "", new_package, False)) - else: - stack.append((fn, prefix + name + "/", package, only_in_packages)) - elif package or not only_in_packages: - # is a file - bad_name = False - for pattern in exclude: - if ( - fnmatch.fnmatchcase(name, pattern) - or fn.lower() == pattern.lower() - ): - bad_name = True - if show_ignored: - print >> sys.stderr, ( - "File %s ignored by pattern %s" % (fn, pattern) - ) - break - if bad_name: - continue - out.setdefault(package, []).append(prefix + name) - - return out - - -def find_data( - where=".", exclude=EXCLUDE, exclude_directories=EXCLUDE_DIRECTORIES, prefix="" -): - out = {} - stack = [convert_path(where)] - while stack: - where = stack.pop(0) - for name in os.listdir(where): - fn = os.path.join(where, name) - d = os.path.join(prefix, os.path.dirname(fn)) - if os.path.isdir(fn): - stack.append(fn) - else: - bad_name = False - for pattern in exclude: - if ( - fnmatch.fnmatchcase(name, pattern) - or fn.lower() == pattern.lower() - ): - bad_name = True - break - if bad_name: - continue - out.setdefault(d, []).append(fn) - - out = [(k, v) for k, v in out.items()] - - return out - - -################################# -# User data section -################################# - -DATA_FILES = [] - - -################################# -# Documentation -################################# - -if sphinx: - try: - from sphinx.ext.apidoc import main as sphinx_apidoc_main - except ImportError: - from sphinx.apidoc import main as sphinx_apidoc_main - - # class mdanse_build_doc(sphinx.setup_command.BuildDoc): - class mdanse_build_doc: - def run(self): - build = self.get_finalized_command("build") - - buildDir = os.path.abspath(build.build_lib) - - if not os.path.exists(buildDir): - raise IOError("build command must be performed prior building the doc") - - sys.path.insert(0, buildDir) - - sphinxDir = os.path.abspath( - os.path.join(build.build_base, "sphinx", self.doctype) - ) - - if not os.path.exists(sphinxDir): - os.makedirs(sphinxDir) - - metadata = self.distribution.metadata - args = [ - "-F", - "--separate", - "-H%s" % metadata.name, - "-A%s" % metadata.author, - "-R%s" % metadata.version, - "-o%s" % sphinxDir, - os.path.join(buildDir, "MDANSE"), - os.path.join(buildDir, "MDANSE", "Externals"), - ] - - # /!\ apidoc.main is deprecated. The API has been broken in sphinx 1.7.0, see https://github.com/sphinx-doc/sphinx/issues/4615 - if int(sphinx.__version__.split(".")[1]) <= 6: - args.insert(0, "") - - sphinx_apidoc_main(args) - - currentDirectory = os.getcwd() - - import shutil - - shutil.copy( - os.path.join(currentDirectory, "Doc", "conf_%s.py" % self.doctype), - os.path.join(sphinxDir, "conf.py"), - ) - shutil.copy( - os.path.join(currentDirectory, "Doc", "mdanse_logo.png"), - os.path.join(sphinxDir, "_static"), - ) - shutil.copy( - os.path.join(currentDirectory, "Doc", "layout.html"), - os.path.join(sphinxDir, "_templates"), - ) - - # The directory where the rst files are located. - self.source_dir = sphinxDir - # The directory where the conf.py file is located. - self.config_dir = self.source_dir - - # The directory where the documentation will be built - self.build_dir = os.path.join(buildDir, "MDANSE", "Doc", self.doctype) - - if isinstance(self.builder, str): - builders = [self.builder] - else: - builders = self.builder - - for builder in builders: - self.builder_target_dir = os.path.join(self.build_dir, builder) - sphinx.setup_command.BuildDoc.finalize_options(self) - sphinx.setup_command.BuildDoc.run(self) - - sys.path.pop(0) - - class mdanse_build_help(mdanse_build_doc): - doctype = "help" - - class mdanse_build_api(mdanse_build_doc): - doctype = "api" - - -################################# -# Extensions section -################################# - -if "linux" in sys.platform: - (opt,) = get_config_vars("OPT") - os.environ["OPT"] = " ".join( - flag for flag in opt.split() if flag != "-Wstrict-prototypes" - ) - -EXTENSIONS = [ - Extension( - "MDANSE.Extensions.atoms_in_shell", - include_dirs=INCLUDE_DIR, - sources=[os.path.join("Extensions", "atoms_in_shell.pyx")], - ), - Extension( - "MDANSE.Extensions.com_trajectory", - include_dirs=INCLUDE_DIR, - sources=[os.path.join("Extensions", "com_trajectory.pyx")], - ), - Extension( - "MDANSE.Extensions.contiguous_coordinates", - include_dirs=INCLUDE_DIR, - sources=[os.path.join("Extensions", "contiguous_coordinates.pyx")], - ), - Extension( - "MDANSE.Extensions.van_hove", - include_dirs=INCLUDE_DIR, - sources=[os.path.join("Extensions", "van_hove.pyx")], - ), - Extension( - "MDANSE.Extensions.fast_calculation", - include_dirs=INCLUDE_DIR, - sources=[os.path.join("Extensions", "fast_calculation.pyx")], - ), - Extension( - "MDANSE.Extensions.sas_fast_calc", - include_dirs=INCLUDE_DIR, - sources=[os.path.join("Extensions", "sas_fast_calc.pyx")], - ), - Extension( - "MDANSE.Extensions.mic_fast_calc", - include_dirs=INCLUDE_DIR, - sources=[os.path.join("Extensions", "mic_fast_calc.pyx")], - language="c++", - ), - Extension( - "MDANSE.Extensions.xtc", - include_dirs=[ - numpy.get_include(), - os.path.join("Extensions", "xtc", "include"), - ], - sources=glob.glob(os.path.join("Extensions", "xtc", "src", "*.c")) - + [os.path.join("Extensions", "xtc", "xtc.pyx")], - ), - Extension( - "MDANSE.Extensions.trr", - include_dirs=[ - numpy.get_include(), - os.path.join("Extensions", "xtc", "include"), - ], - sources=glob.glob(os.path.join("Extensions", "xtc", "src", "*.c")) - + [os.path.join("Extensions", "xtc", "trr.pyx")], - ), -] - -for ext in EXTENSIONS: - ext.cython_directives = {"language_level": "3"} - -CMDCLASS = {"build_ext": cython_build_ext} - -if sphinx: - CMDCLASS["build_api"] = mdanse_build_api - CMDCLASS["build_help"] = mdanse_build_help - -################################# -# The setup section -################################# - -setup( - name="MDANSE", - packages=find_packages("Src"), - package_dir={"": "Src"}, - data_files=DATA_FILES, - platforms=["Unix", "Windows"], - ext_modules=EXTENSIONS, - cmdclass=CMDCLASS, -) +import fnmatch +import os +import sys + +import numpy + +from setuptools import setup, find_packages + +from distutils.sysconfig import get_config_vars +from distutils.util import convert_path + +try: + import sphinx +except ImportError: + sphinx = None + + +################################# +# Modules variables +################################# +EXCLUDE = ["*.py", "*.pyc", "*$py.class", "*~", ".*", "*.bak", "*.so", "*.pyd"] + +EXCLUDE_DIRECTORIES = ( + ".*", + "CVS", + "_darcs", + "./build", + "*svn", + "./dist", + "EGG-INFO", + "*.egg-info", +) + +INCLUDE_DIR = [numpy.get_include()] + +################################# +# Helper function +################################# + + +def is_package(path): + return os.path.isdir(path) and os.path.isfile(os.path.join(path, "__init__.py")) + + +def find_package_data( + where=".", + package="", + exclude=EXCLUDE, + exclude_directories=EXCLUDE_DIRECTORIES, + only_in_packages=True, + show_ignored=False, +): + out = {} + stack = [(convert_path(where), "", package, only_in_packages)] + while stack: + where, prefix, package, only_in_packages = stack.pop(0) + for name in os.listdir(where): + fn = os.path.join(where, name) + if os.path.isdir(fn): + bad_name = False + for pattern in exclude_directories: + if ( + fnmatch.fnmatchcase(name, pattern) + or fn.lower() == pattern.lower() + ): + bad_name = True + if show_ignored: + print >> sys.stderr, ( + "Directory %s ignored by pattern %s" % (fn, pattern) + ) + break + if bad_name: + continue + if os.path.isfile(os.path.join(fn, "__init__.py")) and not prefix: + if not package: + new_package = name + else: + new_package = package + "." + name + stack.append((fn, "", new_package, False)) + else: + stack.append((fn, prefix + name + "/", package, only_in_packages)) + elif package or not only_in_packages: + # is a file + bad_name = False + for pattern in exclude: + if ( + fnmatch.fnmatchcase(name, pattern) + or fn.lower() == pattern.lower() + ): + bad_name = True + if show_ignored: + print >> sys.stderr, ( + "File %s ignored by pattern %s" % (fn, pattern) + ) + break + if bad_name: + continue + out.setdefault(package, []).append(prefix + name) + + return out + + +def find_data( + where=".", exclude=EXCLUDE, exclude_directories=EXCLUDE_DIRECTORIES, prefix="" +): + out = {} + stack = [convert_path(where)] + while stack: + where = stack.pop(0) + for name in os.listdir(where): + fn = os.path.join(where, name) + d = os.path.join(prefix, os.path.dirname(fn)) + if os.path.isdir(fn): + stack.append(fn) + else: + bad_name = False + for pattern in exclude: + if ( + fnmatch.fnmatchcase(name, pattern) + or fn.lower() == pattern.lower() + ): + bad_name = True + break + if bad_name: + continue + out.setdefault(d, []).append(fn) + + out = [(k, v) for k, v in out.items()] + + return out + + +################################# +# User data section +################################# + +DATA_FILES = [] + + +################################# +# Documentation +################################# + +if sphinx: + try: + from sphinx.ext.apidoc import main as sphinx_apidoc_main + except ImportError: + from sphinx.apidoc import main as sphinx_apidoc_main + + # class mdanse_build_doc(sphinx.setup_command.BuildDoc): + class mdanse_build_doc: + def run(self): + build = self.get_finalized_command("build") + + buildDir = os.path.abspath(build.build_lib) + + if not os.path.exists(buildDir): + raise IOError("build command must be performed prior building the doc") + + sys.path.insert(0, buildDir) + + sphinxDir = os.path.abspath( + os.path.join(build.build_base, "sphinx", self.doctype) + ) + + if not os.path.exists(sphinxDir): + os.makedirs(sphinxDir) + + metadata = self.distribution.metadata + args = [ + "-F", + "--separate", + "-H%s" % metadata.name, + "-A%s" % metadata.author, + "-R%s" % metadata.version, + "-o%s" % sphinxDir, + os.path.join(buildDir, "MDANSE"), + os.path.join(buildDir, "MDANSE", "Externals"), + ] + + # /!\ apidoc.main is deprecated. The API has been broken in sphinx 1.7.0, see https://github.com/sphinx-doc/sphinx/issues/4615 + if int(sphinx.__version__.split(".")[1]) <= 6: + args.insert(0, "") + + sphinx_apidoc_main(args) + + currentDirectory = os.getcwd() + + import shutil + + shutil.copy( + os.path.join(currentDirectory, "Doc", "conf_%s.py" % self.doctype), + os.path.join(sphinxDir, "conf.py"), + ) + shutil.copy( + os.path.join(currentDirectory, "Doc", "mdanse_logo.png"), + os.path.join(sphinxDir, "_static"), + ) + shutil.copy( + os.path.join(currentDirectory, "Doc", "layout.html"), + os.path.join(sphinxDir, "_templates"), + ) + + # The directory where the rst files are located. + self.source_dir = sphinxDir + # The directory where the conf.py file is located. + self.config_dir = self.source_dir + + # The directory where the documentation will be built + self.build_dir = os.path.join(buildDir, "MDANSE", "Doc", self.doctype) + + if isinstance(self.builder, str): + builders = [self.builder] + else: + builders = self.builder + + for builder in builders: + self.builder_target_dir = os.path.join(self.build_dir, builder) + sphinx.setup_command.BuildDoc.finalize_options(self) + sphinx.setup_command.BuildDoc.run(self) + + sys.path.pop(0) + + class mdanse_build_help(mdanse_build_doc): + doctype = "help" + + class mdanse_build_api(mdanse_build_doc): + doctype = "api" + + +################################# +# Extensions section +################################# + +if "linux" in sys.platform: + (opt,) = get_config_vars("OPT") + os.environ["OPT"] = " ".join( + flag for flag in opt.split() if flag != "-Wstrict-prototypes" + ) + +CMDCLASS = {} + +if sphinx: + CMDCLASS["build_api"] = mdanse_build_api + CMDCLASS["build_help"] = mdanse_build_help + +################################# +# The setup section +################################# + +setup( + name="MDANSE", + packages=find_packages("Src"), + package_dir={"": "Src"}, + data_files=DATA_FILES, + platforms=["Unix", "Windows"], + cmdclass=CMDCLASS, +) diff --git a/MDANSE_GUI/CHANGELOG b/MDANSE_GUI/CHANGELOG index 62253943fc..42eac234be 100644 --- a/MDANSE_GUI/CHANGELOG +++ b/MDANSE_GUI/CHANGELOG @@ -1,240 +1,12 @@ -version 1.5.2 --------------- -* CHANGED issue #191 Extended and improved the text output of Analysis -* FIXED issues #178 #182 minor GUI problems have been removed -* CHANGED issue #157 DISABLED 3D viewer on ARM (Apple Silicon) MacOS; this will only be fixed in MDANSE 2 -* ADDED issue #167 CP2K Trajectory Converter with example files have been added to MDANSE -* CHANGED issue #165 The Trajectory Viewer functionality can now be opened without having to open the Molecular Viewer first -* FIXED issue #158 Molecular Viewer and Elevation, Iso-Surface, and Scalar-Field plotters no longer crash MDANSE on Ubuntu 22 -* FIXED issue #152 MDANSE API documentation that comes with released installers now has class inheritance diagrams displayed properly -* FIXED issue #151 MDANSE should no longer interfere with system functionalities such as nano on Ubuntu 22 -* FIXED issue #148 The python2 script that comes with MacOS installer can now be used to run scripts -* FIXED issue #147 MDANSE installed with the installer can now be started on MacOS 11 and MacOS 12. However, Molecular Viewer, Animation, and the Elevation, Iso-Surface, and Scalar-Field plotters continue to crash MDANSE, which will not be fixed until MDANSE 2.0.0 -* CHANGED (internal) issue #136 MDANSE is now tested only on MacOS 11 and MacOS 12 (for MacOS) - - -version 1.5.1 --------------- -* ADDED issue #129 Installers for Ubuntu 22.x are now automatically generated and available for download -* FIXED issue #131 Importing MDANSE and other installed packages no longer raises exceptions -* FIXED issue #126 The error messages are now helpful when incorrect input is provided in fields like DL_POLY's 'aliases' that take Python object-like input -* CHANGED issue #127 The minimum size of Quick View in 2D/3D Plotter is now much smaller, allowing for resizing downwards - -version 1.5.0 --------------- -* ADDED issue #93 HDF5 format can now be used as output format of analyses as well as input for plotters -* ADDED issue #75 Added a new Units Editor window for handling unit definitions and expanded the range of unit conversions in plotters. The internal unit handling has been also rewritten -* ADDED issue #71 Trajectory files generated by MDANSE now contain units -* ADDED (internal) issue #68 The data used by plotters is now abstracted, allowing for easier implementation of different data format inputs -* ADDED issue #62 A new MDANSE script, mdanse_job, has been added which can be used to directly open the GUI window for a job -* ADDED issue #58 2 new plotters for visualising trajectory variables (configuration, velocities, and gradients) have been added -* ADDED issue #49 The new MDANSE User Guide is now accessible from the Help menu -* ADDED issue #45 Data Info now also lists the variables present in the trajectory -* ADDED issue #43 Gromacs converter can now be used to also convert TRR files. This creates a trajectory with velocities and gradients if present -* ADDED issue #26 Added velocity interpolation to Current Correlation Function -* ADDED issue #25 The Pair Distribution Function analysis now also calculates Radial Distribution Function and Total Correlation Function -* FIXED issue #100 The Atoms List Selection now displays the correct number in the 'Number of atoms' field and prevents its change when opened from an analysis -* FIXED issue #98 Centre of masses option in the Eccentricity analysis now works correctly -* FIXED issue #95 MaterialsStudio converters (such as Forcite, Discover, DFTB) now create atom clusters correctly -* FIXED issue #69 Fixed the variables generated by the Rigid Body Trajectory job -* FIXED issue #48 The 'Toggle toolbar' button now works correctly on Windows -* FIXED issue #46 Trajectory files generated by MDANSE no longer include temperature and kinetic energy variables -* FIXED issue #31 Gromacs converter is now able to process files of any size -* FIXED issue #30 Scalar Field Plotter now works on Windows -* FIXED issue #29 Elements Database GUI now works properly -* FIXED issue #22 Normalisation now works correctly in Position Autocorrelation Function and General Autocorrelation Function -* FIXED issue #21 Opening an analysis that implements Atom Selection or Reference Basis no longer causes other analyses' GUI windows to crash when opened -* FIXED issue #17 MDANSE can now be opened on all recent versions of MacOS, including computers with the M1 chip -* FIXED issue #15 CASTEP converter now works on MD files with headers of any size -* CHANGED issue #42 The 'variable' box in 2D/3D Plotter is now larger and resizeable -* CHANGED issue #41 Improved the default names of output files of analyses and conversions. A number is also appended to prevent overwriting -* CHANGED (internal) issue #67 Where possible Scientific.IO.NetCDF has been replaced with netCDF4 package -* CHANGED issue #28 Simple Help is now formatted nicely -* CHANGED issue #24 Plotters no longer show variables which contain non-numeric values -* CHANGED (internal) issue #19 The CI/CD has been migrated to use GitHub Actions and GitHub-hosted runners -* CHANGED (internal) MDANSE is now maintained by the ISIS Neutron and Muon Source. Licensing and other documents now reflect this - -version 1.5.0.ill (unreleased) --------------- -* ADDED issue #59 Added smart_association in LAMMPS converter. When two or more masses are matching in a LAMMPS trajectory, and smart_association is set to True, we select the nearest one. -* FIXED issue #58 Some LAMMPS files could not be converted with MDANSE -* FIXED issue #57 On macOS, MDANSE did not use the embedded python -* CHANGED LAMMPS converter accepts now "0" as the number of steps (=> automatic detection of the steps number) -* CHANGED Default mass tolerance in LAMMPS converter in now 10^-3 instead of 10^-5 -* CHANGED (internal) netCDF version has been updated on macOS - -version 1.4.0 --------------- -* FIXED issue #56 Layout on Windows differed from macOS and Linux (we keep Windows Layout: "Data" on top, "Plugins" below) -* FIXED issue #55 Plotter (1D, 2D, Plotter, NetCDF Plotter) had several issues (see https://code.ill.fr/scientific-software/mdanse/issues/55) -* FIXED issue #54 Some Gromacs trajectories cannot be opened (due to the presence of "M" dummy atoms) -* FIXED issue #53 VASP NPT trajectories could not be opened -* FIXED issue #52 QVector circular lattice generator created an error -* FIXED issue #51 SFFSF job was accessible from MMTK trajectory and molecular viewer -* FIXED issue #50 Some results could be inconsistent when using multiple atom selections -* FIXED issue #49 Some LAMMPS files could not be converted -* FIXED issue #48 There was an error in NDTSF analysis -* FIXED issue #47 GUI converter did not close -* CHANGED (internal) Docker build has been changed -* CHANGED (internal) macOS build has been changed - -version 1.3.1 --------------- -* FIXED issue #46 There was an error message when closign converter plugins. The error is no more here but the converter still does not close. - -version 1.3.0 --------------- -* ADDED Neutron Dynamic Total Structure Factor analysis -* ADDED User can now launch a script with qvector parameters instead of saved qvector definition -* FIXED issue #45 MSD computing on non-orthorombic universe was wrong (fixed with new mmtk version) /!\ Modifies MSD, VACF and DoS, and maybe others... /!\ Units in MMTK have slightly changed -* FIXED issue #44 An error message appeared when display, close and reopen molecular viewer and animation plugins -* FIXED issue #43 The weights are no more normalized with absolute sum /!\ Modifies at least CCF, DCSF and SSF job behavior -* FIXED issue #42 Plotter units could be inconsistent -* FIXED issue #41 Instrument resolution window froze GUI on macOS -* FIXED issue #40 MDANSE could not be used on Ubuntu Bionic systems (due to vtk dependency name) -* FIXED issue #38 Interface was frozen when jobs were launched. A delay of 2s is added at the end of jobs before suppressing the status file - -version 1.2.1 --------------- -* FIXED issue #39 Time scale in Gromacs converter was incorrect - -version 1.2.0 --------------- -* ADDED issue #27 Better error handling when an error occurs when running an analysis from the GUI -* ADDED issue #10 Implemented Gromacs trajectory converter -* FIXED issue #36 Jobs could not be launched on Windows machines -* FIXED issue #35 Result of RBT was incorrect /!\ Modifies API (removes stepwise parameter) /!\ Modifies job behavior -* FIXED issue #32 Result of DCSF was incorrect (the QVectors changed over frames) /!\ Modifies job behavior -* FIXED issue #31 Result of RMSD was incorrect (the sqrt was not applied) /!\ Modifies job behavior -* FIXED issue #30 Normalization of VACF total value was incorrect /!\ Modifies job behavior -* FIXED issue #29 Some jobs (AC, CCF, DOS, DACF, DCSF, DISF, GACF, GDISF, MSD, PACF, RMSD, VACF) did not behave properly if first time of the MD is not zero /!\ Modifies jobs behaviors -* FIXED issue #28 The time step is now part of the settings for DCD based converters /!\ Modifies API (with default value) /!\ Modifies jobs CHARMM, NAMD and XPLOR behaviors -* FIXED issue #26 Data can be deleted from the GUI -* FIXED issue #25 The plotter is now compliant with all matplotlib versions (was crashing on ubuntu-xenial) -* FIXED issue #24 Q vectors in CCF Job were incorrect /!\ Modifies job behavior -* FIXED issue #21 "Save a template for new analysis" button produced an error -* FIXED issue #20 Opening the theoretical help on MacOS is now OK -* FIXED issue #18 The items of the checkable combobox are now checkable on Windows -* FIXED issue #16 The file filter combo box is now available from the input file dialog on MacOS -* CHANGED (internal) issue #34 source code is moved to Src/ instead of MDANSE/ -* CHANGED (internal) MDANSE can now be built without documentation -* CHANGED (internal) issue #33 The macOS dmg is now ligher than before -* CHANGED (internal) MDANSE is now built with system python on macOS (2.7.10) - -version 1.1.3 --------------- -* FIXED issue #19 Users could not display user definitions -* FIXED issue #17 The plotter crashed when user was clicking on "plot in new window" - -version 1.1.2 --------------- -* ADDED issue #15 An automatic mode for data loading is added to compel with the fact that data filter does not appear in data loading popup on macOS - -version 1.1.1 --------------- -* FIXED issue #14 Package name was not valid for debian package in 1.1.0 version -* CHANGED Moving artifacts for more convenient user downloading - -version 1.1.0 --------------- -* ADDED issue #12 MDANSE has now three different development phases (beta, rc, release) -* ADDED issue #11 MDANSE is now compliant with 16.04 and 18.04 Ubuntu systems -* CHANGED Replacing all "output_file" occurences by "output_files" /!\ Modifies API /!\ -* FIXED issue #13 The changelog was not updated in the windows installer -* FIXED issue #9 updated the macos build scripts in order to have an universal dmg -* FIXED issue #7 suppression of the appearance of an error dialog when closing widgets -* ADDED (internal) Reference data files integrated in functional tests -* CHANGED (internal) MDANSE is now build on new build servers (2018) -* CHANGED (internal) Complete refactoring of build scripts - -version 1.0.11 -------------- -* bug fix when converting MS trajectories due to a too small value for recursionlimit parameter - -version 1.0.10 -------------- -* bug fix due to missing import in ElementsDatabaseEditor -* bug fix when loading trajectory with missing unknown MMTK atoms - -version 1.0.9 -------------- -* use the short git commit id instead of long - -version 1.0.8 -------------- -* bug fix when getting git commit id in long format - -version 1.0.7 -------------- -* = 1.0.6.a - -version 1.0.6.a ----------------- -* bug fix when setting the commit id in __pkginfo__ - -version 1.0.6 ----------------- -* the bug report button opens now the default mail client for sending bug report -* the __pkginfo__ is now updated with the commit id when MDANSE is built - -version 1.0.5 ----------------- -* bug fix on macos when closing the application from the Finder: the jobs manager thread was not properly closed -* the macos dmg file was corrupted due to the use of pkg_resources module for storing resources - -version 1.0.4.rc4 ----------------- -* bug fix in coordination number analysis: some pairs were never taken into account -* bug fix when transmutating atoms from an atom selection different to 'all' - -version 1.0.4.rc3 ----------------- -* bug fix in debian packager: add the python-setuptools dependency -* improved LAMMPS converter: the parsing of the config file is more tolerance in term of required keyword format - -version 1.0.4.rc2 ----------------- -* added support for gitflow bugfix branch to gitlab config file -* the DL_POLY converter is now compliant with DL_POLY 4 -* added the mass tolerance to LAMMP converter -* updated the LAMMP example trajectory with more accurate masses - -version 1.0.4.rc1 ----------------- -* bug fix in IWidget: the label of the configurator was not taken into account -* bug fix path_to_module function: the list of directories has to be parsed backwards -* raise an error when none or more than atom matches the mass stored in the LAMMPS config file -* modified the label for r value - -version 1.0.3.e: ----------------- -* bug fix relative to REGISTRY update - -version 1.0.3.d: ----------------- -* redirected the sys.stdout and sys.stderr to the LOGGER -* implemented a write method so as ConsoleHandler behaves as a file-like object -* the job short name appears now explicitely in the saved job files -* small refactoring of MDANSE __init__ -* small refactoring of ClassRegistry -* removed the explicit imports of the framework classes -* the plotter show the last loaded data now -* bug fix in rmsd job: the PBC were not applied to the calculation -* bug fix in AtomSelectionConfigurator::get_indexes -* bug fix when saving user definitions -* bug fix with the initial size of the user definitions plugins -* updated the job template writing -* small refactoring of the save job template command line option -* removed unused imports -* removed white spaces - -version 1.0.3.c: ----------------- -* bug fix with JobControllerPanel in case of crashing jobs -* regression fixed due to trials to get rid of relative imports MDANSE/Externals/pubsub package - -version 1.0.3.a: ----------------- -* updated `MDANSE/__pkginfo__.py` module - +version 0.1.0a3 +--------------- +* CHANGED plotting interface to a new 2-tab design +* ADDED user settings connected to every tab +* ADDED instrument definitions as a separate tab + +version 0.1.0a1 +--------------- +* ADDED a new interface based on qtpy +* ADDED a VTK 3D viewer adapted from E. Pellegrini's Waterstay project +* ADDED a plotting mechanism based on E. Pellegrini's implementation diff --git a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomSelectionWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomSelectionWidget.py index e4e9eafcf6..c7c6c784dc 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomSelectionWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomSelectionWidget.py @@ -92,7 +92,7 @@ def __init__( self.selector = selector self._field = field self.settings = self.selector.settings - self.atm_full_names = [atm.full_name for atm in self.selector.system.atom_list] + self.atm_full_names = self.selector.system.name_list self.selection_textbox = QPlainTextEdit() self.selection_textbox.setReadOnly(True) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomTransmutationWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomTransmutationWidget.py index 82dec8a880..901c846a50 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomTransmutationWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/AtomTransmutationWidget.py @@ -139,9 +139,7 @@ def update_transmutation_textbox(self) -> None: text = [f"Number of atoms transmuted:\n{len(map)}\n\nTransmuted atoms:\n"] atoms = self.selector.system.atom_list for idx, symbol in map.items(): - text.append( - f"{idx} ({atoms[idx].full_name}): {atoms[idx].symbol} -> {symbol}\n" - ) + text.append(f"{idx} {atoms[idx]} -> {symbol}\n") self.transmutation_textbox.setText("".join(text)) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/PartialChargeWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/PartialChargeWidget.py index da85f47d87..d9d9b97703 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/PartialChargeWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/PartialChargeWidget.py @@ -134,7 +134,7 @@ def update_charge_textbox(self) -> None: text = [f"Partial charge mapping:\n"] atoms = self.selector.system.atom_list for idx, charge in map.items(): - text.append(f"{idx} ({atoms[idx].full_name}) -> {charge}\n") + text.append(f"{idx} ({atoms[idx]}) -> {charge}\n") self.charge_textbox.setText("".join(text)) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/QVectorsWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/QVectorsWidget.py index 3b4893709d..b61d868b58 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/QVectorsWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/InputWidgets/QVectorsWidget.py @@ -28,17 +28,19 @@ class VectorModel(QStandardItemModel): type_changed = Signal() input_is_valid = Signal(bool) - def __init__(self, *args, chemical_system=None, **kwargs): + def __init__(self, *args, trajectory=None, **kwargs): super().__init__(*args, **kwargs) self._generator = None self._defaults = [] - self._chemical_system = chemical_system + self._trajectory = trajectory @Slot(str) def switch_qvector_type(self, vector_type: str, optional_settings: dict = None): self.clear() self._defaults = [] - self._generator = IQVectors.create(vector_type, self._chemical_system) + self._generator = IQVectors.create( + vector_type, self._trajectory.configuration(0) + ) settings = self._generator.settings for kv in settings.items(): name = kv[0] # dictionary key @@ -111,12 +113,12 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._relative_size = 3 trajectory_configurator = kwargs.get("trajectory_configurator", None) - chemical_system = None + trajectory = None if trajectory_configurator is not None: - chemical_system = trajectory_configurator["instance"].chemical_system + trajectory = trajectory_configurator["instance"] self._selector = QComboBox(self._base) self._selector.addItems(IQVectors.indirect_subclasses()) - self._model = VectorModel(self._base, chemical_system=chemical_system) + self._model = VectorModel(self._base, trajectory=trajectory) self._view = QTableView(self._base) self._layout.addWidget(self._selector) self._layout.addWidget(self._view) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/AtomProperties.py b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/AtomProperties.py index cd38488484..f6e52d86a6 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/AtomProperties.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/AtomProperties.py @@ -22,7 +22,7 @@ from qtpy.QtGui import QStandardItemModel, QStandardItem, QColor from qtpy.QtCore import Signal, Slot, QObject, Qt -from MDANSE.Chemistry.Databases import AtomsDatabase +from MDANSE.MolecularDynamics.Trajectory import Trajectory RGB_COLOURS = [] RGB_COLOURS.append((1.00, 0.20, 1.00)) # selection @@ -149,7 +149,7 @@ def add_colour(self, rgb: tuple[int, int, int]) -> int: def reinitialise_from_database( self, atoms: list[str], - element_database: AtomsDatabase, + element_database: Trajectory, dummy_size: Optional[float] = None, ) -> list[int]: """Puts colours into the list based on chemical elements list @@ -189,9 +189,12 @@ def reinitialise_from_database( ): size = dummy_size else: - size = round(element_database[atom]["vdw_radius"], 2) + size = round(element_database.get_atom_property(atom, "vdw_radius"), 2) atom_entry = AtomEntry() - rgb = [int(x) for x in element_database[atom]["color"].split(";")] + rgb = [ + int(x) + for x in element_database.get_atom_property(atom, "color").split(";") + ] colour_index_list.append(self.add_colour(rgb)) item_row = atom_entry.set_values( atom, diff --git a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/Controls.py b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/Controls.py index e3d0bdc0b2..49518e1aa8 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/Controls.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/Controls.py @@ -236,7 +236,7 @@ def createSidePanel(self): layout4 = QHBoxLayout(wrapper4) size_factor = QDoubleSpinBox(base) size_factor.setToolTip("Scaling factor for atom size") - size_factor.setValue(0.8) + size_factor.setValue(0.4) size_factor.setMinimum(0.0) size_factor.setMaximum(50.0) size_factor.setSingleStep(0.05) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/MolecularViewer.py b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/MolecularViewer.py index 0450f67201..7f48c6a2fd 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/MolecularViewer.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/MolecularViewer.py @@ -28,7 +28,6 @@ from vtkmodules.vtkRenderingAnnotation import vtkAxesActor from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData -from MDANSE.Chemistry import ATOMS_DATABASE as CHEMICAL_ELEMENTS from MDANSE.MLogging import LOG from MDANSE_GUI.MolecularViewer.readers import hdf5wrapper @@ -71,9 +70,10 @@ class MolecularViewer(QtWidgets.QWidget): def __init__(self): super(MolecularViewer, self).__init__() - self._scale_factor = 0.8 + self._scale_factor = 0.4 self._datamodel = None + self._element_database = None self._iren = QVTKRenderWindowInteractor(self) @@ -309,7 +309,7 @@ def create_traj_actors(self, polydata, line_opacity=1.0, ball_opacity=1.0): else: glyph.SetInputData(polydata) - temp_scale = float(0.2 * self._scale_factor) + temp_scale = float(1.0 * self._scale_factor) glyph.SetScaleModeToScaleByScalar() glyph.SetColorModeToColorByScalar() glyph.SetScaleFactor(temp_scale) @@ -618,6 +618,7 @@ def set_reader(self, reader): self._reader = reader + self._element_database = self._reader._trajectory self._n_atoms = self._reader.n_atoms self._n_frames = self._reader.n_frames self._current_frame = min(self._current_frame, self._n_frames - 1) @@ -630,19 +631,19 @@ def set_reader(self, reader): self._resolution = 4 if self._resolution < 4 else self._resolution self._atom_colours = self._colour_manager.reinitialise_from_database( - self._atoms, CHEMICAL_ELEMENTS, self.dummy_size + self._atoms, self._element_database, self.dummy_size ) # this returns a list of indices, mapping colours to atoms self._atom_scales = np.array( [ - CHEMICAL_ELEMENTS.get_atom_property(at, "vdw_radius") + self._element_database.get_atom_property(at, "vdw_radius") for at in self._atoms ] ).astype(np.float32) self.du_log = np.array( [ - CHEMICAL_ELEMENTS.get_atom_property(at, "element") != "dummy" + self._element_database.get_atom_property(at, "dummy") == 0 for at in self._reader.atom_types ] ) @@ -650,12 +651,12 @@ def set_reader(self, reader): [ i for i, at in enumerate(self._reader.atom_types) - if CHEMICAL_ELEMENTS.get_atom_property(at, "element") != "dummy" + if self._element_database.get_atom_property(at, "dummy") == 0 ] ) self.covs = np.array( [ - CHEMICAL_ELEMENTS.get_atom_property(at, "covalent_radius") + self._element_database.get_atom_property(at, "covalent_radius") for at in self._reader.atom_types ] ) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/hdf5wrapper.py b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/hdf5wrapper.py index c0c403c915..4d352801dc 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/hdf5wrapper.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/hdf5wrapper.py @@ -14,26 +14,31 @@ # along with this program. If not, see . # +import typing import numpy as np from MDANSE.MolecularDynamics.UnitCell import UnitCell +if typing.TYPE_CHECKING: + from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem + from MDANSE.MolecularDynamics.Trajectory import Trajectory + from MDANSE_GUI.MolecularViewer.readers.i_reader import IReader class HDF5Wrapper(IReader): - def __init__(self, fname, trajectory, chemical): + def __init__(self, fname, trajectory: "Trajectory", chemical: "ChemicalSystem"): super(HDF5Wrapper, self).__init__(fname) - self._n_atoms = chemical._number_of_atoms + self._n_atoms = chemical.number_of_atoms self._n_frames = len(trajectory) self._trajectory = trajectory self._chemical_system = chemical self._filename = fname - self._atom_ids = [atom.index for atom in chemical.atoms] - self._atom_types = [atom.symbol for atom in chemical.atoms] + self._atom_ids = chemical._atom_indices + self._atom_types = chemical.atom_list self._atom_names = [ - "_".join([str(x) for x in [atom.symbol, atom.index]]) - for atom in chemical.atoms + str(element) + "_" + str(index) + for element, index in zip(chemical.atom_list, chemical._atom_indices) ] def read_frame(self, frame: int) -> "np.array": diff --git a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/i_reader.py b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/i_reader.py index dfe481a184..39c073d2c0 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/i_reader.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/MolecularViewer/readers/i_reader.py @@ -20,7 +20,6 @@ from MDANSE.MLogging import LOG from MDANSE.Chemistry import ATOMS_DATABASE -from MDANSE.Chemistry import RESIDUES_DATABASE class InvalidFileError(Exception): @@ -143,7 +142,6 @@ def guess_atom_types(self): self._atom_types = [] for i in range(self._n_atoms): atom_name = self._atom_names[i] - residue_name = self._residue_names[i] # Remove the trailing and initial digits from the upperized atom names upper_atom_name = atom_name.upper() @@ -152,29 +150,16 @@ def guess_atom_types(self): # Case of the an atom that belongs to a standard residue # Guess the atom type by the starting from the first alpha letter from the left, # increasing the word by one letter if there was no success in guessing the atom type - if residue_name in RESIDUES_DATABASE: - start = 1 - while True: - upper_atom_name = upper_atom_name[:start] - if upper_atom_name in symbols: - self._atom_types.append(upper_atom_name.capitalize()) - break - if start > len(atom_name): - raise ValueError("Unknown atom type: {}".format(atom_name)) - start += 1 - # Case of the an atom that does not belong to a standard residue - # Guess the atom type by the starting from whole atom name, - # decreasing the word by one letter from the right if there was no success in guessing the atom type - else: - start = len(upper_atom_name) - while True: - upper_atom_name = upper_atom_name[:start] - if upper_atom_name in symbols: - self._atom_types.append(upper_atom_name.capitalize()) - break - if start == 0: - raise ValueError("Unknown atom type: {}".format(atom_name)) - start -= 1 + + start = len(upper_atom_name) + while True: + upper_atom_name = upper_atom_name[:start] + if upper_atom_name in symbols: + self._atom_types.append(upper_atom_name.capitalize()) + break + if start == 0: + raise ValueError("Unknown atom type: {}".format(atom_name)) + start -= 1 def read_atom_trajectory(self, index): """Read the trajectory of a single atom with a given index diff --git a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Views/TrajectoryView.py b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Views/TrajectoryView.py index 5837aa6cc7..a97932e770 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Views/TrajectoryView.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Views/TrajectoryView.py @@ -55,7 +55,8 @@ def deleteNode(self): model = self.model() index = self.currentIndex() node_number = model.itemFromIndex(index).data() - trajectory = model._nodes[node_number][0] + trajectory, instance = model._nodes[node_number] + instance.close() self.free_name.emit(str(trajectory)) model.removeRow(index.row()) self.item_details.emit(("", None)) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotDataInfo.py b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotDataInfo.py index 36df9d6a4b..e8bac120ac 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotDataInfo.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotDataInfo.py @@ -14,10 +14,15 @@ # along with this program. If not, see . # from collections import defaultdict +from typing import TYPE_CHECKING +import numpy as np from qtpy.QtCore import Slot, Signal from qtpy.QtWidgets import QTextBrowser +if TYPE_CHECKING: + from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem + class PlotDataInfo(QTextBrowser): error = Signal(str) @@ -37,13 +42,13 @@ def update_panel(self, input_text): filtered = self.filter(text) self.setHtml(filtered) - def summarise_chemical_system(self, cs): + def summarise_chemical_system(self, cs: "ChemicalSystem"): text = "\n ==== Chemical System summary ==== \n" - counter = defaultdict(int) - for atom in cs.atom_list: - counter[(atom.full_name, atom.symbol)] += 1 - for key, value in counter.items(): - text += f"Full Name: {key[0]}; Element: {key[1]}; Count: {value}\n" + atoms, counts = np.unique(cs.atom_list, return_counts=True) + for ind in range(len(atoms)): + text += f"Element: {atoms[ind]}; Count: {counts[ind]}\n" + for molname, mollist in cs._clusters.items(): + text += f"Molecule: {molname}; Count: {len(mollist)}" text += " ===== \n" return text diff --git a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/TrajectoryInfo.py b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/TrajectoryInfo.py index 765e30023f..3ada21f5d8 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/TrajectoryInfo.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/TrajectoryInfo.py @@ -13,11 +13,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from collections import defaultdict +from typing import TYPE_CHECKING + +import numpy as np from qtpy.QtCore import Slot, Signal from qtpy.QtWidgets import QTextBrowser +if TYPE_CHECKING: + from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem + class TrajectoryInfo(QTextBrowser): error = Signal(str) @@ -46,13 +51,13 @@ def update_panel(self, data: tuple): filtered = self.filter(text) self.setHtml(filtered) - def summarise_chemical_system(self, cs): + def summarise_chemical_system(self, cs: "ChemicalSystem"): text = "\n ==== Chemical System summary ==== \n" - counter = defaultdict(int) - for atom in cs.atom_list: - counter[(atom.full_name, atom.symbol)] += 1 - for key, value in counter.items(): - text += f"Full Name: {key[0]}; Element: {key[1]}; Count: {value}\n" + atoms, counts = np.unique(cs.atom_list, return_counts=True) + for ind in range(len(atoms)): + text += f"Element: {atoms[ind]}; Count: {counts[ind]}\n" + for molname, mollist in cs._clusters.items(): + text += f"Molecule: {molname}; Count: {len(mollist)}\n" text += " ===== \n" return text diff --git a/MDANSE_GUI/Tests/UnitTests/test_PlottingContext.py b/MDANSE_GUI/Tests/UnitTests/test_PlottingContext.py index 0d51fa8ede..7e98515da5 100644 --- a/MDANSE_GUI/Tests/UnitTests/test_PlottingContext.py +++ b/MDANSE_GUI/Tests/UnitTests/test_PlottingContext.py @@ -40,7 +40,8 @@ def test_single_dataset_2d(file_2d): def test_available_x_axes_1d(file_1d): temp = SingleDataset("dos_total", file_1d) axes = temp.available_x_axes() - assert axes == ["rad/ps"] + assert axes == ["omega"] + assert temp._axes_units[axes[0]] == "rad/ps" longest = temp.longest_axis() assert longest[0] == "rad/ps" @@ -49,7 +50,7 @@ def test_available_x_axes_2d(file_2d): temp = SingleDataset("f(q,t)_total", file_2d) axes = temp.available_x_axes() print(axes) - assert axes == ["1/nm", "ps"] + assert [temp._axes_units[axis] for axis in axes] == ["1/nm", "ps"] longest = temp.longest_axis() print(longest) assert longest[0] == "ps" diff --git a/MDANSE_GUI/Tests/UnitTests/test_hdf5wrapper.py b/MDANSE_GUI/Tests/UnitTests/test_hdf5wrapper.py index 7bf86fe2fc..0dec67270a 100644 --- a/MDANSE_GUI/Tests/UnitTests/test_hdf5wrapper.py +++ b/MDANSE_GUI/Tests/UnitTests/test_hdf5wrapper.py @@ -19,7 +19,7 @@ import numpy as np -from MDANSE.Chemistry.ChemicalEntity import Atom, ChemicalSystem +from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem from MDANSE.MolecularDynamics.Configuration import RealConfiguration from MDANSE.MolecularDynamics.Trajectory import Trajectory, TrajectoryWriter from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData @@ -34,8 +34,7 @@ def chemical_system(): temp = ChemicalSystem("Dummy test system") nAtoms = N_ATOMS - for i in range(nAtoms): - temp.add_chemical_entity(Atom(symbol="H")) + temp.initialise_atoms(nAtoms * ["H"]) return temp @@ -62,8 +61,7 @@ def sample_trajectory(chemical_system, sample_configuration): os.close(fdesc) writer = TrajectoryWriter(fname, chemical_system, n_steps=N_TIMESTEPS) for n, ts in enumerate(np.arange(N_TIMESTEPS)): - writer.chemical_system.configuration = sample_configuration - writer.dump_configuration(ts) + writer.dump_configuration(sample_configuration, ts) return fname diff --git a/MDANSE_GUI/pyproject.toml b/MDANSE_GUI/pyproject.toml index 274a259c1a..37f30ae1f0 100644 --- a/MDANSE_GUI/pyproject.toml +++ b/MDANSE_GUI/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "MDANSE_GUI" -version = "0.1.0b3" +version = "0.1.0b4" description = 'MDANSE GUI package - the graphical interface for MDANSE' readme = "README.md" requires-python = ">=3.9" diff --git a/MDANSE_GUI/setup.py b/MDANSE_GUI/setup.py index 7ed51e9d17..65295c04be 100644 --- a/MDANSE_GUI/setup.py +++ b/MDANSE_GUI/setup.py @@ -30,19 +30,8 @@ "*.egg-info", ) -EXTENSIONS_PATH = "Extensions" - INCLUDE_DIR = [numpy.get_include()] -QHULL_DIR = os.path.join("Extensions", "qhull_lib") - -QHULL_INCLUDE_DIR = ( - INCLUDE_DIR - + [EXTENSIONS_PATH] - + [os.path.join(QHULL_DIR, "ext")] - + [os.path.join(QHULL_DIR, "src")] -) - ################################# # Helper function #################################