├── .gitignore ├── ForcePy ├── Analysis.py ├── Basis.c ├── Basis.pyx ├── CGMap.py ├── ForceCategories.py ├── ForceMatch.py ├── Forces.py ├── Mesh.c ├── Mesh.pyx ├── NeighborList.c ├── NeighborList.pyx ├── States.py ├── Util.c ├── Util.pyx └── __init__.py ├── README.md ├── distribute_setup.py ├── ez_setup.py ├── scripts ├── convert_lammps_tables.py └── plot_forces.py ├── setup.py └── test ├── lj ├── lj.json ├── lj.obs ├── lj.pdb ├── lj.py ├── lj.xyz └── lj_parallel.py ├── methanol ├── cg.pdb ├── cg.trr ├── cg.xyz ├── methanol.json └── methanol.py ├── test_basis.py └── water ├── hbnum.obs ├── lammps.inp ├── spc.gro ├── spc.tpr ├── spc_cg.json ├── spc_cg.py └── traj.trr /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | build 3 | dist 4 | lammps 5 | 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Packages 11 | *.egg 12 | *.egg-info 13 | dist 14 | build 15 | eggs 16 | parts 17 | bin 18 | var 19 | sdist 20 | develop-eggs 21 | .installed.cfg 22 | lib 23 | lib64 24 | 25 | # Installer logs 26 | pip-log.txt 27 | 28 | # Unit test / coverage reports 29 | .coverage 30 | .tox 31 | nosetests.xml 32 | 33 | # Translations 34 | *.mo 35 | 36 | # Mr Developer 37 | .mr.developer.cfg 38 | .project 39 | .pydevproject 40 | 41 | #emacs files 42 | \#*\# 43 | *~ 44 | 45 | #Mac Files 46 | *.DS_Store -------------------------------------------------------------------------------- /ForcePy/Analysis.py: -------------------------------------------------------------------------------- 1 | from ForceCategories import Pairwise, Bond, Angle, Dihedral 2 | from States import State_Mask 3 | from MDAnalysis import Universe 4 | import numpy as np 5 | from math import floor, pi 6 | 7 | 8 | class Analysis(object): 9 | """ An abstract class that is used for analysis classes. For 10 | example, a radial distribution function calculation. The 11 | methods in the class are for specializing the analysis to 12 | certain types/states and interfacing with neighborlists for 13 | pairs or bonds. 14 | """ 15 | def __init__(self, category, period, outfile, cutoff=None): 16 | self.category = category.get_instance(cutoff) 17 | self.period = period 18 | self.outfile = outfile 19 | self.update_counts = 0 20 | self.sel1 = None 21 | self.sel2 = None 22 | self.mask1 = None 23 | self.mask2 = None 24 | 25 | def get_category(self): 26 | return self.category 27 | 28 | def update(self, u): 29 | if(self.update_counts % self.period == 0): 30 | self.do_update(u) 31 | self.update_counts += 1 32 | 33 | #In case the force needs access to the universe for setting up, override (and call this method). 34 | def setup_hook(self, u): 35 | try: 36 | self._build_mask(self.sel1, self.sel2, u) 37 | except AttributeError: 38 | pass #some forces don't have selections, ie FileForce 39 | 40 | def specialize_types(self, selection_pair_1 = None, selection_pair_2 = None): 41 | self.sel1 = selection_pair_1 42 | self.sel2 = selection_pair_2 43 | self.type_name = "[%s] -- [%s]" % (selection_pair_1, selection_pair_2) 44 | 45 | def specialize_states(self, mask1, mask2, name1 = None, name2 = None): 46 | self.mask1 = mask1 47 | self.mask2 = mask2 48 | self.type_name = "[state %s] -- [state %s]" % (name1, name2) 49 | 50 | 51 | def _build_mask(self, sel1, sel2, u): 52 | if(self.mask1 is None and sel1 is not None): 53 | self.mask1 = [False for x in range(u.atoms.numberOfAtoms())] 54 | for a in u.selectAtoms('type %s' % sel1): 55 | self.mask1[a.number] = True 56 | elif(self.mask1 is None): 57 | self.mask1 = [True for x in range(u.atoms.numberOfAtoms())] 58 | 59 | 60 | if(self.mask2 is None and sel2 is not None): 61 | self.mask2 = [False for x in range(u.atoms.numberOfAtoms())] 62 | for a in u.selectAtoms('type %s' % sel2): 63 | self.mask2[a.number] = True 64 | elif(self.mask2 is None): 65 | self.mask2 = self.mask1 66 | 67 | def valid_pair(self, atom1, atom2): 68 | """Checks the two atoms' types to see if they match the type 69 | specialization. If no type selections are set, returns true 70 | """ 71 | #Don't use the selection class since it's a little heavy for this 72 | import re 73 | if(type(atom1) != type("")): 74 | atom1 = atom1.type 75 | if(type(atom2) != type("")): 76 | atom2 = atom2.type 77 | 78 | try: 79 | if(re.match(self.sel1, atom1) is not None and re.match(self.sel2, atom2) is not None): 80 | return True 81 | if(re.match(self.sel2, atom1) is not None and re.match(self.sel1, atom2) is not None): 82 | return True 83 | except AttributeError: 84 | return True 85 | 86 | return False 87 | 88 | 89 | class RDF(Analysis): 90 | def __init__(self, category, period, outfile, binsize=0.1, cutoff=None): 91 | super(RDF, self).__init__(category, period, outfile, cutoff) 92 | self.hist = [0 for x in np.arange(0,cutoff, binsize)] 93 | self.binsize = binsize 94 | try: 95 | self.cutoff = self.category.cutoff 96 | except AttributeError: 97 | raise ValueError('Must pass cutoff for category type {}'.format(category)) 98 | 99 | 100 | def do_update(self, u): 101 | for i in range(u.atoms.numberOfAtoms()): 102 | #check to if this is a valid type 103 | if(self.mask1[i]): 104 | maskj = self.mask2 105 | elif(self.mask2[i]): 106 | maskj = self.mask1 107 | else: 108 | continue 109 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 110 | if(i < j): 111 | b = int(floor(d / self.binsize)) 112 | if(b < len(self.hist)): 113 | self.hist[b] += 1 114 | 115 | def __getstate__(self): 116 | odict = self.__dict__.copy() 117 | #close and delete output file 118 | if(type(self.outfile) == file): 119 | self.outfile.close() 120 | self.outfile = self.outfile.name 121 | del odict['outfile'] 122 | return odict 123 | 124 | def write(self): 125 | 126 | if(type(self.outfile) != file): 127 | self.outfile = open(self.outfile, 'w') 128 | 129 | self.outfile.seek(0) 130 | 131 | N = sum(self.hist) 132 | density = N / (4/3. * pi * self.cutoff ** 3) 133 | 134 | for rl,rr,h in zip(np.arange(0,self.cutoff, self.binsize), 135 | np.arange(self.binsize,self.cutoff + self.binsize, self.binsize), 136 | self.hist): 137 | r = 0.5 * (rl + rr) 138 | gr = (3 * h / (density * 4 * pi * (rr**3 - rl**3))) 139 | self.outfile.write('{:10} {:10}\n'.format(r, gr)) 140 | 141 | 142 | class CoordNumber(Analysis): 143 | def __init__(self, category, period, outfile, r0, cutoff): 144 | super(CoordNumber, self).__init__(category, period, outfile, cutoff) 145 | self.cutoff = cutoff 146 | self.r0 = r0 147 | 148 | 149 | def do_update(self, u): 150 | self.cn = 0 151 | count = 0 152 | for i in range(u.atoms.numberOfAtoms()): 153 | #check to if this is a valid type 154 | if(self.mask1[i]): 155 | maskj = self.mask2 156 | elif(self.mask2[i]): 157 | maskj = self.mask1 158 | else: 159 | continue 160 | count += 1 161 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 162 | if(i < j and d < self.r0): 163 | self.cn += 1 164 | self.cn /= float(count) 165 | #since we avoided double counting 166 | self.cn *= 2 167 | 168 | 169 | def write(self): 170 | 171 | if(type(self.outfile) != file): 172 | self.outfile = open(self.outfile, 'w') 173 | 174 | self.outfile.write('{:10}\n'.format(self.cn)) 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /ForcePy/Basis.pyx: -------------------------------------------------------------------------------- 1 | # cython: profile=False 2 | #filename Basis.pyx 3 | 4 | """ 5 | force: returns force 6 | force_and_grad: puts the forces in cache, the Nx3 grad in the grad vector. Zeros cache, doesn't modify grad, returns magnitude of pair-wise distnace vector 7 | 8 | """ 9 | 10 | 11 | 12 | import numpy as np 13 | cimport numpy as np 14 | from .Mesh import * 15 | import cython 16 | from libc.math cimport sqrt, pow , floor, ceil, exp, erf, acos 17 | 18 | FTYPE = np.float32 19 | ctypedef np.float32_t FTYPE_t 20 | 21 | cdef FTYPE_t pi = 3.14159265 22 | 23 | class UnitStep(object): 24 | 25 | @staticmethod 26 | def force(FTYPE_t x, mesh): 27 | result = np.zeros(len(mesh), dtype=FTYPE) 28 | result[mesh.mesh_index(x)] = 1 29 | return result 30 | 31 | @staticmethod 32 | def force_cache(FTYPE_t x, np.ndarray[FTYPE_t, ndim=1] cache, mesh): 33 | cache.fill(0) 34 | i = mesh.mesh_index(x) 35 | assert i < len(mesh), "Attempted calculation outside of mesh. {} is beyond [{},{}]. Index = {} ( > {})".format(x, mesh.min(), mesh.max(), i, len(mesh)) 36 | cache[mesh.mesh_index(x)] = 1 37 | 38 | @staticmethod 39 | def potential(FTYPE_t x, mesh): 40 | cdef int i, lm 41 | lm = len(mesh) 42 | result = np.zeros(lm, dtype=FTYPE) 43 | mesh_point = mesh.mesh_index(x) 44 | for i in range(lm - 1, mesh_point - 1, -1): 45 | result[i] = (mesh[i + 1] - mesh[i]) 46 | return result 47 | 48 | 49 | cdef class Quartic(object): 50 | 51 | #The number of non-zero neighbor bins which must be evaluated 52 | cdef int basis_n 53 | #inverse of the width, needed for scaling 54 | cdef FTYPE_t inv_width 55 | 56 | def __init__(self, mesh = None, width = None, *pickle_args): 57 | """ Construct a Quartic basis. The mesh must be given so that 58 | the Quartice mesh can optimize its layout on the mesh. The 59 | width should be from the left edge to right edge of the basis 60 | """ 61 | 62 | #only works on uniform mesh 63 | if(mesh): 64 | assert type(mesh) is UniformMesh, "Quartic basis only works on uniform mesh currently and not %s" % type(mesh) 65 | 66 | self.basis_n = 0 67 | 68 | if(width is None or width < mesh.dx * 1.5): 69 | width = mesh.dx * 1.5 70 | 71 | #count neighbor non-zero bins in addition to main bin 72 | self.basis_n = ceil(width / mesh.dx) 73 | 74 | self.inv_width = (2. / width) 75 | #check for pickling 76 | else: 77 | self.basis_n = pickle_args[0] 78 | self.inv_width = pickle_args[1] 79 | 80 | 81 | cdef inline FTYPE_t _basis(self, FTYPE_t x, FTYPE_t left_edge): 82 | #Assumes we're given the left edge, instead of center, hence -1 83 | x = self.inv_width * (x - left_edge) - 1 84 | if(abs(x) >= 1): 85 | return 0 86 | return (15. / 16.) * (1. - x * x) * (1. - x * x) 87 | 88 | cdef inline FTYPE_t _int_basis(self, FTYPE_t x, FTYPE_t left_edge): 89 | #Assumes we're given the left edge, instead of center, hence -1 90 | x = self.inv_width * (x - left_edge) - 1 91 | if(x < -1): 92 | return 1 93 | elif(x > 1): 94 | return 0 95 | return -1. / 16. * (x - 1)**3 * (3 * x**2 + 9 * x + 8) 96 | 97 | def force(self, FTYPE_t x, mesh): 98 | result = np.zeros(len(mesh), dtype=FTYPE) 99 | self.force_cache(x, result, mesh) 100 | return result 101 | 102 | cpdef force_cache(self, FTYPE_t x, np.ndarray[FTYPE_t, ndim=1] cache, mesh): 103 | cache.fill(0) 104 | cdef int index = mesh.mesh_index(x) 105 | 106 | cache[index] = self._basis(x, mesh.cgetitem(index)) 107 | 108 | cdef int i 109 | #upwards on mesh 110 | for i in range(index + 1, min(len(mesh), index + self.basis_n + 1)): 111 | cache[i] = self._basis(x, mesh.cgetitem(i)) 112 | #downwards on mesh 113 | for i in range(index - 1, max(-1, index - self.basis_n - 1), -1): 114 | cache[i] = self._basis(x, mesh.cgetitem(i)) 115 | 116 | 117 | cpdef np.ndarray[FTYPE_t, ndim=1] potential(self, FTYPE_t x, mesh): 118 | cdef int i, lm, maxb 119 | lm = len(mesh) 120 | 121 | result = np.zeros(lm, dtype=FTYPE) 122 | mesh_point = mesh.mesh_index(x) 123 | maxb = min(lm - 1, mesh_point + self.basis_n) # the point at which we must evaluate numerically 124 | 125 | for i in range(lm - 1, maxb, -1): 126 | result[i] = mesh.dx 127 | for i in range(maxb, max(-1, mesh_point - self.basis_n - 1), -1): 128 | result[i] = mesh.dx * self._int_basis(x, mesh.cgetitem(i)) 129 | return result 130 | 131 | def __reduce__(self): 132 | return Quartic, (None, None, self.basis_n, self.inv_width) 133 | 134 | 135 | 136 | 137 | cdef class Gaussian(object): 138 | 139 | #The number of non-zero neighbor bins which must be evaluated 140 | cdef int basis_n 141 | #gaussian sigma inverse 142 | cdef FTYPE_t inv_sigma 143 | 144 | def __init__(self, mesh = None, sigma = None, *pickle_args): 145 | """ Construct a Guassian basis. The mesh must be given so 146 | that the Gaussian mesh can optimize its layout on the 147 | mesh. 148 | """ 149 | 150 | #only works on uniform mesh 151 | if(mesh): 152 | assert type(mesh) is UniformMesh, "Gaussian basis only works on uniform mesh currently and not %s" % type(mesh) 153 | 154 | self.basis_n = 0 155 | 156 | if(sigma is None or sigma < mesh.dx * 1.5): 157 | sigma = mesh.dx * 1.5 158 | 159 | #count neighbor non-zero bins in addition to main bin 160 | self.basis_n = ceil(4 * sigma / mesh.dx) 161 | 162 | self.inv_sigma = (1. / sigma) 163 | #check for pickling 164 | else: 165 | self.basis_n = pickle_args[0] 166 | self.inv_sigma = pickle_args[1] 167 | 168 | 169 | cdef inline FTYPE_t _basis(self, FTYPE_t x, FTYPE_t left_edge): 170 | 171 | x = self.inv_sigma * (x - left_edge) - 0.5 172 | if(abs(x) >= 3.5): 173 | return 0 174 | return 1 / sqrt(2 * pi) * exp(-0.5 * x ** 2) 175 | 176 | cdef inline FTYPE_t _int_basis(self, FTYPE_t x, FTYPE_t left_edge): 177 | 178 | x = self.inv_sigma * (x - left_edge) - 0.5 179 | if(x < -3.5): 180 | return 1 181 | elif(x > 3.5): 182 | return 0 183 | return 1 - (0.5 + 0.5 * erf(x / sqrt(2.))) 184 | 185 | def force(self, FTYPE_t x, mesh): 186 | result = np.zeros(len(mesh), dtype=FTYPE) 187 | self.force_cache(x, result, mesh) 188 | return result 189 | 190 | cpdef force_cache(self, FTYPE_t x, np.ndarray[FTYPE_t, ndim=1] cache, mesh): 191 | cache.fill(0) 192 | cdef int index = mesh.mesh_index(x) 193 | 194 | cache[index] = self._basis(x, mesh.cgetitem(index)) 195 | 196 | cdef int i 197 | #upwards on mesh 198 | for i in range(index + 1, min(len(mesh), index + self.basis_n + 1)): 199 | cache[i] = self._basis(x, mesh.cgetitem(i)) 200 | #downwards on mesh 201 | for i in range(index - 1, max(-1, index - self.basis_n - 1), -1): 202 | cache[i] = self._basis(x, mesh.cgetitem(i)) 203 | 204 | 205 | cpdef np.ndarray[FTYPE_t, ndim=1] potential(self, FTYPE_t x, mesh): 206 | cdef int i, lm, maxb 207 | lm = len(mesh) 208 | 209 | result = np.zeros(lm, dtype=FTYPE) 210 | mesh_point = mesh.mesh_index(x) 211 | maxb = min(lm - 1, mesh_point + self.basis_n) # the point at which we must evaluate numerically 212 | 213 | for i in range(lm - 1, maxb, -1): 214 | result[i] = mesh.dx 215 | for i in range(maxb, max(-1, mesh_point - self.basis_n - 1), -1): 216 | result[i] = mesh.dx * self._int_basis(x, mesh.cgetitem(i)) 217 | return result 218 | 219 | def __reduce__(self): 220 | return Gaussian, (None, None, self.basis_n, self.inv_sigma) 221 | 222 | -------------------------------------------------------------------------------- /ForcePy/ForceCategories.py: -------------------------------------------------------------------------------- 1 | from ForcePy.NeighborList import NeighborList 2 | import numpy as np 3 | from ForcePy.Util import norm3, min_img_vec 4 | 5 | class ForceCategory(object): 6 | """A category of force/potential type. 7 | 8 | The forces used in force matching are broken into categories, where 9 | the sum of each category of forces is what's matched in the force 10 | matching code. Examples of categories are pairwise forces, 11 | threebody forces, topology forces (bonds, angles, etc). 12 | """ 13 | 14 | def __init__(self): 15 | self.nlist_ready = False 16 | 17 | def _setup(self, u): 18 | pass 19 | 20 | def _teardown(self): 21 | pass 22 | 23 | def generate_nlist(self, i): 24 | assert self.nlist_ready, "Neighbor list not built yet" 25 | nlist_accum = np.sum(self.nlist_lengths[:i]) if i > 0 else 0 26 | for j in self.nlist[nlist_accum:(nlist_accum + self.nlist_lengths[i])]: 27 | yield j 28 | 29 | def generate_neighbor_vecs(self, i, u, mask = None): 30 | positions = u.atoms.get_positions() 31 | dims = u.trajectory.ts.dimensions 32 | 33 | for j in self.generate_nlist(i): 34 | if(mask and not mask[j]): 35 | continue 36 | r = min_img_vec(positions[j], positions[i], dims, u.trajectory.periodic) 37 | d = norm3(r) 38 | #We do allow overlap here for cases when variable particle numbers 39 | #are dealt with by creating overlapping particles 40 | r = r if d == 0 else r / d 41 | yield (r,d,j) 42 | 43 | 44 | 45 | class Angle(ForceCategory): 46 | pass 47 | 48 | class Dihedral(ForceCategory): 49 | pass 50 | 51 | class Improper(ForceCategory): 52 | pass 53 | 54 | class Global(ForceCategory): 55 | """Creates a global vector that acts on all particles. Calling generate neighbor vecs returns the outer and inner products""" 56 | 57 | _vector = np.array([0,0,0], dtype='f') 58 | 59 | def get_instance(self, *args): 60 | #we will never be static 61 | return self 62 | 63 | def __init__(self, vector): 64 | super(Global, self).__init__() 65 | self._vector = np.array(vector) 66 | self.nlist_ready = True 67 | 68 | def generate_neighbor_vecs(self, i, u, mask = None): 69 | positions = u.atoms.get_positions() 70 | dims = u.trajectory.ts.dimensions 71 | 72 | r = np.cross(self._vector, positions[i]) 73 | d = np.inner(self._vector, positions[i]) 74 | yield (r,d,-1) 75 | 76 | @property 77 | def __name__(self): 78 | return 'Global {} {} {}'.format(self._vector[0], self._vector[1], self._vector[2]) 79 | 80 | def pair_exists(self, u, type1, type2): 81 | return False 82 | 83 | 84 | 85 | class Pairwise(ForceCategory): 86 | """Pairwise force category. It handles constructing a neighbor-list at each time-step. 87 | """ 88 | instance = None 89 | 90 | @staticmethod 91 | def get_instance(*args): 92 | 93 | if(len(args) == 0 or args[0] is None): 94 | #doesn't care about cutoff 95 | return Pairwise.instance 96 | 97 | if(Pairwise.instance is None): 98 | Pairwise.instance = Pairwise(args[0]) 99 | else: 100 | #check cutoff 101 | if(Pairwise.instance.cutoff - args[0] < 0): 102 | raise RuntimeError("Incompatible cutoffs: Already set to %g, not %g" % (Pairwise.instance.cutoff,args[0])) 103 | return Pairwise.instance 104 | 105 | def __init__(self, cutoff=12): 106 | super(Pairwise, self).__init__() 107 | self.cutoff = cutoff 108 | self.forces = [] 109 | self.nlist_obj = None 110 | 111 | def _build_nlist(self, u): 112 | if(self.nlist_obj is None): 113 | self.nlist_obj = NeighborList(u, self.cutoff) 114 | self.nlist, self.nlist_lengths = self.nlist_obj.build_nlist(u) 115 | 116 | self.nlist_ready = True 117 | 118 | def _setup(self, u): 119 | if(not self.nlist_ready): 120 | self._build_nlist(u) 121 | 122 | def _teardown(self): 123 | self.nlist_ready = False 124 | 125 | def pair_exists(self, u, type1, type2): 126 | return True 127 | 128 | def __reduce__(self): 129 | return Pairwise, (self.cutoff,) 130 | 131 | class Bond(ForceCategory): 132 | 133 | """Bond category. It caches each atoms bonded neighbors when constructued 134 | """ 135 | instance = None 136 | 137 | @staticmethod 138 | def get_instance(*args): 139 | if(Bond.instance is None): 140 | Bond.instance = Bond() 141 | return Bond.instance 142 | 143 | def __init__(self): 144 | super(Bond, self).__init__() 145 | 146 | 147 | def _build_nlist(self, u): 148 | temp = [[] for x in range(u.atoms.numberOfAtoms())] 149 | #could be at most everything bonded with everything 150 | self.nlist = np.empty((u.atoms.numberOfAtoms() - 1) * (u.atoms.numberOfAtoms() / 2), dtype=np.int32) 151 | self.nlist_lengths = np.empty(u.atoms.numberOfAtoms(), dtype=np.int32) 152 | nlist_accum = 0 153 | for b in u.bonds: 154 | temp[b[0].number].append(b[1].number) 155 | temp[b[1].number].append(b[0].number) 156 | 157 | #unwrap the bond list to make it look like neighbor lists 158 | for i,bl in zip(range(u.atoms.numberOfAtoms()), temp): 159 | self.nlist_lengths[i] = len(temp[i]) 160 | for b in bl: 161 | self.nlist[nlist_accum] = b 162 | nlist_accum += 1 163 | 164 | #resize now we know how many bond items there are 165 | self.nlist = self.nlist[:nlist_accum] 166 | self.nlist_ready = True 167 | 168 | def _setup(self, u): 169 | if(not self.nlist_ready): 170 | self._build_nlist(u) 171 | 172 | def _teardown(self): 173 | self.nlist_ready = False 174 | 175 | def pair_exists(self, u, type1, type2): 176 | """Check to see if a there exist any pairs of the two types given 177 | """ 178 | if(not self.nlist_ready): 179 | self._build_nlist(u) 180 | 181 | sel2 = u.atoms.selectAtoms(type2) 182 | for a in u.atoms.selectAtoms(type1): 183 | i = a.number 184 | nlist_accum = np.sum(self.nlist_lengths[:i]) if i > 0 else 0 185 | for j in self.nlist[nlist_accum:(nlist_accum + self.nlist_lengths[i])]: 186 | if(u.atoms[int(j)] in sel2): 187 | return True 188 | 189 | return False 190 | 191 | -------------------------------------------------------------------------------- /ForcePy/ForceMatch.py: -------------------------------------------------------------------------------- 1 | import random, os, json 2 | import numpy as np 3 | import numpy.linalg as ln 4 | from math import ceil 5 | from MDAnalysis import Universe 6 | from math import * 7 | import ForceCategories as ForceCategories 8 | try: 9 | import matplotlib.pyplot as plt 10 | plotting_support = True 11 | except ImportError as e: 12 | plotting_support = False 13 | plotting_error = e 14 | 15 | from ForcePy.Util import * 16 | from ForcePy.ForceCategories import * 17 | from ForcePy.Analysis import * 18 | from ForcePy.CGMap import CGUniverse, apply_mass_map, create_mass_map, write_lammps_data 19 | try: 20 | from mpi4py import MPI 21 | mpi_support = True 22 | except ImportError as e: 23 | mpi_support = False 24 | mpi_error = e 25 | 26 | 27 | class ForceMatch: 28 | """Main force match class. 29 | """ 30 | 31 | def __init__(self, cguniverse, input_json = None): 32 | self.ref_cats = [] 33 | self.tar_cats = [] 34 | self.ref_forces = [] 35 | self.analysis = [] 36 | self.tar_forces = [] 37 | self.u = cguniverse 38 | if(input_json): 39 | self._load_json(input_json) 40 | else: 41 | self.json = [] 42 | self.force_match_calls = 0 43 | self.plot_frequency = 1 if plotting_support else -1 44 | self.plot_output = None 45 | self.atom_type_map = None 46 | self.tar_force_buffer = None 47 | self.send_buffer = None 48 | self.rec_buffer = None 49 | 50 | 51 | 52 | def _load_json(self, input_file): 53 | with open(input_file, 'r') as f: 54 | self.json = json.load(f) 55 | self._test_json(self.json, []) 56 | 57 | if('box' in self.json): 58 | if(len(self.json['box']) != 3): 59 | raise IOError('Input file JSON: box must look like \"box\":[5,5,5]. It must have 3 dimensions in an array') 60 | 61 | 62 | 63 | def _test_json(self, json, required_keys = [("kT", "Boltzmann's constant times temperature")]): 64 | for rk in required_keys: 65 | if(not json.has_key(rk[0])): 66 | raise IOError("Error in input file, could not find %s\n. Set using %s keyword" % (rk[1], rk[0])) 67 | 68 | def __getstate__(self): 69 | odict = self.__dict__.copy() 70 | #remove universe, we don't want to serialize all that 71 | del odict['u'] 72 | 73 | if(type(self.u ) == CGUniverse): 74 | self.u = self.u.cache() 75 | 76 | #store the filename and trajectory of the universe 77 | odict['structure_filename'] = self.u.filename 78 | odict['trajectory_filename'] = self.u.trajectory.filename 79 | odict['mass_map'] = create_mass_map(self.u) 80 | odict['trajectory_is_periodic'] = self.u.trajectory.periodic 81 | 82 | return odict 83 | 84 | def __setstate__(self, dict): 85 | self.__dict__.update(dict) 86 | #reconstruct the universe 87 | self.u = Universe(dict['structure_filename'], dict['trajectory_filename']) 88 | apply_mass_map(self.u, dict['mass_map']) 89 | self.u.trajectory.periodic = dict['trajectory_is_periodic'] 90 | for f in self.tar_forces + self.ref_forces: 91 | cat = f.get_category() 92 | if(not (cat is None)): 93 | self.ref_cats.append(cat) 94 | f.setup_hook(self.u) 95 | 96 | 97 | 98 | 99 | def add_tar_force(self, *forces): 100 | for f in forces: 101 | self.tar_forces.append(f) 102 | cat = f.get_category() 103 | if(not (cat is None)): 104 | self.tar_cats.append(cat) 105 | f.setup_hook(self.u) 106 | 107 | def add_ref_force(self, *forces): 108 | for f in forces: 109 | self.ref_forces.append(f) 110 | cat = f.get_category() 111 | if(not (cat is None)): 112 | self.ref_cats.append(cat) 113 | f.setup_hook(self.u) 114 | 115 | def add_analysis(self, *analysis): 116 | for a in analysis: 117 | self.analysis.append(a) 118 | cat = a.get_category() 119 | if(cat is not None): 120 | self.ref_cats.append(cat) 121 | a.setup_hook(self.u) 122 | 123 | 124 | 125 | def output_energies_mpi(self, outfile): 126 | '''This is for assesing the energy coming out of the force-matching 127 | ''' 128 | 129 | comm = MPI.COMM_WORLD 130 | rank = comm.Get_rank() 131 | size = comm.Get_size() 132 | 133 | #distribute the jobs evenly 134 | batch = range(0,len(self.u.trajectory) + 1, int(floor(float(len(self.u.trajectory)) / size))) 135 | batch[-1] = len(self.u.trajectory) 136 | 137 | self.u.trajectory.rewind() 138 | 139 | for i in range(batch[rank]): 140 | self.u.trajectory.next() 141 | 142 | #build buffer 143 | if(self.send_buffer is None or len(self.send_buffer) != (batch[rank + 1] - batch[rank])): 144 | self.send_buffer = np.empty(batch[rank + 1] - batch[rank], dtype=np.float32) 145 | 146 | for i in range(batch[rank], batch[rank + 1]): 147 | self._setup() 148 | energy = 0 149 | for f in self.tar_forces: 150 | energy += f.calc_potentials(self.u) 151 | self.send_buffer[i - batch[rank]] = energy 152 | self._teardown() 153 | try: 154 | self.u.trajectory.next() 155 | except (EOFError, IOError): 156 | #finished reading the file 157 | break 158 | 159 | 160 | rec_buffer = comm.gather([self.send_buffer, MPI.FLOAT]) 161 | 162 | if rank == 0: 163 | with open(outfile, 'w') as f: 164 | f.write('{:<16} {:<16}\n'.format('frame', 'pot')) 165 | i = 0 166 | for ebatch in rec_buffer: 167 | for e in ebatch[0]: 168 | if(i == len(self.u.trajectory)): 169 | break 170 | f.write('{:<16} {:<16}\n'.format(i+1,e)) 171 | i += 1 172 | 173 | #clear buffers 174 | self.send_buffer = self.rec_buffer = None 175 | 176 | 177 | 178 | def force_match_mpi(self, batch_size = None, do_plots = False, repeats = 1, frame_number=0, quiet=False): 179 | 180 | if(not mpi_support): 181 | raise mpi_error 182 | 183 | comm = MPI.COMM_WORLD 184 | size = comm.Get_size() 185 | rank = comm.Get_rank() 186 | 187 | if(size == 1): 188 | raise RuntimeError("MPI should not be run on a single process. Call force_match instead") 189 | 190 | if(do_plots and rank == 0): 191 | self._setup_plot() 192 | 193 | frame_number = frame_number if frame_number > 0 else len(self.u.trajectory) 194 | 195 | 196 | if(batch_size): 197 | index = 0 198 | while(index * size * batch_size < frame_number * repeats): 199 | try: 200 | self._distribute_fm_tasks(batch_size, index * batch_size, quiet=quiet, frame_number=frame_number) 201 | except (EOFError, IOError): 202 | #just finished reading the file, eat the exception. Will be rewound in force_match_task 203 | pass 204 | 205 | self._reduce_fm_tasks() 206 | index +=1 207 | if(rank == 0 and not quiet): 208 | print "%d / %d iterations" % (index * size * batch_size, frame_number * repeats) 209 | if(do_plots): 210 | self._plot_forces() 211 | 212 | else: 213 | for i in range(repeats): 214 | try: 215 | self._distribute_fm_tasks(quiet=quiet, frame_number=frame_number) 216 | except (EOFError, IOError): 217 | #just finished reading the file, eat the exception. Will be rewound in force_match_task 218 | pass 219 | 220 | self._reduce_fm_tasks() 221 | if(rank == 0 and not quiet): 222 | print "%d / %d iterations" % (i+1, repeats) 223 | if(do_plots): 224 | self._plot_forces() 225 | 226 | if(rank == 0): 227 | if(do_plots): 228 | if(not self.plot_output is None): 229 | self._save_plot() 230 | self._teardown_plot() 231 | 232 | 233 | 234 | def force_match(self, iterations = 0): 235 | 236 | 237 | if(iterations == 0): 238 | iterations = len(self.u.trajectory) 239 | 240 | ref_forces = np.zeros( (len(self.u.atoms), 3) ) 241 | self.u.trajectory.rewind() # just in case this is called after some analysis has been done 242 | 243 | #setup plots 244 | if(self.plot_frequency != -1): 245 | self._setup_plot() 246 | 247 | 248 | for ts in self.u.trajectory: 249 | 250 | #set box if necessary 251 | if("box" in self.json): 252 | #strange ordering due to charm in previous MDAnalysis versions was 0,2,5 253 | self.u.trajectory.ts.dimensions = self.json["box"] + [90, 90 ,90] 254 | 255 | self._setup() 256 | 257 | for rf in self.ref_forces: 258 | rf.calc_forces(ref_forces, self.u) 259 | 260 | #make plots 261 | if(self.plot_frequency != -1 and iterations % self.plot_frequency == 0): 262 | self._plot_forces() 263 | 264 | #track error 265 | net_df = 0 266 | self.force_match_calls += 1 267 | 268 | #sample particles and run updates on them 269 | for i in random.sample(range(len(self.u.atoms)),len(self.u.atoms)): 270 | 271 | #calculate net forces deviation 272 | df = np.array(ref_forces[i], dtype=np.float32) 273 | mag_temp = ln.norm(df) 274 | for f in self.tar_forces: 275 | df -= f.calc_particle_force(i,self.u) 276 | net_df += ln.norm(df) / mag_temp 277 | 278 | #now run gradient update step on all the force types 279 | for f in self.tar_forces: 280 | f.update(df) 281 | f.update_avg() 282 | 283 | ref_forces.fill(0) 284 | self._teardown() 285 | 286 | print "avg relative magnitude error at %d = %g" % (iterations, net_df / len(self.u.atoms)) 287 | 288 | iterations -= 1 289 | if(iterations == 0): 290 | break 291 | 292 | if(not self.plot_output is None): 293 | self._save_plot() 294 | if(self.plot_frequency != -1): 295 | self._teardown_plot() 296 | 297 | 298 | def finalize(self): 299 | '''Call this method before writing to use the average over the 300 | force-matching, instead of the last observed weights. This 301 | is important when convergence is oscillatory or observation 302 | matching is being used. Some forces also do things at the 303 | end like fill in unobserved points with repulsion 304 | 305 | ''' 306 | for f in self.tar_forces: 307 | f.finalize_hook(self) 308 | 309 | def _force_match_task(self, start, end, do_print = False): 310 | ref_forces = np.zeros( (len(self.u.atoms), 3) ) 311 | 312 | 313 | self.u.trajectory.rewind() 314 | for i in range(start): 315 | self.u.trajectory.next() 316 | 317 | for tsi in range(start,end): 318 | ts = self.u.trajectory.ts 319 | 320 | #set box if necessary 321 | if("box" in self.json): 322 | #strange ordering due to charm in previous MDAnalysis versions was 0,2,5 323 | self.u.trajectory.ts.dimensions = self.json["box"] + [90, 90 ,90] 324 | 325 | self._setup() 326 | 327 | for rf in self.ref_forces: 328 | rf.calc_forces(ref_forces, self.u) 329 | 330 | #track error 331 | net_df = 0 332 | self.force_match_calls += 1 333 | 334 | #sample particles and run updates on them 335 | for i in random.sample(range(len(self.u.atoms)),len(self.u.atoms)): 336 | #calculate net forces deviation 337 | df = np.array(ref_forces[i], dtype=np.float32) 338 | mag_temp = ln.norm(df) 339 | for f in self.tar_forces: 340 | df -= f.calc_particle_force(i,self.u) 341 | net_df += ln.norm(df) / mag_temp 342 | 343 | #now run gradient update step on all the force types 344 | for f in self.tar_forces: 345 | f.update(df) 346 | 347 | ref_forces.fill(0) 348 | self._teardown() 349 | 350 | if(do_print): 351 | print "avg relative magnitude error = %g" % (net_df / len(self.u.atoms)) 352 | 353 | 354 | if(tsi != end - 1): 355 | self.u.trajectory.next() 356 | 357 | 358 | 359 | def _pack_tar_forces(self): 360 | if(self.send_buffer is None): 361 | count = 0 362 | for f in self.tar_forces: 363 | count += len(f.w) 364 | self.send_buffer = np.empty((count,), dtype=np.float32) 365 | 366 | index = 0 367 | for f in self.tar_forces: 368 | self.send_buffer[index:(index + len(f.w))] = f.w[:] 369 | index += len(f.w) 370 | 371 | return self.send_buffer 372 | 373 | def _unpack_tar_forces(self): 374 | index = 0 375 | for f in self.tar_forces: 376 | f.w[:] = self.rec_buffer[index:(index + len(f.w))] 377 | index += len(f.w) 378 | f.update_avg() 379 | 380 | def _reduce_fm_tasks(self): 381 | 382 | self._pack_tar_forces() 383 | 384 | comm = MPI.COMM_WORLD 385 | rank = comm.Get_rank() 386 | 387 | #sum all of em 388 | if(self.rec_buffer is None): 389 | self.rec_buffer = np.copy(self.send_buffer) 390 | 391 | comm.Reduce([self.send_buffer, MPI.FLOAT], [self.rec_buffer, MPI.FLOAT]) 392 | 393 | #average 394 | if rank == 0: 395 | self.rec_buffer /= comm.Get_size() 396 | 397 | self.rec_buffer = comm.bcast(self.rec_buffer) #broadcast the average 398 | 399 | self._unpack_tar_forces() 400 | 401 | def _distribute_fm_tasks(self, batch_size = None, offset = 0, quiet=False, frame_number = 0): 402 | comm = MPI.COMM_WORLD 403 | size = comm.Get_size() 404 | rank = comm.Get_rank() 405 | 406 | frame_number = frame_number if frame_number > 0 else len(self.u.trajectory) 407 | span = frame_number / size 408 | 409 | #get remainder 410 | spanr = frame_number - size * span 411 | 412 | if(batch_size): 413 | #use batch size 414 | self._force_match_task(spanr / 2 + rank * span + offset, spanr / 2 + rank * span + batch_size + offset, rank == 0 and not quiet) 415 | else: 416 | #distribute equally on the trajectory 417 | if(rank < spanr): 418 | self._force_match_task(rank * (span + 1), (rank + 1) * (span + 1), rank == 0 and not quiet) 419 | else: 420 | self._force_match_task(rank * span + spanr, (rank + 1) * span + spanr, rank == 0 and not quiet) 421 | 422 | 423 | def _teardown_plot(self): 424 | plt.close() 425 | for f in self.tar_forces: 426 | f.teardown_plot() 427 | 428 | def _setup_plot(self, plot_fig=None): 429 | 430 | if(not plotting_support): 431 | raise plotting_error 432 | 433 | if(plot_fig is None): 434 | plot_fig = plt.figure() 435 | 436 | 437 | #try to maximize the window 438 | mng = plt.get_current_fig_manager() 439 | try: 440 | mng.frame.Maximize(True) 441 | except AttributeError: 442 | try: 443 | mng.resize(*mng.window.maxsize()) 444 | except AttributeError: 445 | #mac os x 446 | mng.resize(1200, 900) 447 | 448 | 449 | 450 | if(self.plot_output is None): 451 | plt.ion() 452 | 453 | #set-up plots for 16/9 screen 454 | plot_w = ceil(sqrt(len(self.tar_forces)) * 4 / 3.) 455 | plot_h = ceil(plot_w * 9. / 16.) 456 | for i in range(len(self.tar_forces)): 457 | self.tar_forces[i].plot(plt.subplot(plot_w, plot_h, i+1)) 458 | if(self.plot_output is None): 459 | plt.show() 460 | plt.ioff() 461 | 462 | 463 | 464 | 465 | def _plot_forces(self): 466 | for f in self.tar_forces: 467 | f.update_plot() 468 | plt.draw() 469 | 470 | def _save_plot(self): 471 | plt.tight_layout() 472 | #don't fail on saving the plot 473 | try: 474 | plt.savefig(self.plot_output) 475 | except IOError: 476 | print 'failed to save plot' 477 | return 478 | 479 | def add_and_type_states(self, force, state_function, state_names): 480 | if(type(self.u) != CGUniverse): 481 | raise ValueError("Must use CGUniverse for states") 482 | masks = self.u.make_state_mask(state_function, len(state_names)) 483 | for i in range(len(state_names)): 484 | for j in range(i,len(state_names)): 485 | f = force.clone_force() 486 | f.specialize_states(masks[i], 487 | masks[j], 488 | state_names[i], 489 | state_names[j]) 490 | self.add_tar_force(f) 491 | 492 | 493 | def add_and_type_pair(self, force): 494 | types = [] 495 | for a in self.u.atoms: 496 | if(not a.type in types): 497 | types.append(a.type) 498 | for i in range(len(types)): 499 | for j in range(i,len(types)): 500 | if(force.category.pair_exists(self.u, 'type %s' % types[i], 'type %s' % types[j])): 501 | f = force.clone_force() 502 | f.specialize_types(types[i], types[j]) 503 | self.add_tar_force(f) 504 | 505 | def _sample_ts(self): 506 | self.u.trajectory.rewind() 507 | index = random.randint(0,len(self.u.trajectory) - 1) 508 | [self.u.trajectory.next() for x in range(index)] 509 | return index 510 | 511 | 512 | 513 | 514 | def _setup(self): 515 | for rfcat in self.ref_cats: 516 | rfcat._setup(self.u) 517 | for tfcat in self.tar_cats: 518 | tfcat._setup(self.u) 519 | 520 | def _teardown(self): 521 | for a in self.analysis: 522 | a.update(self.u) 523 | a.write() 524 | for rfcat in self.ref_cats: 525 | rfcat._teardown() 526 | for tfcat in self.tar_cats: 527 | tfcat._teardown() 528 | 529 | 530 | 531 | def write(self, folder = os.curdir, table_points=10000, 532 | force_conversion = 1.0, energy_conversion=1, distance_conversion=1, 533 | write_restart=True): 534 | 535 | 536 | if(mpi_support): 537 | #check if we're running with MPI 538 | comm = MPI.COMM_WORLD 539 | rank = comm.Get_rank() 540 | 541 | if(rank != 0): 542 | return 543 | 544 | 545 | if(not os.path.exists(folder)): 546 | os.mkdir(folder) 547 | original_dir = os.path.abspath(os.getcwd()) 548 | os.chdir(folder) 549 | 550 | try: 551 | for rf in self.tar_forces: 552 | with open("{}.txt".format(rf.short_name), 'w') as f: 553 | rf.write_table(f, force_conversion, energy_conversion, distance_conversion, table_points) 554 | if(write_restart): 555 | import pickle 556 | try: 557 | pickle.dump(self, open('restart.pickle', 'wb')) 558 | except pickle.PicklingError as e: 559 | print 'Could not write restart: {}'.format(e) 560 | except (IOError,AttributeError) as e: 561 | print e 562 | finally: 563 | os.chdir(original_dir) 564 | 565 | 566 | 567 | 568 | def write_lammps_tables(self, prefix, force_conv=1, energy_conv=1, dist_conv=1, points=1000): 569 | 570 | if(mpi_support): 571 | #check if we're running with MPI 572 | comm = MPI.COMM_WORLD 573 | rank = comm.Get_rank() 574 | 575 | if(rank != 0): 576 | return 577 | 578 | # print "conversion = %g" % force_conv 579 | 580 | #table file names 581 | table_names = {} 582 | table_names[Pairwise] = open("%s_pair.table" % prefix, 'w') 583 | table_names[Bond] = open("%s_bond.table" % prefix, 'w') 584 | table_names[Angle] = open("%s_angle.table" % prefix, 'w') 585 | table_names[Dihedral] = open("%s_dihedral.table" % prefix, 'w') 586 | 587 | #write the files, one file for each category 588 | for rf in self.tar_forces: 589 | try: 590 | of = table_names[type(rf.category)] 591 | rf.write_lammps_table(of, force_conv, energy_conv, dist_conv,points) 592 | of.write("\n\n") 593 | except KeyError: 594 | print "Don't know how to write a table for {}".format(type(rf.category)) 595 | 596 | for f in table_names: 597 | table_names[f].close() 598 | 599 | 600 | 601 | #generate a snippet of lammps code to load the table 602 | 603 | #pairs 604 | string = ["\npair_style table linear %d\n\n" % points] 605 | 606 | for f in self.tar_forces: 607 | #we try a few times, because not all forces are for 2 types 608 | if(type(f.category) == Pairwise): 609 | try: 610 | string.append("pair_coeff %d %d %s %s %g\n" % (self.get_atom_type_index(f.sel1), 611 | self.get_atom_type_index(f.sel2), 612 | table_names[Pairwise].name, 613 | f.short_name, 614 | f.maxd * dist_conv)) 615 | except AttributeError: 616 | try: 617 | string.append("pair_coeff * %d %s %s %g\n" % (self.get_atom_type_index(f.sel1), 618 | table_names[Pairwise].name, 619 | f.short_name, 620 | f.maxd * dist_conv)) 621 | except AttributeError: 622 | string.append("pair_coeff * * %s %s %g\n" % (table_names[Pairwise].name, 623 | f.short_name, 624 | f.maxd * dist_conv)) 625 | #bonds 626 | index = 0 627 | for f in self.tar_forces: 628 | #we try a few times, because not all forces are for 2 types 629 | if(type(f.category) == Bond): 630 | if(index == 0): 631 | string.append("\nbond_style table linear %d\n\n" % points) 632 | index += 1 633 | string.append("bond_coeff %d %s %s\n" % (index, 634 | table_names[Bond].name, 635 | f.short_name)) 636 | 637 | #Angles 638 | index = 0 639 | for f in self.tar_forces: 640 | #we try a few times, because not all forces are for 2 types 641 | if(type(f.category) == Angle): 642 | if(index == 0): 643 | string.append("\nangle_style table linear %d\n\n" % points) 644 | index += 1 645 | string.append("angle_coeff %d %s %s %d\n" % (index, 646 | table_names[Angle].name, 647 | f.short_name)) 648 | 649 | #Dihedrals 650 | 651 | index = 0 652 | for f in self.tar_forces: 653 | #we try a few times, because not all forces are for 2 types 654 | if(type(f.category) == Dihedral): 655 | if(index == 0): 656 | string.append("\ndihedral_style table linear %d\n\n" % points) 657 | index += 1 658 | string.append("dihedral_coeff %d %s %s %d\n" % (index, 659 | table_names[Dihedral].name, 660 | f.short_name)) 661 | 662 | #Impropers 663 | #no table style in lammps, not sure what to do about this one 664 | return "".join(string) 665 | 666 | 667 | def write_hoomd_tables(self, prefix, force_conv=1, energy_conv=1, dist_conv=1, points=1000): 668 | 669 | if(mpi_support): 670 | #check if we're running with MPI 671 | comm = MPI.COMM_WORLD 672 | rank = comm.Get_rank() 673 | 674 | if(rank != 0): 675 | return 676 | 677 | #table file names 678 | table_names = {} 679 | table_names[Pairwise] = open("%s_pair.table" % prefix, 'w') 680 | table_names[Bond] = open("%s_bond.table" % prefix, 'w') 681 | table_names[Angle] = open("%s_angle.table" % prefix, 'w') 682 | table_names[Dihedral] = open("%s_dihedral.table" % prefix, 'w') 683 | 684 | #write the files, one file for each category 685 | for rf in self.tar_forces: 686 | try: 687 | of = table_names[type(rf.category)] 688 | rf.write_hoomd_table(of, force_conv, energy_conv, dist_conv,points) 689 | except KeyError: 690 | print "Don't know how to write a table for {}".format(type(rf.category)) 691 | 692 | for f in table_names: 693 | table_names[f].close() 694 | 695 | return '' 696 | 697 | 698 | def get_pair_type_index(self, atom1, atom2): 699 | """Return the index of the pairwise force for this pair 700 | """ 701 | return self._get_category_type(Pairwise, atom1, atom2) 702 | 703 | def get_atom_type_index(self, atom_type): 704 | """Return the atom type index for the given type string. Index starts from 1. 705 | """ 706 | if(self.atom_type_map is None): 707 | self.atom_type_map = {} 708 | index = 1 709 | for a in self.u.atoms: 710 | if(not a.type in self.atom_type_map): 711 | self.atom_type_map[a.type] = index 712 | index += 1 713 | #check if this is a one component system first 714 | if(len(self.atom_type_map) == 1): 715 | return 1 716 | if(type(atom_type) != type("")): 717 | assert(type(atom_type) == type(self.u.atoms[0])) 718 | atom_type = atom_type.type 719 | try: 720 | return self.atom_type_map[atom_type] 721 | except KeyError: 722 | pass 723 | return -1 724 | 725 | 726 | def get_bond_type_index(self, atom1, atom2): 727 | """Return the index of the bond force for this pair 728 | """ 729 | return self._get_category_type_index(Bond, atom1, atom2) 730 | 731 | 732 | def _get_category_type_index(self, category, atom1, atom2): 733 | """ Count the number of forces with the given category and return 734 | the index of the force for which atom1 and atom2 are valid. 735 | Indexing starts at 1. 736 | """ 737 | index = 0 738 | for f in self.tar_forces: 739 | if(type(f.category) == category): 740 | index += 1 741 | if f.valid_pair(atom1, atom2): 742 | return index 743 | 744 | def get_force_type_count(self): 745 | """ Returns a dictionary containing the number of types for each force 746 | """ 747 | type_count = {Bond:0, Angle:0, Pairwise:0, Dihedral:0, Improper:0} 748 | for f in self.tar_forces: 749 | try: 750 | type_count[f.category.__class__] += 1 751 | except (AttributeError, KeyError): 752 | pass 753 | return type_count 754 | 755 | 756 | def write_lammps_scripts(self, prefix='cg', folder = os.curdir, lammps_units="real", table_points=10000, lammps_input_file_head=None, lammps_input_file=None, force_conv=1.0, dist_conv=1.0, energy_conv=1.0, create_data_file=True): 757 | """Using the given ForceMatch and Universe object, this will create a set of input files for Lammps. 758 | 759 | The function will create the given folder and put all files 760 | in it. Tables are generated for each type of force from the 761 | ForceMatch object, a datafile derived from the current 762 | timestep of this Universe object and an input script that 763 | loads the force fields. The given lammps input file will be appended 764 | to the input script. 765 | """ 766 | if(mpi_support): 767 | #check if we're running with MPI 768 | comm = MPI.COMM_WORLD 769 | rank = comm.Get_rank() 770 | 771 | if(rank != 0): 772 | return 773 | 774 | 775 | #before we change directories, we need to get the path of the lammps input files 776 | if(lammps_input_file is not None): 777 | lammps_input_file = os.path.abspath(lammps_input_file) 778 | 779 | if(lammps_input_file_head is not None): 780 | lammps_input_file_head = os.path.abspath(lammps_input_file_head) 781 | 782 | 783 | if(not os.path.exists(folder)): 784 | os.mkdir(folder) 785 | original_dir = os.path.abspath(os.getcwd()) 786 | os.chdir(folder) 787 | 788 | #write force tables 789 | force_info = self.write_lammps_tables('%s_force' % prefix, 790 | force_conv = force_conv, 791 | energy_conv = energy_conv, 792 | dist_conv = dist_conv, 793 | points=table_points) 794 | 795 | #write data file 796 | #determin sim type 797 | type_count = self.get_force_type_count() 798 | 799 | if(create_data_file): 800 | sim_type = write_lammps_data(self.u, '%s_fm.data' % prefix, bonds=type_count[ForceCategories.Bond] > 0, 801 | angles=type_count[ForceCategories.Angle] > 0, dihedrals=False, impropers=False, 802 | force_match=self) 803 | 804 | #alright, now we prepare an input file 805 | with open("%s_fm.inp" % prefix, 'w') as output: 806 | 807 | #If we have a header for the lammps input file, write that 808 | if(lammps_input_file_head is not None): 809 | with open(lammps_input_file_head, 'r') as infile: 810 | for line in infile.readlines(): 811 | output.write(line) 812 | 813 | #now write our stuff 814 | output.write("#Lammps input file generated by ForcePy\n") 815 | if(create_data_file): 816 | output.write("units %s\n" % lammps_units) 817 | output.write("atom_style %s\n" % sim_type) 818 | output.write("read_data %s_fm.data\n" % prefix) 819 | output.write(force_info) 820 | output.write("\n") 821 | 822 | #now if an input file is given, add that 823 | if(lammps_input_file is not None): 824 | with open(lammps_input_file, 'r') as infile: 825 | for line in infile.readlines(): 826 | output.write(line) 827 | 828 | #now write a pdb, I've found that can come in handy 829 | try: 830 | self.u.atoms.write("%s_start.pdb" % prefix, bonds='all') 831 | except Exception as e: 832 | print "Failed to write start PDB ", e 833 | 834 | 835 | #now go back to original directory 836 | os.chdir(original_dir) 837 | 838 | #return the name of the input file we just created 839 | return "%s_fm.inp" % prefix 840 | -------------------------------------------------------------------------------- /ForcePy/Forces.py: -------------------------------------------------------------------------------- 1 | from ForceCategories import Pairwise, Bond, Angle, Dihedral 2 | from Mesh import UniformMesh 3 | from Util import norm3, spec_force_inner_loop, min_img_vec 4 | from States import State_Mask 5 | from Basis import UnitStep 6 | 7 | import numpy as np 8 | import random 9 | import numpy.linalg as ln 10 | from MDAnalysis import Universe 11 | from math import ceil,log 12 | 13 | class Force(object): 14 | """Can calculate forces from a universe object. 15 | 16 | To be used in the stochastic gradient step, a force should implement all of the methods here 17 | """ 18 | 19 | def __init__(self): 20 | self.sel1 = None 21 | self.sel2 = None 22 | self.mask1 = None 23 | self.mask2 = None 24 | 25 | 26 | def _setup_update_params(self, w_dim, initial_w=100, eta=None, hard_pow=2): 27 | """ Assumes a line from given initial height down to zero. Basically repulsive force 28 | """ 29 | self.eta = eta 30 | try: 31 | if(w_dim != len(initial_w)): 32 | self.w = initial_w[0] * (np.power(np.arange( w_dim - 1, -1, -1 , dtype=np.float32),hard_pow) / np.float32(w_dim ** hard_pow)) 33 | else: 34 | self.w = np.copy(initial_w) 35 | if(eta is None): 36 | self.eta = max(25, np.median(np.abs(initial_w)) * 2) 37 | except TypeError: 38 | self.w = initial_w * (np.power(np.arange( w_dim - 1, -1, -1 , dtype=np.float32),hard_pow) / np.float32(w_dim ** hard_pow)) 39 | if(eta is None): 40 | self.eta = max(25, abs(initial_w) * 2) 41 | 42 | self.avg_count = 0 43 | self.temp_grad = np.empty( (w_dim, 3) , dtype=np.float32) 44 | self.temp_force = np.empty( 3 , dtype=np.float32) 45 | self.w_grad = np.empty( w_dim, dtype=np.float32) 46 | self.regularization = [] 47 | self.lip = np.ones( np.shape(self.w) , dtype=np.float32) 48 | 49 | def setup_clone(self, clone): 50 | 51 | clone.sel1, clone.sel2, clone.mask1, clone.mask2 = self.sel1, self.sel2, self.mask1, self.mask2 52 | 53 | try: 54 | clone.avg_count = self.avg_count 55 | clone.avg_w = np.copy(self.avg_w) 56 | clone.lip = np.copy(lip) 57 | for r in self.regularization: 58 | clone.add_regularizer(r) 59 | except AttributeError: 60 | pass 61 | 62 | 63 | def update(self, df): 64 | negative_grad = self.w_grad #not actually negative yet. The negative sign is in the df 65 | np.dot(self.temp_grad, df, negative_grad) 66 | 67 | #apply any regularization 68 | for r in self.regularization: 69 | negative_grad -= r[0](self.w) 70 | self.lip += np.square(negative_grad) 71 | 72 | #we should be taking the negative of the dot product 73 | #but its easier to put the minus sign in this expression 74 | self.w = self.w + self.eta / np.sqrt(self.lip) * negative_grad 75 | 76 | def update_avg(self): 77 | #update overall average 78 | if(self.avg_count == 0): 79 | self.w_avg = np.copy( self.w ) 80 | self.avg_count += 1 81 | self.w_avg = self.w_avg * (self.avg_count - 1) / (self.avg_count) + self.w / (self.avg_count) 82 | 83 | def _swap_avg(self): 84 | self.w, self.w_avg = self.w_avg, self.w 85 | 86 | 87 | #In case the force needs access to the universe for setting up, override (and call this method). 88 | def setup_hook(self, u): 89 | try: 90 | self._build_mask(self.sel1, self.sel2, u) 91 | except AttributeError: 92 | pass #some forces don't have selections, eg FileForce 93 | 94 | #In case the force needs access to the universe for finishing up, override (and call this method). 95 | def finalize_hook(self, u): 96 | #switch to average 97 | self._swap_avg() 98 | 99 | 100 | def set_potential(self, u): 101 | """ Set the basis function for the potential calculation 102 | """ 103 | self.call_potential = u 104 | 105 | def get_category(self): 106 | try: 107 | return self.category 108 | except AttributeError: 109 | pass 110 | 111 | return None 112 | 113 | def specialize_types(self, selection_pair_1 = None, selection_pair_2 = None): 114 | self.sel1 = selection_pair_1 115 | self.sel2 = selection_pair_2 116 | self.type_name = "[%s] -- [%s]" % (selection_pair_1, selection_pair_2) 117 | 118 | def specialize_states(self, mask1, mask2, name1 = None, name2 = None): 119 | self.mask1 = mask1 120 | self.mask2 = mask2 121 | self.type_name = "[state %s] -- [state %s]" % (name1, name2) 122 | 123 | 124 | def _build_mask(self, sel1, sel2, u): 125 | if(self.mask1 is None and sel1 is not None): 126 | self.mask1 = [False for x in range(u.atoms.numberOfAtoms())] 127 | for a in u.selectAtoms('type %s' % sel1): 128 | self.mask1[a.number] = True 129 | elif(self.mask1 is None): 130 | self.mask1 = [True for x in range(u.atoms.numberOfAtoms())] 131 | 132 | 133 | if(self.mask2 is None and sel2 is not None): 134 | self.mask2 = [False for x in range(u.atoms.numberOfAtoms())] 135 | for a in u.selectAtoms('type %s' % sel2): 136 | self.mask2[a.number] = True 137 | elif(self.mask2 is None): 138 | self.mask2 = self.mask1 139 | 140 | def valid_pair(self, atom1, atom2): 141 | """Checks the two atoms' types to see if they match the type 142 | specialization. If no type selections are set, returns true 143 | """ 144 | #Don't use the selection class since it's a little heavy for this 145 | import re 146 | if(type(atom1) != type("")): 147 | atom1 = atom1.type 148 | if(type(atom2) != type("")): 149 | atom2 = atom2.type 150 | 151 | try: 152 | if(re.match(self.sel1, atom1) is not None and re.match(self.sel2, atom2) is not None): 153 | return True 154 | if(re.match(self.sel2, atom1) is not None and re.match(self.sel1, atom2) is not None): 155 | return True 156 | except AttributeError: 157 | return True 158 | 159 | return False 160 | 161 | 162 | 163 | def add_regularizer(self, *regularizers): 164 | """Add regularization to the stochastic gradient descent 165 | algorithm. 166 | """ 167 | for r in regularizers: 168 | self.regularization.append((r.grad_fxn, r.reg_fxn)) 169 | 170 | def plot(self, force_ax, potential_ax = None, true_force = None, true_potential = None): 171 | #make a mesh finer than the mesh used for finding paramers 172 | self.plot_x = np.arange( self.mind, self.maxd, (self.maxd - self.mind) / 1000. ) 173 | self.plot_force = np.empty( len(self.plot_x) ) 174 | 175 | self.true_force = true_force 176 | self.true_potential = true_potential 177 | 178 | self.force_ax = force_ax 179 | self.force_ax.set_ylabel('Force',color='b') 180 | 181 | #set the plot title 182 | force_ax.set_title(self.name) 183 | 184 | if(potential_ax is None): 185 | self.potential_ax = self.force_ax 186 | else: 187 | self.potential_ax.set_title("Potential of %s" % self.name) 188 | 189 | #call the force calculations 190 | self.calc_force_array(self.plot_x, self.plot_force) 191 | 192 | #draw true functions, if they are given 193 | if(not (true_force is None)): 194 | true_force_a = np.empty( len(self.plot_x) ) 195 | for i in range(len(true_force_a)): 196 | true_force_a[i] = true_force(self.plot_x[i]) 197 | force_ax.plot(self.plot_x, true_force_a, color="green") 198 | if(not (true_potential is None) and not (self.call_potential is None)): 199 | true_potential_a = np.empty( len(self.plot_x) ) 200 | for i in range(len(true_potential_a)): 201 | true_potential_a[i] = true_potential(self.plot_x[i]) 202 | potential_ax.plot(self.plot_x, true_potential_a, color="green") 203 | 204 | #plot force and save reference to line 205 | self.force_line, = self.force_ax.plot(self.plot_x, self.plot_force, color="blue", label="Force") 206 | force_ax.set_ylim(-1.1*min(-min(self.plot_force), max(self.plot_force)), 1.1*max(self.plot_force)) 207 | 208 | from matplotlib.ticker import FormatStrFormatter 209 | force_ax.yaxis.set_major_formatter(FormatStrFormatter('%0.3f')) 210 | 211 | #plot potential if possible 212 | try: 213 | self.plot_potential = np.empty( len(self.plot_x) ) 214 | self.calc_potential_array(self.plot_x, self.plot_potential) 215 | self.potential_ax = self.force_ax.twinx() 216 | self.potential_ax.set_ylabel('Potential',color='r') 217 | self.potential_line, = self.potential_ax.plot(self.plot_x, self.plot_potential, color="red", label="Potential") 218 | 219 | if(self.force_ax == self.potential_ax): 220 | self.potential_ax.set_ylim(min(1.1*min(min(self.plot_potential), -max(self.plot_potential)), -1.1*min(-min(self.plot_force), max(self.plot_force))), max(1.1*max(self.plot_force), 1.1*max(self.plot_potential))) 221 | else: 222 | self.potential_ax.set_ylim(1.1*min(min(self.plot_potential), -max(self.plot_potential)), 1.1*max(self.plot_potential)) 223 | 224 | except NotImplementedError: 225 | self.plot_potential = None 226 | 227 | 228 | self.potential_ax.legend([self.force_line,self.potential_line],["Force","Potential"]) 229 | 230 | 231 | 232 | def update_plot(self): 233 | #call the force calculations 234 | self.calc_force_array(self.plot_x, self.plot_force) 235 | self.force_line.set_ydata(self.plot_force) 236 | self.force_ax.set_ylim(-1.1*min(-min(self.plot_force), max(self.plot_force)), 1.1*max(self.plot_force)) 237 | 238 | #plot potential if possible 239 | if(not (self.plot_potential is None)): 240 | self.calc_potential_array(self.plot_x, self.plot_potential) 241 | self.potential_line.set_ydata(self.plot_potential) 242 | 243 | 244 | if(self.force_ax == self.potential_ax): 245 | self.potential_ax.set_ylim(min(1.1*min(min(self.plot_potential), -max(self.plot_potential)), -1.1*min(-min(self.plot_force), max(self.plot_force))), max(1.1*max(self.plot_force), 1.1*max(self.plot_potential))) 246 | else: 247 | self.potential_ax.set_ylim(1.1*min(min(self.plot_potential), -max(self.plot_potential)), 1.1*max(self.plot_potential)) 248 | 249 | 250 | def teardown_plot(self): 251 | #These are in a specific order 252 | try: 253 | del self.plot_x 254 | del self.plot_force 255 | del self.true_force 256 | del self.true_potential 257 | del self.force_ax 258 | del self.force_line 259 | del self.potential_ax 260 | del self.plot_potential 261 | del self.potential_line 262 | except AttributeError: 263 | pass 264 | 265 | def write_lammps_table(self, outfile, force_conv=1., energy_conv=1., dist_conv=1., points=10000): 266 | import os 267 | """Write the current forcefield to the given outfile. 268 | """ 269 | 270 | #header 271 | if(type(outfile ) == type('')): 272 | outfile = open(outfile, 'w') 273 | outfile.write('#%s\n\n' % self.name) 274 | outfile.write('%s\n' % self.short_name) 275 | 276 | #setup force table 277 | rvals = np.arange( self.mind, self.maxd, (self.maxd - self.mind) / float(points)) 278 | force = np.empty( len(rvals) ) 279 | potential = np.empty( len(rvals) ) 280 | 281 | self.calc_force_array(rvals, force) 282 | self.calc_potential_array(rvals, potential) 283 | 284 | #header parameters 285 | if(type(self.category) == Pairwise): 286 | outfile.write("N %d R %f %f\n\n" % (len(rvals), self.mind * dist_conv, self.maxd * dist_conv)) 287 | elif(type(self.category) == Bond or type(self.category) == Angle): 288 | outfile.write("N %d EQ %f\n\n" % (len(rvals), rvals[np.nonzero(potential == min(potential))[0][0]])) 289 | elif(type(self.category) == Dihedral): 290 | outfile.write("N %d RADIANS\n\n" % (len(rvals))) 291 | for i in range(len(rvals)): 292 | outfile.write("%d %f %f %f\n" % (i+1, dist_conv * rvals[i], energy_conv * potential[i], force_conv * force[i])) 293 | 294 | 295 | def write_hoomd_table(self, outfile, force_conv=1., energy_conv=1., dist_conv=1., points=1000): 296 | """Write the forcefield to hoomd readable outfile. 297 | """ 298 | 299 | #open 300 | if(type(outfile ) == type('')): 301 | 302 | outfile = open(outfile, 'w') 303 | 304 | #setup table 305 | rvals = np.arange( self.mind, self.maxd, (self.maxd - self.mind) / float(points)) 306 | force = np.empty( len(rvals) ) 307 | potential = np.empty( len(rvals) ) 308 | 309 | self.calc_force_array(rvals, force) 310 | self.calc_potential_array(rvals, potential) 311 | 312 | #write table 313 | for i in range(len(rvals)): 314 | outfile.write("%f %f %f\n" % (dist_conv * rvals[i], energy_conv * potential[i], force_conv * force[i])) 315 | 316 | 317 | def write_table(self, outfile, force_conv=1., energy_conv=1., dist_conv=1., points=10000): 318 | outfile.write('#%s\n\n' % self.name) 319 | rvals = np.arange( self.mind, self.maxd, (self.maxd - self.mind) / float(points)) 320 | force = np.empty( len(rvals) ) 321 | potential = np.empty( len(rvals) ) 322 | 323 | self.calc_force_array(rvals, force) 324 | self.calc_potential_array(rvals, potential) 325 | 326 | for i in range(len(rvals)): 327 | outfile.write("%d %f %f %f\n" % (i+1, dist_conv * rvals[i], energy_conv * potential[i], force_conv * force[i])) 328 | 329 | @property 330 | def name(self): 331 | name = "Force" #in case none 332 | try: 333 | name = self._long_name #if there is a plot_title 334 | name = "%s type %s" % (name, self.type_name) #if this is specialized 335 | except AttributeError: 336 | pass 337 | finally: 338 | return name 339 | 340 | @property 341 | def short_name(self): 342 | name = "F" 343 | try: 344 | name = self._short_name 345 | if(self.sel1 is not None and self.sel2 is not None): 346 | name = "%s_%s_%s" % (name, self.sel1, self.sel2) 347 | #check if the masks are secretely state masks 348 | elif(self.mask1 and type(self.mask1 == State_Mask)): 349 | name = "{}_{}_{}".format(name, self.mask1.state, self.mask2.state) 350 | name = ''.join(name.split()) #remove whitesspace 351 | except AttributeError: 352 | pass 353 | finally: 354 | return name 355 | 356 | def calc_force_array(self, d, force): 357 | raise NotImplementedError("Must implement this function") 358 | 359 | def calc_potential_array(self, d, potentials): 360 | raise NotImplementedError("Must implement this function") 361 | 362 | def calc_potentials(self, u): 363 | raise NotImplementedError("Must implement this function") 364 | 365 | def calc_forces(self, forces, u): 366 | raise NotImplementedError("Must implement this function") 367 | 368 | def calc_particle_force(self, i, u): 369 | raise NotImplementedError("Must implement this function") 370 | 371 | def clone_force(self): 372 | """Instantiates a new Force, with a reference to the same mesh. 373 | """ 374 | raise NotImplementedError("Must implement this function") 375 | 376 | class FileForce(Force): 377 | """ Reads forces from the trajectory file 378 | """ 379 | 380 | def calc_forces(self, forces, u): 381 | forces[:] = u.trajectory.ts._forces 382 | 383 | def clone_force(self): 384 | return FileForce() 385 | 386 | 387 | class LammpsFileForce(Force): 388 | """Reads forces from a lammps force output 389 | """ 390 | def __init__(self, file_name): 391 | super(LammpsFileForce, self).__init__() 392 | self.file = open(file_name, 'r') 393 | 394 | def calc_forces(self, forces, u): 395 | while(not self.file.readline().startswith('ITEM: ATOMS')): 396 | pass 397 | for i in range(len(forces)): 398 | sline = self.file.readline().split() 399 | try: 400 | forces[int(sline[0]),:] = [-float(x) for x in sline[1:]] 401 | except ValueError: 402 | print "Invalid forces line at %s" % reduce(lambda x,y: x + y, sline) 403 | 404 | def clone_force(self): 405 | return LammpsFileForce(self.file.name) 406 | 407 | 408 | class XYZFileForce(Force): 409 | """Reads forces from a lammps force output 410 | """ 411 | def __init__(self, file_name, skip=0): 412 | super(XYZFileForce, self).__init__() 413 | self.file_name = file_name 414 | self.frames_read = 0 415 | self.file = open(file_name, 'r') 416 | for s in range(skip): 417 | #read 2 header lines 418 | self.file.readline() 419 | self.file.readline() 420 | #read the rest 421 | while(len(self.file.readline().split()) == 4): 422 | pass 423 | self.frames_read += 1 424 | 425 | 426 | def calc_forces(self, forces, u): 427 | #read 2 header lines 428 | self.file.readline() 429 | self.file.readline() 430 | 431 | for i in range(len(forces)): 432 | sline = self.file.readline().split() 433 | try: 434 | if(len(sline) < 3): 435 | break 436 | forces[i] = [-float(x) for x in sline[1:]] 437 | except ValueError: 438 | print "Invalid forces line at %s" % reduce(lambda x,y: x + y, sline) 439 | 440 | self.frames_read += 1 441 | 442 | def clone_force(self): 443 | return XYZFileForce(self.file.name) 444 | 445 | def __reduce__(self): 446 | return XYZFileForce, (self.file_name, self.frames_read) 447 | 448 | class AnalyticForce(Force): 449 | """ A pairwise analtric force that takes in a function for 450 | calculating the force. The function passed should accept the 451 | scalar distance between the two particles as its first argument 452 | and a scalar vector of length n for its second argument. The 453 | gradient should take in the pairwise distance and a vector of 454 | length n (as set in the constructor). It should return a gradient 455 | of length n. 456 | 457 | """ 458 | 459 | 460 | def __init__(self, category, f, g, n, cutoff=None, potential = None): 461 | super(AnalyticForce, self).__init__() 462 | self.call_force = f 463 | self.call_grad = g 464 | self.call_potential = potential 465 | self.w = np.zeros( n ) 466 | self._setup_update_params(n) 467 | self.category = category.get_instance(cutoff) 468 | self.cutoff = cutoff 469 | self._long_name = "AnalyticForce for %s" % category.__name__ 470 | self._short_name = "AF_%s" % category.__name__ 471 | 472 | 473 | def clone_force(self): 474 | assert type(copy) == AnalyticForce, "Must implement clone_force method for %s" % type(copy) 475 | copy = AnalyticForce(self.category.__class__, self.call_force, self.call_grad, len(self.w), self.cutoff, self.call_potential) 476 | self.setup_clone(copy) 477 | return copy 478 | 479 | @property 480 | def mind(self): 481 | return 0.01 482 | 483 | @property 484 | def maxd(self): 485 | try: 486 | return self.cutoff 487 | except AttributeError: 488 | return 10 489 | 490 | 491 | def calc_force_array(self, d, forces): 492 | for i in range(len(d)): 493 | forces[i] = self.call_force(d[i], self.w) 494 | 495 | def calc_potential_array(self, d, potentials): 496 | if(self.call_potential is None): 497 | return 498 | for i in range(len(d)): 499 | potentials[i] = self.call_potential(d[i], self.w) 500 | 501 | def calc_potentials(self, u): 502 | if(self.call_potential is None): 503 | return 0 504 | 505 | positions = u.atoms.get_positions() 506 | potential = 0 507 | dims = u.trajectory.ts.dimensions 508 | for i in range(u.atoms.numberOfAtoms()): 509 | #check atom types 510 | if(self.mask1[i]): 511 | maskj = self.mask2 512 | elif(self.mask2[i]): 513 | maskj = self.mask1 514 | else: 515 | continue 516 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 517 | #do not double count 518 | if(i < j): 519 | continue 520 | potential += self.call_potential(d,self.w) 521 | return potential 522 | 523 | 524 | def calc_forces(self, forces, u): 525 | 526 | positions = u.atoms.get_positions() 527 | dims = u.trajectory.ts.dimensions 528 | for i in range(u.atoms.numberOfAtoms()): 529 | #check atom types 530 | if(self.mask1[i]): 531 | maskj = self.mask2 532 | elif(self.mask2[i]): 533 | maskj = self.mask1 534 | else: 535 | continue 536 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 537 | forces[i] += self.call_force(d,self.w) * (r / d) 538 | 539 | def calc_particle_force(self, i, u): 540 | 541 | self.temp_force.fill(0) 542 | self.temp_grad.fill(0) 543 | 544 | if(self.mask1[i]): 545 | maskj = self.mask2 546 | elif(self.mask2[i]): 547 | maskj = self.mask1 548 | else: 549 | return self.temp_force 550 | 551 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 552 | self.temp_force += self.call_force(d, self.w) * r 553 | f_grad = self.call_grad(d, self.w) 554 | self.temp_grad += np.outer(f_grad, r) 555 | return self.temp_force 556 | 557 | 558 | class LJForce(AnalyticForce): 559 | """ Lennard jones pairwise analytic force. Not shifted (!) 560 | """ 561 | def __init__(self, cutoff, sigma=1, epsilon=1): 562 | super(LJForce, self).__init__(Pairwise, LJForce.lj, LJForce.dlj, 2, cutoff, LJForce.ulj) 563 | self.w[0] = epsilon 564 | self.w[1] = sigma 565 | self._long_name = "LJForce" 566 | self._short_name = "LJ" 567 | 568 | @staticmethod 569 | def lj(d, w): 570 | return -4 * w[0] / w[1] * (6 * (w[1] / d) ** 7 - 12 * (w[1] / d) ** 13) 571 | 572 | @staticmethod 573 | def ulj(d, w): 574 | return 4 * w[0] * ((w[1] / d) ** 12 - (w[1] / d) ** 6) 575 | 576 | @staticmethod 577 | def dlj(d, w): 578 | return np.asarray([4 / w[1] * (6 * (w[1] / d) ** 7 - 12 * (w[1] / d) ** 13), 4 * w[0] / w[1] * (36 * (w[1] / d) ** 6 - 144 * (w[1] / d) ** 12)]) 579 | 580 | @property 581 | def mind(self): 582 | return w[1] * 0.5 583 | 584 | @property 585 | def maxd(self): 586 | return w[1] * 5 587 | 588 | class HarmonicForce(AnalyticForce): 589 | def __init__(self, category, cutoff=None): 590 | super(HarmonicForce, self).__init__(category, HarmonicForce.force, HarmonicForce.grad, 2, cutoff, HarmonicForce.potential) 591 | self._setup_update_params(2, [1., self.maxd / 2.], eta=0.1) 592 | 593 | def clone_force(self): 594 | copy = HarmonicForce(self.category.__class__, self.cutoff) 595 | self.setup_clone(copy) 596 | return copy 597 | 598 | 599 | 600 | @staticmethod 601 | def force(d, w): 602 | return 2 * w[0] * (d - w[1]) 603 | 604 | @staticmethod 605 | def grad(d, w): 606 | return [2 * (d - w[1]), -2 * w[0]] 607 | 608 | @staticmethod 609 | def potential(d,w): 610 | return w[0] * (d - w[1]) ** 2 611 | 612 | class FixedHarmonicForce(AnalyticForce): 613 | """This is meant for bonds which are fixed in the trajectory. The 614 | spring constant is set, but the equilibrium distance may 615 | optimized if it's not set in the constructor. Do not use this 616 | for movable bonds in a trajectory, since it will not register 617 | forces 618 | """ 619 | def __init__(self, category, k, x0=None, x0_guess=None): 620 | super(FixedHarmonicForce, self).__init__(category, HarmonicForce.force, self.grad, 2, None, HarmonicForce.potential) 621 | self.w_grad.fill(0) #we might not update and want it correct 622 | self.k = k 623 | self.x0 = x0 624 | self.w[0] = k 625 | if(self.x0 is not None): 626 | self.w[1] = self.x0 627 | elif(x0_guess is not None): 628 | self.w[1] = x0_guess 629 | 630 | 631 | def clone_force(self): 632 | copy = FixedHarmonicForce(self.category.__class__, k=self.k, x0=self.x0, x0_guess=self.w[1]) 633 | self.setup_clone(copy) 634 | return copy 635 | 636 | def __reduce__(self): 637 | return FixedHarmonicForce, (self.category.__class__, self.k, self.x0, self.w[1]) 638 | 639 | def calc_particle_force(self, i, u): 640 | self.temp_force.fill(0) 641 | if(self.mask1[i]): 642 | maskj = self.mask2 643 | elif(self.mask2[i]): 644 | maskj = self.mask1 645 | else: 646 | return self.temp_force 647 | 648 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 649 | self.w_grad[1] += -(d - self.w[1]) 650 | 651 | return self.temp_force 652 | 653 | 654 | def grad(self, d, w): 655 | return [0,0] 656 | 657 | def update(self, df): 658 | #slightly modified 659 | self.lip += np.square(self.w_grad) 660 | self.w = self.w - self.eta / np.sqrt(self.lip) * self.w_grad 661 | self.w_grad.fill(0) 662 | 663 | 664 | @property 665 | def mind(self): 666 | return 0.01 667 | 668 | @property 669 | def maxd(self): 670 | return self.w[1] * 2 671 | 672 | 673 | class Regularizer: 674 | """grad_fxn: takes in vector returns gradient vector 675 | reg_fxn: takes in vector, returns scalar 676 | """ 677 | def __init__(self, grad_fxn, reg_fxn): 678 | self.grad_fxn = grad_fxn 679 | self.reg_fxn = reg_fxn 680 | 681 | 682 | class SmoothRegularizer(Regularizer): 683 | """ sum_i (w_{i+1} - w{i}) ^ 2 684 | """ 685 | 686 | def __init__(self): 687 | raise Exception("Smoothregulizer is static and should not be instanced") 688 | 689 | @staticmethod 690 | def grad_fxn(x): 691 | g = np.empty( np.shape(x) ) 692 | g[0] = 2 * x[0] 693 | g[-1] = 2 * x[-1] 694 | g[1:] = 4 * x[1:] - 2 * x[:-1] 695 | g[:-1] -= 2 * x[1:] 696 | return g 697 | 698 | @staticmethod 699 | def reg_fxn(x): 700 | return 0 701 | 702 | 703 | class L2Regularizer(Regularizer): 704 | """ sum_i (w_{i+1} - w{i}) ^ 2 705 | """ 706 | 707 | def __init__(self): 708 | raise Exception("L2Regularizer is static and should not be instanced") 709 | 710 | @staticmethod 711 | def grad_fxn(x): 712 | g = 2 * np.copy(x) 713 | return g 714 | 715 | @staticmethod 716 | def reg_fxn(x): 717 | return ln.norm(x) 718 | 719 | 720 | class SpectralForce(Force): 721 | """A pairwise force that is a linear combination of basis functions 722 | The basis function should take two arguments: the distance and the 723 | mesh. Additional arguments after the function pointer will be 724 | passed after the two arguments. For example, the function may be 725 | defined as so: def unit_step(x, mesh, height). 726 | """ 727 | 728 | def __init__(self, category, mesh, basis, initial_w=0, w_range=None, permit_overlap = False): 729 | super(SpectralForce, self).__init__() 730 | self.basis = basis 731 | self.mesh = mesh 732 | self.permit_overlap = permit_overlap 733 | #create weights 734 | self.temp_force = np.zeros( 3 ) 735 | self.category = category.get_instance(mesh.max()) 736 | self._long_name = "SpectralForce for %s" % category.__name__ 737 | self._short_name = "SF_%s" % category.__name__ 738 | self.do_repulsion_fill = None 739 | 740 | #if this is an updatable force, set up stuff for it 741 | self._setup_update_params(len(mesh), initial_w=initial_w, eta=w_range) 742 | 743 | @staticmethod 744 | def load_lammps_table(lammps_file, category, label, force_conversion=1., eta=None): 745 | """ Build a spectral force with a uniform mesh and a unit step 746 | basis from a lammps table. The lable is the lammps label that 747 | comes before the information about the table size. 748 | """ 749 | with open(lammps_file) as f: 750 | #read until the label 751 | while(not f.readline().startswith(label)): 752 | pass 753 | #get info to build mesh 754 | info = f.readline().split() 755 | (points, left, right) = (float(info[1]), float(info[3]), float(info[4])) 756 | mesh = UniformMesh(left, right, (right - left) / points) 757 | force = SpectralForce(category, mesh, UnitStep) 758 | 759 | 760 | i = 0 761 | for l in f: 762 | if(len(l) < 2): 763 | continue 764 | assert (mesh[i] - float(l.split()[1])) < 0.00001, "Mesh not matching lammps table" 765 | force.w[i] = force_conversion * float(l.split()[3]) 766 | i += 1 767 | 768 | if(eta): 769 | force.eta = eta 770 | else: 771 | force.eta = np.mean(abs(force.w)) / 100. #only a 1% change is allowed. 772 | 773 | return force 774 | 775 | 776 | @property 777 | def mind(self): 778 | """lots of codes get confused with the force/potential being at 0, so avoid that 779 | """ 780 | return self.mesh.min() if self.mesh.min() > 0 else 0.01 781 | 782 | @property 783 | def maxd(self): 784 | return self.mesh.max() 785 | 786 | def clone_force(self): 787 | copy = SpectralForce(self.category.__class__, self.mesh, self.basis, initial_w=self.w, w_range=self.eta) 788 | copy.do_repulsion_fill = self.do_repulsion_fill 789 | self.setup_clone(copy) 790 | return copy 791 | 792 | def calc_force_array(self, d, forces): 793 | for i in range(len(d)): 794 | forces[i] = self.w.dot(self.basis.force(d[i], self.mesh)) 795 | 796 | def calc_potential_array(self, d, potentials): 797 | for i in range(len(d)): 798 | potentials[i] = self.w.dot(self.basis.potential(d[i], self.mesh)) 799 | 800 | 801 | def calc_potentials(self, u): 802 | 803 | potential = 0 804 | self.temp_grad.fill(0) 805 | for i in range(u.atoms.numberOfAtoms()): 806 | #check to if this is a valid type 807 | if(self.mask1[i]): 808 | maskj = self.mask2 809 | elif(self.mask2[i]): 810 | maskj = self.mask1 811 | else: 812 | continue 813 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 814 | if( i < j): 815 | continue 816 | temp = self.basis.potential(d, self.mesh) 817 | potential += self.w.dot(temp) 818 | self.temp_grad[:,1] += temp 819 | 820 | return potential 821 | 822 | 823 | def calc_forces(self, forces, u): 824 | for i in range(u.atoms.numberOfAtoms()): 825 | #check to if this is a valid type 826 | if(self.mask1[i]): 827 | maskj = self.mask2 828 | elif(self.mask2[i]): 829 | maskj = self.mask1 830 | else: 831 | continue 832 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 833 | if(self.permit_overlap and d < 10**-50): 834 | continue 835 | force = self.w.dot(self.basis.force(d, self.mesh)) * (r / d) 836 | forces[i] += force 837 | 838 | def calc_particle_force(self, i, u): 839 | """ 840 | This is the most called function, so I've tried a few approaches to improve speed. 841 | The weave was the fastest, but least portable. Now I'm using a tuned cython function 842 | for the most intensive calculation which is defined in Util.pyx 843 | """ 844 | 845 | self.temp_force.fill(0) 846 | self.temp_grad.fill(0) 847 | 848 | 849 | #check type 850 | if(self.mask1[i]): 851 | maskj = self.mask2 852 | elif(self.mask2[i]): 853 | maskj = self.mask1 854 | else: 855 | return self.temp_force 856 | 857 | temp = np.empty( len(self.w) , dtype=np.float32) 858 | dims = u.trajectory.ts.dimensions 859 | for r,d,j in self.category.generate_neighbor_vecs(i, u, maskj): 860 | self.basis.force_cache(d, temp, self.mesh) 861 | #tuned cython funciton 862 | spec_force_inner_loop(self.w, temp, self.temp_grad, self.temp_force, r) 863 | 864 | return self.temp_force 865 | 866 | def fill_with_repulsion(self, end_at=None, height=None, power=12): 867 | self.do_repulsion_fill = (end_at, height, power) 868 | 869 | def finalize_hook(self, u): 870 | super(SpectralForce, self).finalize_hook(u) 871 | if(self.do_repulsion_fill is not None): 872 | self._do_fill_with_repulsion(*self.do_repulsion_fill) 873 | 874 | def _do_fill_with_repulsion(self, end_at=None, height=None, power=12): 875 | ''' 876 | Fill out the beginning of the force with a repulsive 877 | potential. Can give a distance from which to end the 878 | repulsion or the last point, starting from the beginning, of 879 | the weights that never was moved. 880 | ''' 881 | #convert from distance to mesh index 882 | if(end_at is None): 883 | end_at_index = np.where(self.lip > 1.)[0][0] 884 | end_at = self.mesh[end_at_index] 885 | else: 886 | end_at_index = self.mesh.mesh_index(end_at) 887 | if(height is None): 888 | height = self.w[end_at_index] 889 | self.w[:end_at_index] = height * (1 + np.power(np.arange( end_at, 0, -float(end_at) / end_at_index, dtype=np.float32), power)) 890 | -------------------------------------------------------------------------------- /ForcePy/Mesh.pyx: -------------------------------------------------------------------------------- 1 | # cython: profile=False 2 | #filename Mesh.pyx 3 | 4 | from libc.math cimport ceil, floor 5 | import numpy as np 6 | cimport numpy as np 7 | 8 | 9 | FTYPE = np.float32 10 | ctypedef np.float32_t FTYPE_t 11 | 12 | 13 | cdef class UniformMesh(object): 14 | """Uniform mesh. Right is not inclusive. Left is in construction. 15 | """ 16 | cdef FTYPE_t l 17 | cdef FTYPE_t r 18 | cdef FTYPE_t __dx 19 | cdef int length 20 | 21 | 22 | def __init__(self, l, r, dx): 23 | self.l = l 24 | self.r = r 25 | self.__dx = dx 26 | self.length = int(ceil((self.r - self.l) / self.__dx)) 27 | 28 | cpdef FTYPE_t max(self): 29 | return self.r 30 | 31 | cpdef FTYPE_t min(self): 32 | return self.l 33 | 34 | cpdef int mesh_index(self, FTYPE_t x): 35 | #I'm having trouble deciding to permit things past the boundary from working or not 36 | # assert x >= self.l and x <= self.r, "Mesh point is not within mesh: %g, [%g, %g]" % (x, self.l, self.r) 37 | return max(0, min(self.length - 1, int(floor( (x - self.l) / self.__dx) ))) 38 | 39 | def __len__(self): 40 | return self.length 41 | 42 | def __getitem__(self, i): 43 | return i * self.__dx + self.l 44 | 45 | cpdef FTYPE_t cgetitem(self, int i): 46 | return i * self.__dx + self.l 47 | 48 | property dx: 49 | 50 | def __get__(self): 51 | return self.__dx 52 | def __set__(self, value): 53 | self.__dx = value 54 | 55 | 56 | def __reduce__(self): 57 | return UniformMesh, (self.l, self.r, self.dx) 58 | 59 | -------------------------------------------------------------------------------- /ForcePy/NeighborList.pyx: -------------------------------------------------------------------------------- 1 | # cython: profile=False 2 | # filename: NeighborList.pyx 3 | 4 | import pstats, cProfile 5 | import numpy as np 6 | cimport numpy as np 7 | import cython 8 | import time 9 | from libc.stdlib cimport malloc, free 10 | from libc.math cimport ceil, floor, sqrt 11 | 12 | 13 | DTYPE = np.int32 14 | ctypedef np.int32_t DTYPE_t 15 | 16 | FTYPE = np.float32 17 | ctypedef np.float32_t FTYPE_t 18 | 19 | cdef FTYPE_t cround(FTYPE_t x): 20 | return ceil(x - 0.5) if x < 0. else floor(x + 0.5) 21 | 22 | 23 | @cython.boundscheck(False) # turn off bounds-checking for entire function 24 | cdef FTYPE_t min_img_dist_sq(np.ndarray[FTYPE_t, ndim=1] x, np.ndarray[FTYPE_t, ndim=1] y, double* img, bint periodic=True): 25 | cdef FTYPE_t dx 26 | cdef FTYPE_t dist = 0 27 | cdef int i 28 | for i in range(3): 29 | dx = x[i] - y[i] 30 | if(periodic): 31 | dx -= cround(dx / img[i]) * img[i] 32 | dist += dx * dx 33 | return dist 34 | 35 | 36 | cdef class NeighborList(object): 37 | """Neighbor list class 38 | """ 39 | cdef double cutoff 40 | cdef double* box 41 | cdef nlist_lengths 42 | cdef nlist 43 | cdef int* cell_number 44 | cdef cell_neighbors #use python object for simplicity and since this isn't yet a bottleneck 45 | cdef int* cells 46 | cdef int* head 47 | cdef exclusion_list 48 | cdef int cell_number_total 49 | cdef bint exclude_14 50 | 51 | 52 | def __init__(self, u, cutoff, exclude_14 = True): 53 | 54 | #set up cell number and data 55 | 56 | self.cutoff = cutoff 57 | self.box = malloc(3 * sizeof(double)) 58 | self.cell_number = malloc(3 * sizeof(int)) 59 | self.cell_number_total = 1 60 | cdef i 61 | for i in range(3): 62 | self.box[i] = u.dimensions[i] 63 | self.cell_number[i] = max(1,int(self.box[i] / self.cutoff)) 64 | self.cell_number_total *= self.cell_number[i] 65 | 66 | self.nlist_lengths = [0 for x in range(u.atoms.numberOfAtoms())] 67 | self.nlist = np.arange(u.atoms.numberOfAtoms() * (u.atoms.numberOfAtoms() - 1), dtype=DTYPE) 68 | 69 | self.cells = malloc(u.atoms.numberOfAtoms() * sizeof(int)) 70 | self.head = malloc(self.cell_number_total * sizeof(int)) 71 | self.exclusion_list = None 72 | self.exclude_14 = exclude_14 73 | 74 | 75 | #pre-compute neighbors. Waste of space, but saves programming effort required for ghost cellls 76 | self.cell_neighbors = [[] for x in range(self.cell_number_total)] 77 | #Leaving all this stuff as python objects because speed is not an issue here 78 | for xi in range(self.cell_number[0]): 79 | for yi in range(self.cell_number[1]): 80 | for zi in range(self.cell_number[2]): 81 | #get neighbors 82 | index = (xi * self.cell_number[1] + yi) * self.cell_number[2] + zi 83 | index_vector = [xi, yi, zi] 84 | neighs = [[] for x in range(3)] 85 | for i in range(3): 86 | neighs[i] = [self.cell_number[i] - 1 if index_vector[i] == 0 else index_vector[i] - 1, 87 | index_vector[i], 88 | 0 if index_vector[i] == self.cell_number[i] - 1 else index_vector[i] + 1] 89 | for xd in neighs[0]: 90 | for yd in neighs[1]: 91 | for zd in neighs[2]: 92 | neighbor = (xd * self.cell_number[1] + yd) * self.cell_number[2] + zd 93 | #check if neighbor is already in cell_neighbor 94 | #this is possible if wrapped and cell number is 1 95 | if(not neighbor in self.cell_neighbors[index]): 96 | self.cell_neighbors[index].append(neighbor) 97 | 98 | def __del__(self): 99 | free(self.box) 100 | free(self.cell_number) 101 | free(self.head) 102 | free(self.cells) 103 | 104 | @cython.boundscheck(False) #turn off bounds checking 105 | cdef bin_particles(self, u): 106 | cdef int i,j,icell 107 | cdef double k 108 | for i in range(self.cell_number_total): 109 | self.head[i] = -1 110 | 111 | positions = u.atoms.get_positions(copy=False) 112 | for i in range(u.atoms.numberOfAtoms()): 113 | 114 | icell = 0 115 | #fancy index and binning loop over dimensions 116 | for j in range(3): 117 | #sometimes things are unwrapped, better to assume they aren't 118 | k = positions[i][j]/ self.box[j] * self.cell_number[j] 119 | k = floor(k % self.cell_number[j]) 120 | icell = k + icell * self.cell_number[j] 121 | #push what is on the head into the cells 122 | self.cells[i] = self.head[icell] 123 | #add current value 124 | self.head[icell] = i 125 | 126 | 127 | cdef _build_exclusion_list(self, u): 128 | #what we're building 129 | self.exclusion_list = [[] for x in range(u.atoms.numberOfAtoms())] 130 | #The exclusion list at the most recent depth 131 | temp_list = [[] for x in range(u.atoms.numberOfAtoms())] 132 | #build 1,2 terms 133 | if u.bonds is not None: 134 | for b in u.bonds: 135 | self.exclusion_list[b[0].number].append(b[1].number) 136 | self.exclusion_list[b[1].number].append(b[0].number) 137 | # build 1,3 and 1,4 138 | for i in range( 1 if self.exclude_14 else 2): 139 | #copy 140 | temp_list[:] = self.exclusion_list[:] 141 | for a in range(u.atoms.numberOfAtoms()): 142 | for b in range(len(temp_list[a])): 143 | self.exclusion_list[a].append(b) 144 | 145 | @cython.boundscheck(False) #turn off bounds checking 146 | @cython.wraparound(False) #turn off negative indices 147 | cdef int _build_nlist(self, u): 148 | 149 | if(self.exclusion_list == None): 150 | self._build_exclusion_list(u) 151 | 152 | #bin the particles 153 | self.bin_particles(u) 154 | 155 | 156 | ntime = time.time() 157 | positions = u.atoms.get_positions(copy=False) 158 | 159 | cdef int i, j, nlist_count, icell 160 | cdef double k 161 | nlist_count = 0 162 | for i in range(u.atoms.numberOfAtoms()): 163 | self.nlist_lengths[i] = 0 164 | 165 | periodic = u.trajectory.periodic 166 | for i in range(u.atoms.numberOfAtoms()): 167 | icell = 0 168 | #fancy indepx and binning loop over dimensions 169 | for j in range(3): 170 | #sometimes things are unwrapped, better to assume they aren't 171 | k = positions[i][j]/ self.box[j] * self.cell_number[j] 172 | k = floor(k % self.cell_number[j]) 173 | icell = int(k) + icell * self.cell_number[j] 174 | for ncell in self.cell_neighbors[icell]: 175 | j = self.head[ncell] 176 | while(j != - 1): 177 | if(i != j and 178 | not (j in self.exclusion_list[i]) and 179 | min_img_dist_sq(positions[i], positions[j], self.box, periodic) < self.cutoff ** 2): 180 | self.nlist[nlist_count] = j 181 | self.nlist_lengths[i] += 1 182 | nlist_count += 1 183 | j = self.cells[j] 184 | return nlist_count 185 | 186 | def build_nlist(self, u): 187 | return self.nlist[:self._build_nlist(u)], self.nlist_lengths 188 | 189 | -------------------------------------------------------------------------------- /ForcePy/States.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | class State_Mask(object): 4 | """This object generates a mask on the fly for a particle given 5 | its state function acting on a MDAnalysis Universe""" 6 | 7 | def __init__(self, cgtofg_fiber_map, state_function, state_index): 8 | self.fxn = state_function 9 | self.map = cgtofg_fiber_map 10 | self.state = state_index 11 | 12 | 13 | def __getitem__(self, index): 14 | ag = self.map[index] 15 | 16 | if(ag.state is None): 17 | x = random.random() 18 | vsum = 0 19 | for i,v in enumerate(self.fxn(ag.ag)): 20 | vsum += v 21 | if(x <= vsum): 22 | break 23 | ag.state = i 24 | 25 | return ag.state == self.state 26 | -------------------------------------------------------------------------------- /ForcePy/Util.pyx: -------------------------------------------------------------------------------- 1 | # cython: profile=False 2 | # filename: Util.pyx 3 | 4 | import numpy as np 5 | cimport numpy as np 6 | import cython 7 | from libc.math cimport ceil, floor, sqrt 8 | 9 | FTYPE = np.float32 10 | ctypedef np.float32_t FTYPE_t 11 | 12 | cdef FTYPE_t cround(FTYPE_t x): 13 | return ceil(x - 0.5) if x < 0. else floor(x + 0.5) 14 | 15 | @cython.boundscheck(False) # turn off bounds-checking for entire function 16 | def min_img_vec(np.ndarray[FTYPE_t, ndim=1] x, np.ndarray[FTYPE_t, ndim=1] y, np.ndarray[FTYPE_t, ndim=1] img, bint periodic=True): 17 | cdef np.ndarray[FTYPE_t, ndim=1] dx = np.empty(3, dtype=FTYPE) 18 | cdef int i 19 | for i in range(3): 20 | dx[i] = x[i] - y[i] 21 | if(periodic): 22 | dx[i] -= cround(dx[i] / img[i]) * img[i] 23 | return dx 24 | 25 | #put x into the same image as y 26 | @cython.boundscheck(False) # turn off bounds-checking for entire function 27 | def same_img(np.ndarray[FTYPE_t, ndim=1] x, np.ndarray[FTYPE_t, ndim=1] y, np.ndarray[FTYPE_t, ndim=1] img): 28 | cdef FTYPE_t dx 29 | cdef int i 30 | for i in range(3): 31 | dx = x[i] - y[i] 32 | x[i] -= cround(dx / img[i]) * img[i] 33 | return x 34 | 35 | @cython.boundscheck(False) # turn off bounds-checking for entire function 36 | def min_img(np.ndarray[FTYPE_t, ndim=1] x, np.ndarray[FTYPE_t, ndim=1] img, bint periodic=True): 37 | cdef int i 38 | for i in range(3): 39 | x[i] -= floor(x[i] / img[i]) * img[i] 40 | return x 41 | 42 | @cython.boundscheck(False) # turn off bounds-checking for entire function 43 | cpdef FTYPE_t min_img_dist_sq(np.ndarray[FTYPE_t, ndim=1] x, np.ndarray[FTYPE_t, ndim=1] y, np.ndarray[FTYPE_t, ndim=1] img, bint periodic=True): 44 | cdef FTYPE_t dx 45 | cdef FTYPE_t dist = 0 46 | cdef int i 47 | for i in range(3): 48 | dx = x[i] - y[i] 49 | if(periodic): 50 | dx -= cround(dx / img[i]) * img[i] 51 | dist += dx * dx 52 | return dist 53 | 54 | def min_img_dist(np.ndarray[FTYPE_t, ndim=1] x, np.ndarray[FTYPE_t, ndim=1] y, np.ndarray[FTYPE_t, ndim=1] img, bint periodic=True): 55 | return sqrt(min_img_dist_sq(x, y, img, periodic)) 56 | 57 | @cython.boundscheck(False) # turn off bounds-checking for entire function 58 | cpdef double norm3(np.ndarray[FTYPE_t, ndim=1] x): 59 | return sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]) 60 | 61 | def spec_force_inner_loop(np.ndarray[FTYPE_t, ndim=1] w, np.ndarray[FTYPE_t, ndim=1] basis_out, 62 | np.ndarray[FTYPE_t, ndim=2] grad, np.ndarray[FTYPE_t, ndim=1] force, 63 | np.ndarray[FTYPE_t, ndim=1] r): 64 | for i in range(w.shape[0]): 65 | for j in range(r.shape[0]): 66 | force[j] = force[j] + w[i] * basis_out[i] * r[j] 67 | grad[i,j] = basis_out[i] * r[j] + grad[i,j] 68 | -------------------------------------------------------------------------------- /ForcePy/__init__.py: -------------------------------------------------------------------------------- 1 | from ForcePy.ForceMatch import ForceMatch, Pairwise, Bond, Global 2 | from ForcePy.Analysis import RDF, CoordNumber 3 | from ForcePy.Forces import FileForce, XYZFileForce, AnalyticForce, SpectralForce, SmoothRegularizer, L2Regularizer, LJForce, HarmonicForce, FixedHarmonicForce 4 | import ForcePy.Mesh as Mesh 5 | from ForcePy.CGMap import CGUniverse, add_sequential_bonds, add_residue_bonds, write_structure, write_trajectory, write_lammps_data, add_residue_bonds_table 6 | import ForcePy.Basis 7 | import ForcePy.Analysis 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 2020 Update 2 | 3 | **This proejct is very dead. It has been superseded by better tools. For example, see our [latest work](https://github.com/ur-whitelab/hoomd-tf/)** 4 | 5 | Summary 6 | ======= 7 | 8 | Force-matching and utilities package. Supports input from Gromacs and Lammps. Not 9 | tested for input NAMD simulations. Outputs Lammps tabular potentials, input files, and plain text tabular potentials. 10 | Can be used independently for topology reduction in coarse-graining. 11 | 12 | ## License 13 | 14 | MIT 15 | 16 | Example 1: Coarse-graining a Trajectory of Water 17 | ========== 18 | 19 | The ForcePy module can be used to coarse-grained a trajectory. In this 20 | example, we'll convert an all-atom water simulation to a 2-site water 21 | model. 22 | 23 | The first step is to import the necessary libraries: 24 | 25 | ```python 26 | from MDAnalysis import Universe 27 | from ForcePy import * 28 | ``` 29 | 30 | Next, we load the fine-grained trajectory. The first file 31 | is the structure (`pdb`, `tpr`, `gro`, or `psf`) and the 32 | second file is the trajectory. Note that the `tpr` file reader depends highly 33 | on what version of gromacs with which the `tpr` file was created. See the 34 | [help page](https://code.google.com/p/mdanalysis/wiki/TPRReaderDevelopment) 35 | about the `tpr` file format support in MDAnalysis. The code to load the 36 | fine-grained trajectory is: 37 | 38 | ```python 39 | fine_uni = Universe("foo.tpr", "foo.trr") 40 | fine_uni.trajectory.periodic = True #NOTE: You MUST set this flag yourself 41 | ``` 42 | 43 | Now we create a coarse-grained trajectory using the fine-grained trajectory as 44 | an input: 45 | 46 | ```python 47 | coarse_uni =CGUniverse(fine_uni, selections=['name OW', 'name HW1 or name HW2'], 48 | names=['O', 'H2'], 49 | collapse_hydrogens=False) 50 | ``` 51 | 52 | The `selections` variable is an array of strings. Each string is a 53 | Charmm atom selection string. Note, these are very similar to VMD 54 | selection string. You may test them out using the following snippet:: 55 | 56 | ```python 57 | selected_atoms = fine_uni.selectAtoms('name OW') 58 | for a in selected_atoms: 59 | print a 60 | ``` 61 | 62 | In the example above water oxygen is the first string and water hydrogens 63 | are the second string. The next variable, `names`, is optional and is 64 | the array of names to be given to the selections. It is an array of 65 | strings the same length as `selections`. If `names` is not given, then 66 | the atoms in the resulting coarse-grained trajectory will have numbers 67 | as their names. The last variable, `collapse_hydrogens`, can be `True` 68 | or `False`. If it's `True`, then all hydrogens will automatically be 69 | included in neighboring atoms which are selected. So, for example, if 70 | you select a carbon, all its hydrogens will be included. Its default 71 | is `False`. 72 | 73 | Now that you have a coarse-grained trajectory, you may write out the 74 | structure or trajectory using the following syntax: 75 | 76 | ```python 77 | write_structure(coarse_uni, "cg_foo.pdb") 78 | write_structure(coarse_uni, "cg_foo.pdb", bonds='all') 79 | write_trajectory(coarse_uni, "cg_foo.dcd") 80 | ``` 81 | 82 | 83 | The coarse-grained trajectory is also a valid MDAnalysis object and 84 | you may perform any analysis techniques on it from the 85 | [MDAnalysis](https://code.google.com/p/mdanalysis/) package. 86 | 87 | Adding Bonds 88 | -------------------- 89 | 90 | Bonding information isn't always included in pdb files. To add a bond 91 | manually, use this line of code: 92 | 93 | ```python 94 | add_residue_bonds(coarse_uni, 'name O', 'name H2') 95 | ``` 96 | 97 | This will bond all atoms named `O` with all atoms named `H2` *within 98 | each residue*. To add bonds between residues, use this line of code: 99 | 100 | ```python 101 | add_sequential_bonds(coarse_uni, 'name C', 'name N') 102 | ``` 103 | 104 | The two selection strings will be the bonded parts of each residue. 105 | 106 | Example 2: Coarse-graining a Protein 107 | ================== 108 | 109 | Here are some examples of coarse-graining a protein with increasingly 110 | more coarse models 111 | 112 | 3-Beads per Residue 113 | ----------- 114 | 115 | ```python 116 | from MDAnalysis import Universe 117 | from ForcePy import * 118 | 119 | protein_file = 'foo.pdb' 120 | fine_uni = Universe(protein_file) 121 | 122 | cgu_1 = CGUniverse(fine_uni, ['name O or C', 'name N or name CA', '((not name C) and (not name O)) and ((not name n) and (not name CA))'], ['O', 'CA', 'S'], collapse_hydrogens=True) 123 | 124 | add_residue_bonds(cgu_1, 'name O', 'name CA') 125 | add_residue_bonds(cgu_1, 'name CA', 'name S') 126 | add_sequential_bonds(cgu_1, 'name CA', 'name CA') 127 | write_structure(cgu_1, 'cg_1.pdb', bonds='all') 128 | ``` 129 | 130 | The first bead is the carbonyl group, the second is the C-alpha and 131 | nitrogen, and the final is the side-chain. 132 | 133 | 1-Bead per Residue 134 | --------- 135 | 136 | ```python 137 | cgu_2 = CGUniverse(fine_uni, ['all']) 138 | add_sequential_bonds(cgu_2) 139 | write_structure(cgu_2, 'cgu_2.pdb', bonds='all') 140 | ``` 141 | 142 | This is much simpler. The names are omitted for the beads and it is 143 | not necessary to pass selection strings to `add_sequential_bonds()` 144 | since there is only one atom in each residue. 145 | 146 | 3-Residues per Bead 147 | -------------- 148 | 149 | Finally, here is how to put multiple residues into single beads. An 150 | array must be passed to the CGUniverse constructor which has a length 151 | of the desired number of beads and the array contains arrays of 152 | indices corresponding to the fine-grain residue indices. For example, 153 | to put residues `1,2,3` into a bead and residues `4,5` into another 154 | bead this array will accomplish that: `[[1,2,3], [4,5]]`. Here is a 155 | complete example of reducing every three residues into one bead: 156 | 157 | ```python 158 | protein_length = len(fine_uni.residues) 159 | reduction_map = [[3 * x, 3 * x + 1, 3 * x + 2] for x in range(protein_length / 3)] 160 | cgu_3 = CGUniverse(fine_uni, ['all'], 161 | residue_reduction_map=reduction_map) 162 | add_sequential_bonds(cgu_3) 163 | write_structure(cgu_3, 'cgu_3.pdb', bonds='all') 164 | ``` 165 | 166 | 167 | Force-Matching 168 | =================== 169 | Let's use again the example of 2-site water. If the original all-atom 170 | trajectory had forces, then we may use this file for force-matching. 171 | 172 | ```python 173 | from MDAnalysis import Universe 174 | from ForcePy import * 175 | import pickle 176 | 177 | fgu = Universe('topol.tpr', 'traj.trr') 178 | fgu.trajectory.periodic = True #NOTE: You MUST set this flag yourself, since there is no indication in the TPR files 179 | cgu = CGUniverse(fgu, ['name OW', 'name HW1 or name HW2'], ['O', 'H2'], False) 180 | add_residue_bonds(cgu, 'name O', 'name H2') 181 | fm = ForceMatch(cgu) 182 | ``` 183 | 184 | At this point, we have a `ForeMatch` object which contains the 185 | coarse-grained universe. Now we need to set-up the force field which 186 | will be force-matched to fit the coarse-grained trajectory forces 187 | (which themselves came from the all-atom trajectory). 188 | 189 | ```python 190 | ff = FileForce() #This just says the forces are found in the universe passed to the ForceMatch object 191 | #Set this force as the reference force to be matched 192 | fm.add_ref_force(ff) 193 | 194 | pair_mesh = Mesh.UniformMesh(0,12,0.05) #This is the mesh on which the force-field will be built. It is in Angstroms 195 | pairwise_force = SpectralForce(Pairwise, pair_mesh, Basis.UnitStep) 196 | #Copy this force type and clone it for each pair-interaction type 197 | fm.add_and_type_pair(pairwise_force) 198 | #This is a harmonic bond that will be fixed to the energy minimum of the bonds with a harmonic constant of 500 kJ/mol 199 | bond_force = FixedHarmonicForce(Bond, 500, cutoff=1) 200 | fm.add_and_type_pair(bond_force) 201 | ``` 202 | 203 | At this point, can now force match. To do it in serial: 204 | 205 | ```python 206 | fm.force_match() 207 | ``` 208 | 209 | You may also pass an `iterations` argument to use less than the entire 210 | trajectory. To do it in parallel (note you must have started using 211 | mpirun, mpiexec, or aprun depending on your MPI environment) 212 | 213 | ```python 214 | fm.force_match_mpi() 215 | ``` 216 | 217 | One thing about MPI runs is that it's much faster to pre-compute all 218 | the trajectory mapping at the beginning so that it isn't repeated on 219 | each node. This may be done by changing a line: 220 | 221 | ```python 222 | cgu = CGUniverse(fgu, ['name OW', 'name HW1 or name HW2'], ['O', 'H2'], False) 223 | #this will precompute the cg trajectory at every frame; this may take some time 224 | cgu = cgu.cache() 225 | ``` 226 | 227 | Finally, to write out the a set of lammps scripts to use the new force field, run 228 | 229 | ```python 230 | fm.write_lammps_scripts() 231 | ``` 232 | 233 | 234 | Installing ForcePy 235 | =============== 236 | 237 | Dependencies 238 | ---------- 239 | 240 | * Python2.7 241 | * scipy/numpy 242 | * MDAnalysis (development branch) 243 | 244 | Optional Dependencies 245 | ---------- 246 | 247 | * MPI4Py (for parallel force-matching) 248 | * matplotlib (for plotting) 249 | 250 | Install 251 | ---------- 252 | 253 | First install the development branch of MDAnalysis 254 | 255 | ```sh 256 | git clone https://code.google.com/p/mdanalysis/ mdanalysis 257 | cd mdanalysis 258 | git checkout develop 259 | cd package 260 | python setup.py install --user 261 | ``` 262 | 263 | Next install ForcePy 264 | 265 | ```sh 266 | cd ../../ 267 | git clone https://github.com/whitead/ForcePy.git ForcePy 268 | cd ForcePy 269 | python setup.py install --user 270 | ``` 271 | 272 | If you see a long list of errors, check the first few. If it says it 273 | can't find `arrayobject.h`, then your numpy headers are not being 274 | found. If you're in the Voth group and using the Enthought python 275 | distribution, try adding this line to your `~/.profile` or 276 | `~/.bash_profile` file: 277 | 278 | ```bash 279 | export C_INCLUDE_PATH=/opt/local/include:/Library/Frameworks/EPD64.framework/Versions/7.2/lib/python2.7/site-packages/numpy/core/include:$C_INCLUDE_PATH 280 | ``` 281 | 282 | 283 | Architecture Notes 284 | ================== 285 | 286 | The main class to utilize is a `ForceMatch` class. This class takes in 287 | one or more reference `Force` objects that define the forces to 288 | match. For example, a `FileForce` will read in forces from a file. The 289 | `ForceMatch` class also takes one or more target `Force` objects, 290 | which are the functional forms that are going to match the reference 291 | forces. Some `Force` objects contain a static class variable that 292 | points to a `ForceCategory` that contains useful 293 | methods/variables. For example, the `Pairwise` contains a 294 | neighborlist implementation. 295 | 296 | Regularizers may be added to force objects as well by calling the 297 | `add_regularizer` method. 298 | 299 | The `SpectralForce` is a linear combination of basis functions. This 300 | is usually a good choice. The `SpectralForce` requires a mesh and 301 | basis function. Currently only `UniformMesh` is implemented. For the 302 | basis functions, `UnitStep`, `Quartic`, and `Gaussian` are 303 | implemented. 304 | 305 | A given `Force` may be 'specialized' to work on only a certain type or 306 | type pair. This may be done by calling `specialize_type` before it is 307 | added to a `ForceMatch` class. 308 | 309 | In order to simplify constructing potentials for many type pairs, 310 | there are utility functions on the ForceMatch class to construct all 311 | possible pairs. `add_and_type_pairs` copies a force as many times as 312 | needed to have a unique force for every possible pair-pair 313 | interaction. 314 | 315 | 316 | Meshes 317 | ------------ 318 | * Uniform mesh 319 | 320 | Basis functions 321 | ------------ 322 | * UnitStep 323 | * Quartic 324 | * Gaussian 325 | 326 | Forces 327 | ------------ 328 | * FileForce 329 | * LammpsFileForce 330 | * SpectralPairwiseForce 331 | * AnalyticForce 332 | * LJForce 333 | * FixedHarmonicForce 334 | 335 | Regularizers 336 | ------------ 337 | * SmoothRegularizer 338 | * L2Regularizer 339 | 340 | -------------------------------------------------------------------------------- /distribute_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap distribute installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from distribute_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import sys 18 | import time 19 | import fnmatch 20 | import tempfile 21 | import tarfile 22 | from distutils import log 23 | 24 | try: 25 | from site import USER_SITE 26 | except ImportError: 27 | USER_SITE = None 28 | 29 | try: 30 | import subprocess 31 | 32 | def _python_cmd(*args): 33 | args = (sys.executable,) + args 34 | return subprocess.call(args) == 0 35 | 36 | except ImportError: 37 | # will be used for python 2.3 38 | def _python_cmd(*args): 39 | args = (sys.executable,) + args 40 | # quoting arguments if windows 41 | if sys.platform == 'win32': 42 | def quote(arg): 43 | if ' ' in arg: 44 | return '"%s"' % arg 45 | return arg 46 | args = [quote(arg) for arg in args] 47 | return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 48 | 49 | DEFAULT_VERSION = "0.6.14" 50 | DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" 51 | SETUPTOOLS_FAKED_VERSION = "0.6c11" 52 | 53 | SETUPTOOLS_PKG_INFO = """\ 54 | Metadata-Version: 1.0 55 | Name: setuptools 56 | Version: %s 57 | Summary: xxxx 58 | Home-page: xxx 59 | Author: xxx 60 | Author-email: xxx 61 | License: xxx 62 | Description: xxx 63 | """ % SETUPTOOLS_FAKED_VERSION 64 | 65 | 66 | def _install(tarball): 67 | # extracting the tarball 68 | tmpdir = tempfile.mkdtemp() 69 | log.warn('Extracting in %s', tmpdir) 70 | old_wd = os.getcwd() 71 | try: 72 | os.chdir(tmpdir) 73 | tar = tarfile.open(tarball) 74 | _extractall(tar) 75 | tar.close() 76 | 77 | # going in the directory 78 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 79 | os.chdir(subdir) 80 | log.warn('Now working in %s', subdir) 81 | 82 | # installing 83 | log.warn('Installing Distribute') 84 | if not _python_cmd('setup.py', 'install'): 85 | log.warn('Something went wrong during the installation.') 86 | log.warn('See the error message above.') 87 | finally: 88 | os.chdir(old_wd) 89 | 90 | 91 | def _build_egg(egg, tarball, to_dir): 92 | # extracting the tarball 93 | tmpdir = tempfile.mkdtemp() 94 | log.warn('Extracting in %s', tmpdir) 95 | old_wd = os.getcwd() 96 | try: 97 | os.chdir(tmpdir) 98 | tar = tarfile.open(tarball) 99 | _extractall(tar) 100 | tar.close() 101 | 102 | # going in the directory 103 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 104 | os.chdir(subdir) 105 | log.warn('Now working in %s', subdir) 106 | 107 | # building an egg 108 | log.warn('Building a Distribute egg in %s', to_dir) 109 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 110 | 111 | finally: 112 | os.chdir(old_wd) 113 | # returning the result 114 | log.warn(egg) 115 | if not os.path.exists(egg): 116 | raise IOError('Could not build the egg.') 117 | 118 | 119 | def _do_download(version, download_base, to_dir, download_delay): 120 | egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' 121 | % (version, sys.version_info[0], sys.version_info[1])) 122 | if not os.path.exists(egg): 123 | tarball = download_setuptools(version, download_base, 124 | to_dir, download_delay) 125 | _build_egg(egg, tarball, to_dir) 126 | sys.path.insert(0, egg) 127 | import setuptools 128 | setuptools.bootstrap_install_from = egg 129 | 130 | 131 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 132 | to_dir=os.curdir, download_delay=15, no_fake=True): 133 | # making sure we use the absolute path 134 | to_dir = os.path.abspath(to_dir) 135 | was_imported = 'pkg_resources' in sys.modules or \ 136 | 'setuptools' in sys.modules 137 | try: 138 | try: 139 | import pkg_resources 140 | if not hasattr(pkg_resources, '_distribute'): 141 | if not no_fake: 142 | _fake_setuptools() 143 | raise ImportError 144 | except ImportError: 145 | return _do_download(version, download_base, to_dir, download_delay) 146 | try: 147 | pkg_resources.require("distribute>="+version) 148 | return 149 | except pkg_resources.VersionConflict: 150 | e = sys.exc_info()[1] 151 | if was_imported: 152 | sys.stderr.write( 153 | "The required version of distribute (>=%s) is not available,\n" 154 | "and can't be installed while this script is running. Please\n" 155 | "install a more recent version first, using\n" 156 | "'easy_install -U distribute'." 157 | "\n\n(Currently using %r)\n" % (version, e.args[0])) 158 | sys.exit(2) 159 | else: 160 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 161 | return _do_download(version, download_base, to_dir, 162 | download_delay) 163 | except pkg_resources.DistributionNotFound: 164 | return _do_download(version, download_base, to_dir, 165 | download_delay) 166 | finally: 167 | if not no_fake: 168 | _create_fake_setuptools_pkg_info(to_dir) 169 | 170 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 171 | to_dir=os.curdir, delay=15): 172 | """Download distribute from a specified location and return its filename 173 | 174 | `version` should be a valid distribute version number that is available 175 | as an egg for download under the `download_base` URL (which should end 176 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 177 | `delay` is the number of seconds to pause before an actual download 178 | attempt. 179 | """ 180 | # making sure we use the absolute path 181 | to_dir = os.path.abspath(to_dir) 182 | try: 183 | from urllib.request import urlopen 184 | except ImportError: 185 | from urllib2 import urlopen 186 | tgz_name = "distribute-%s.tar.gz" % version 187 | url = download_base + tgz_name 188 | saveto = os.path.join(to_dir, tgz_name) 189 | src = dst = None 190 | if not os.path.exists(saveto): # Avoid repeated downloads 191 | try: 192 | log.warn("Downloading %s", url) 193 | src = urlopen(url) 194 | # Read/write all in one block, so we don't create a corrupt file 195 | # if the download is interrupted. 196 | data = src.read() 197 | dst = open(saveto, "wb") 198 | dst.write(data) 199 | finally: 200 | if src: 201 | src.close() 202 | if dst: 203 | dst.close() 204 | return os.path.realpath(saveto) 205 | 206 | def _no_sandbox(function): 207 | def __no_sandbox(*args, **kw): 208 | try: 209 | from setuptools.sandbox import DirectorySandbox 210 | if not hasattr(DirectorySandbox, '_old'): 211 | def violation(*args): 212 | pass 213 | DirectorySandbox._old = DirectorySandbox._violation 214 | DirectorySandbox._violation = violation 215 | patched = True 216 | else: 217 | patched = False 218 | except ImportError: 219 | patched = False 220 | 221 | try: 222 | return function(*args, **kw) 223 | finally: 224 | if patched: 225 | DirectorySandbox._violation = DirectorySandbox._old 226 | del DirectorySandbox._old 227 | 228 | return __no_sandbox 229 | 230 | def _patch_file(path, content): 231 | """Will backup the file then patch it""" 232 | existing_content = open(path).read() 233 | if existing_content == content: 234 | # already patched 235 | log.warn('Already patched.') 236 | return False 237 | log.warn('Patching...') 238 | _rename_path(path) 239 | f = open(path, 'w') 240 | try: 241 | f.write(content) 242 | finally: 243 | f.close() 244 | return True 245 | 246 | _patch_file = _no_sandbox(_patch_file) 247 | 248 | def _same_content(path, content): 249 | return open(path).read() == content 250 | 251 | def _rename_path(path): 252 | new_name = path + '.OLD.%s' % time.time() 253 | log.warn('Renaming %s into %s', path, new_name) 254 | os.rename(path, new_name) 255 | return new_name 256 | 257 | def _remove_flat_installation(placeholder): 258 | if not os.path.isdir(placeholder): 259 | log.warn('Unkown installation at %s', placeholder) 260 | return False 261 | found = False 262 | for file in os.listdir(placeholder): 263 | if fnmatch.fnmatch(file, 'setuptools*.egg-info'): 264 | found = True 265 | break 266 | if not found: 267 | log.warn('Could not locate setuptools*.egg-info') 268 | return 269 | 270 | log.warn('Removing elements out of the way...') 271 | pkg_info = os.path.join(placeholder, file) 272 | if os.path.isdir(pkg_info): 273 | patched = _patch_egg_dir(pkg_info) 274 | else: 275 | patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) 276 | 277 | if not patched: 278 | log.warn('%s already patched.', pkg_info) 279 | return False 280 | # now let's move the files out of the way 281 | for element in ('setuptools', 'pkg_resources.py', 'site.py'): 282 | element = os.path.join(placeholder, element) 283 | if os.path.exists(element): 284 | _rename_path(element) 285 | else: 286 | log.warn('Could not find the %s element of the ' 287 | 'Setuptools distribution', element) 288 | return True 289 | 290 | _remove_flat_installation = _no_sandbox(_remove_flat_installation) 291 | 292 | def _after_install(dist): 293 | log.warn('After install bootstrap.') 294 | placeholder = dist.get_command_obj('install').install_purelib 295 | _create_fake_setuptools_pkg_info(placeholder) 296 | 297 | def _create_fake_setuptools_pkg_info(placeholder): 298 | if not placeholder or not os.path.exists(placeholder): 299 | log.warn('Could not find the install location') 300 | return 301 | pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) 302 | setuptools_file = 'setuptools-%s-py%s.egg-info' % \ 303 | (SETUPTOOLS_FAKED_VERSION, pyver) 304 | pkg_info = os.path.join(placeholder, setuptools_file) 305 | if os.path.exists(pkg_info): 306 | log.warn('%s already exists', pkg_info) 307 | return 308 | 309 | log.warn('Creating %s', pkg_info) 310 | f = open(pkg_info, 'w') 311 | try: 312 | f.write(SETUPTOOLS_PKG_INFO) 313 | finally: 314 | f.close() 315 | 316 | pth_file = os.path.join(placeholder, 'setuptools.pth') 317 | log.warn('Creating %s', pth_file) 318 | f = open(pth_file, 'w') 319 | try: 320 | f.write(os.path.join(os.curdir, setuptools_file)) 321 | finally: 322 | f.close() 323 | 324 | _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) 325 | 326 | def _patch_egg_dir(path): 327 | # let's check if it's already patched 328 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 329 | if os.path.exists(pkg_info): 330 | if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): 331 | log.warn('%s already patched.', pkg_info) 332 | return False 333 | _rename_path(path) 334 | os.mkdir(path) 335 | os.mkdir(os.path.join(path, 'EGG-INFO')) 336 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 337 | f = open(pkg_info, 'w') 338 | try: 339 | f.write(SETUPTOOLS_PKG_INFO) 340 | finally: 341 | f.close() 342 | return True 343 | 344 | _patch_egg_dir = _no_sandbox(_patch_egg_dir) 345 | 346 | def _before_install(): 347 | log.warn('Before install bootstrap.') 348 | _fake_setuptools() 349 | 350 | 351 | def _under_prefix(location): 352 | if 'install' not in sys.argv: 353 | return True 354 | args = sys.argv[sys.argv.index('install')+1:] 355 | for index, arg in enumerate(args): 356 | for option in ('--root', '--prefix'): 357 | if arg.startswith('%s=' % option): 358 | top_dir = arg.split('root=')[-1] 359 | return location.startswith(top_dir) 360 | elif arg == option: 361 | if len(args) > index: 362 | top_dir = args[index+1] 363 | return location.startswith(top_dir) 364 | if arg == '--user' and USER_SITE is not None: 365 | return location.startswith(USER_SITE) 366 | return True 367 | 368 | 369 | def _fake_setuptools(): 370 | log.warn('Scanning installed packages') 371 | try: 372 | import pkg_resources 373 | except ImportError: 374 | # we're cool 375 | log.warn('Setuptools or Distribute does not seem to be installed.') 376 | return 377 | ws = pkg_resources.working_set 378 | try: 379 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', 380 | replacement=False)) 381 | except TypeError: 382 | # old distribute API 383 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) 384 | 385 | if setuptools_dist is None: 386 | log.warn('No setuptools distribution found') 387 | return 388 | # detecting if it was already faked 389 | setuptools_location = setuptools_dist.location 390 | log.warn('Setuptools installation detected at %s', setuptools_location) 391 | 392 | # if --root or --preix was provided, and if 393 | # setuptools is not located in them, we don't patch it 394 | if not _under_prefix(setuptools_location): 395 | log.warn('Not patching, --root or --prefix is installing Distribute' 396 | ' in another location') 397 | return 398 | 399 | # let's see if its an egg 400 | if not setuptools_location.endswith('.egg'): 401 | log.warn('Non-egg installation') 402 | res = _remove_flat_installation(setuptools_location) 403 | if not res: 404 | return 405 | else: 406 | log.warn('Egg installation') 407 | pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') 408 | if (os.path.exists(pkg_info) and 409 | _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): 410 | log.warn('Already patched.') 411 | return 412 | log.warn('Patching...') 413 | # let's create a fake egg replacing setuptools one 414 | res = _patch_egg_dir(setuptools_location) 415 | if not res: 416 | return 417 | log.warn('Patched done.') 418 | _relaunch() 419 | 420 | 421 | def _relaunch(): 422 | log.warn('Relaunching...') 423 | # we have to relaunch the process 424 | # pip marker to avoid a relaunch bug 425 | if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: 426 | sys.argv[0] = 'setup.py' 427 | args = [sys.executable] + sys.argv 428 | sys.exit(subprocess.call(args)) 429 | 430 | 431 | def _extractall(self, path=".", members=None): 432 | """Extract all members from the archive to the current working 433 | directory and set owner, modification time and permissions on 434 | directories afterwards. `path' specifies a different directory 435 | to extract to. `members' is optional and must be a subset of the 436 | list returned by getmembers(). 437 | """ 438 | import copy 439 | import operator 440 | from tarfile import ExtractError 441 | directories = [] 442 | 443 | if members is None: 444 | members = self 445 | 446 | for tarinfo in members: 447 | if tarinfo.isdir(): 448 | # Extract directories with a safe mode. 449 | directories.append(tarinfo) 450 | tarinfo = copy.copy(tarinfo) 451 | tarinfo.mode = 448 # decimal for oct 0700 452 | self.extract(tarinfo, path) 453 | 454 | # Reverse sort directories. 455 | if sys.version_info < (2, 4): 456 | def sorter(dir1, dir2): 457 | return cmp(dir1.name, dir2.name) 458 | directories.sort(sorter) 459 | directories.reverse() 460 | else: 461 | directories.sort(key=operator.attrgetter('name'), reverse=True) 462 | 463 | # Set correct owner, mtime and filemode on directories. 464 | for tarinfo in directories: 465 | dirpath = os.path.join(path, tarinfo.name) 466 | try: 467 | self.chown(tarinfo, dirpath) 468 | self.utime(tarinfo, dirpath) 469 | self.chmod(tarinfo, dirpath) 470 | except ExtractError: 471 | e = sys.exc_info()[1] 472 | if self.errorlevel > 1: 473 | raise 474 | else: 475 | self._dbg(1, "tarfile: %s" % e) 476 | 477 | 478 | def main(argv, version=DEFAULT_VERSION): 479 | """Install or upgrade setuptools and EasyInstall""" 480 | tarball = download_setuptools() 481 | _install(tarball) 482 | 483 | 484 | if __name__ == '__main__': 485 | main(sys.argv[1:]) 486 | -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap distribute installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from distribute_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import sys 18 | import time 19 | import fnmatch 20 | import tempfile 21 | import tarfile 22 | from distutils import log 23 | 24 | try: 25 | from site import USER_SITE 26 | except ImportError: 27 | USER_SITE = None 28 | 29 | try: 30 | import subprocess 31 | 32 | def _python_cmd(*args): 33 | args = (sys.executable,) + args 34 | return subprocess.call(args) == 0 35 | 36 | except ImportError: 37 | # will be used for python 2.3 38 | def _python_cmd(*args): 39 | args = (sys.executable,) + args 40 | # quoting arguments if windows 41 | if sys.platform == 'win32': 42 | def quote(arg): 43 | if ' ' in arg: 44 | return '"%s"' % arg 45 | return arg 46 | args = [quote(arg) for arg in args] 47 | return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 48 | 49 | DEFAULT_VERSION = "0.6.14" 50 | DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" 51 | SETUPTOOLS_FAKED_VERSION = "0.6c11" 52 | 53 | SETUPTOOLS_PKG_INFO = """\ 54 | Metadata-Version: 1.0 55 | Name: setuptools 56 | Version: %s 57 | Summary: xxxx 58 | Home-page: xxx 59 | Author: xxx 60 | Author-email: xxx 61 | License: xxx 62 | Description: xxx 63 | """ % SETUPTOOLS_FAKED_VERSION 64 | 65 | 66 | def _install(tarball): 67 | # extracting the tarball 68 | tmpdir = tempfile.mkdtemp() 69 | log.warn('Extracting in %s', tmpdir) 70 | old_wd = os.getcwd() 71 | try: 72 | os.chdir(tmpdir) 73 | tar = tarfile.open(tarball) 74 | _extractall(tar) 75 | tar.close() 76 | 77 | # going in the directory 78 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 79 | os.chdir(subdir) 80 | log.warn('Now working in %s', subdir) 81 | 82 | # installing 83 | log.warn('Installing Distribute') 84 | if not _python_cmd('setup.py', 'install'): 85 | log.warn('Something went wrong during the installation.') 86 | log.warn('See the error message above.') 87 | finally: 88 | os.chdir(old_wd) 89 | 90 | 91 | def _build_egg(egg, tarball, to_dir): 92 | # extracting the tarball 93 | tmpdir = tempfile.mkdtemp() 94 | log.warn('Extracting in %s', tmpdir) 95 | old_wd = os.getcwd() 96 | try: 97 | os.chdir(tmpdir) 98 | tar = tarfile.open(tarball) 99 | _extractall(tar) 100 | tar.close() 101 | 102 | # going in the directory 103 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 104 | os.chdir(subdir) 105 | log.warn('Now working in %s', subdir) 106 | 107 | # building an egg 108 | log.warn('Building a Distribute egg in %s', to_dir) 109 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 110 | 111 | finally: 112 | os.chdir(old_wd) 113 | # returning the result 114 | log.warn(egg) 115 | if not os.path.exists(egg): 116 | raise IOError('Could not build the egg.') 117 | 118 | 119 | def _do_download(version, download_base, to_dir, download_delay): 120 | egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' 121 | % (version, sys.version_info[0], sys.version_info[1])) 122 | if not os.path.exists(egg): 123 | tarball = download_setuptools(version, download_base, 124 | to_dir, download_delay) 125 | _build_egg(egg, tarball, to_dir) 126 | sys.path.insert(0, egg) 127 | import setuptools 128 | setuptools.bootstrap_install_from = egg 129 | 130 | 131 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 132 | to_dir=os.curdir, download_delay=15, no_fake=True): 133 | # making sure we use the absolute path 134 | to_dir = os.path.abspath(to_dir) 135 | was_imported = 'pkg_resources' in sys.modules or \ 136 | 'setuptools' in sys.modules 137 | try: 138 | try: 139 | import pkg_resources 140 | if not hasattr(pkg_resources, '_distribute'): 141 | if not no_fake: 142 | _fake_setuptools() 143 | raise ImportError 144 | except ImportError: 145 | return _do_download(version, download_base, to_dir, download_delay) 146 | try: 147 | pkg_resources.require("distribute>="+version) 148 | return 149 | except pkg_resources.VersionConflict: 150 | e = sys.exc_info()[1] 151 | if was_imported: 152 | sys.stderr.write( 153 | "The required version of distribute (>=%s) is not available,\n" 154 | "and can't be installed while this script is running. Please\n" 155 | "install a more recent version first, using\n" 156 | "'easy_install -U distribute'." 157 | "\n\n(Currently using %r)\n" % (version, e.args[0])) 158 | sys.exit(2) 159 | else: 160 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 161 | return _do_download(version, download_base, to_dir, 162 | download_delay) 163 | except pkg_resources.DistributionNotFound: 164 | return _do_download(version, download_base, to_dir, 165 | download_delay) 166 | finally: 167 | if not no_fake: 168 | _create_fake_setuptools_pkg_info(to_dir) 169 | 170 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 171 | to_dir=os.curdir, delay=15): 172 | """Download distribute from a specified location and return its filename 173 | 174 | `version` should be a valid distribute version number that is available 175 | as an egg for download under the `download_base` URL (which should end 176 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 177 | `delay` is the number of seconds to pause before an actual download 178 | attempt. 179 | """ 180 | # making sure we use the absolute path 181 | to_dir = os.path.abspath(to_dir) 182 | try: 183 | from urllib.request import urlopen 184 | except ImportError: 185 | from urllib2 import urlopen 186 | tgz_name = "distribute-%s.tar.gz" % version 187 | url = download_base + tgz_name 188 | saveto = os.path.join(to_dir, tgz_name) 189 | src = dst = None 190 | if not os.path.exists(saveto): # Avoid repeated downloads 191 | try: 192 | log.warn("Downloading %s", url) 193 | src = urlopen(url) 194 | # Read/write all in one block, so we don't create a corrupt file 195 | # if the download is interrupted. 196 | data = src.read() 197 | dst = open(saveto, "wb") 198 | dst.write(data) 199 | finally: 200 | if src: 201 | src.close() 202 | if dst: 203 | dst.close() 204 | return os.path.realpath(saveto) 205 | 206 | def _no_sandbox(function): 207 | def __no_sandbox(*args, **kw): 208 | try: 209 | from setuptools.sandbox import DirectorySandbox 210 | if not hasattr(DirectorySandbox, '_old'): 211 | def violation(*args): 212 | pass 213 | DirectorySandbox._old = DirectorySandbox._violation 214 | DirectorySandbox._violation = violation 215 | patched = True 216 | else: 217 | patched = False 218 | except ImportError: 219 | patched = False 220 | 221 | try: 222 | return function(*args, **kw) 223 | finally: 224 | if patched: 225 | DirectorySandbox._violation = DirectorySandbox._old 226 | del DirectorySandbox._old 227 | 228 | return __no_sandbox 229 | 230 | def _patch_file(path, content): 231 | """Will backup the file then patch it""" 232 | existing_content = open(path).read() 233 | if existing_content == content: 234 | # already patched 235 | log.warn('Already patched.') 236 | return False 237 | log.warn('Patching...') 238 | _rename_path(path) 239 | f = open(path, 'w') 240 | try: 241 | f.write(content) 242 | finally: 243 | f.close() 244 | return True 245 | 246 | _patch_file = _no_sandbox(_patch_file) 247 | 248 | def _same_content(path, content): 249 | return open(path).read() == content 250 | 251 | def _rename_path(path): 252 | new_name = path + '.OLD.%s' % time.time() 253 | log.warn('Renaming %s into %s', path, new_name) 254 | os.rename(path, new_name) 255 | return new_name 256 | 257 | def _remove_flat_installation(placeholder): 258 | if not os.path.isdir(placeholder): 259 | log.warn('Unkown installation at %s', placeholder) 260 | return False 261 | found = False 262 | for file in os.listdir(placeholder): 263 | if fnmatch.fnmatch(file, 'setuptools*.egg-info'): 264 | found = True 265 | break 266 | if not found: 267 | log.warn('Could not locate setuptools*.egg-info') 268 | return 269 | 270 | log.warn('Removing elements out of the way...') 271 | pkg_info = os.path.join(placeholder, file) 272 | if os.path.isdir(pkg_info): 273 | patched = _patch_egg_dir(pkg_info) 274 | else: 275 | patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) 276 | 277 | if not patched: 278 | log.warn('%s already patched.', pkg_info) 279 | return False 280 | # now let's move the files out of the way 281 | for element in ('setuptools', 'pkg_resources.py', 'site.py'): 282 | element = os.path.join(placeholder, element) 283 | if os.path.exists(element): 284 | _rename_path(element) 285 | else: 286 | log.warn('Could not find the %s element of the ' 287 | 'Setuptools distribution', element) 288 | return True 289 | 290 | _remove_flat_installation = _no_sandbox(_remove_flat_installation) 291 | 292 | def _after_install(dist): 293 | log.warn('After install bootstrap.') 294 | placeholder = dist.get_command_obj('install').install_purelib 295 | _create_fake_setuptools_pkg_info(placeholder) 296 | 297 | def _create_fake_setuptools_pkg_info(placeholder): 298 | if not placeholder or not os.path.exists(placeholder): 299 | log.warn('Could not find the install location') 300 | return 301 | pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) 302 | setuptools_file = 'setuptools-%s-py%s.egg-info' % \ 303 | (SETUPTOOLS_FAKED_VERSION, pyver) 304 | pkg_info = os.path.join(placeholder, setuptools_file) 305 | if os.path.exists(pkg_info): 306 | log.warn('%s already exists', pkg_info) 307 | return 308 | 309 | log.warn('Creating %s', pkg_info) 310 | f = open(pkg_info, 'w') 311 | try: 312 | f.write(SETUPTOOLS_PKG_INFO) 313 | finally: 314 | f.close() 315 | 316 | pth_file = os.path.join(placeholder, 'setuptools.pth') 317 | log.warn('Creating %s', pth_file) 318 | f = open(pth_file, 'w') 319 | try: 320 | f.write(os.path.join(os.curdir, setuptools_file)) 321 | finally: 322 | f.close() 323 | 324 | _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) 325 | 326 | def _patch_egg_dir(path): 327 | # let's check if it's already patched 328 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 329 | if os.path.exists(pkg_info): 330 | if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): 331 | log.warn('%s already patched.', pkg_info) 332 | return False 333 | _rename_path(path) 334 | os.mkdir(path) 335 | os.mkdir(os.path.join(path, 'EGG-INFO')) 336 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 337 | f = open(pkg_info, 'w') 338 | try: 339 | f.write(SETUPTOOLS_PKG_INFO) 340 | finally: 341 | f.close() 342 | return True 343 | 344 | _patch_egg_dir = _no_sandbox(_patch_egg_dir) 345 | 346 | def _before_install(): 347 | log.warn('Before install bootstrap.') 348 | _fake_setuptools() 349 | 350 | 351 | def _under_prefix(location): 352 | if 'install' not in sys.argv: 353 | return True 354 | args = sys.argv[sys.argv.index('install')+1:] 355 | for index, arg in enumerate(args): 356 | for option in ('--root', '--prefix'): 357 | if arg.startswith('%s=' % option): 358 | top_dir = arg.split('root=')[-1] 359 | return location.startswith(top_dir) 360 | elif arg == option: 361 | if len(args) > index: 362 | top_dir = args[index+1] 363 | return location.startswith(top_dir) 364 | if arg == '--user' and USER_SITE is not None: 365 | return location.startswith(USER_SITE) 366 | return True 367 | 368 | 369 | def _fake_setuptools(): 370 | log.warn('Scanning installed packages') 371 | try: 372 | import pkg_resources 373 | except ImportError: 374 | # we're cool 375 | log.warn('Setuptools or Distribute does not seem to be installed.') 376 | return 377 | ws = pkg_resources.working_set 378 | try: 379 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', 380 | replacement=False)) 381 | except TypeError: 382 | # old distribute API 383 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) 384 | 385 | if setuptools_dist is None: 386 | log.warn('No setuptools distribution found') 387 | return 388 | # detecting if it was already faked 389 | setuptools_location = setuptools_dist.location 390 | log.warn('Setuptools installation detected at %s', setuptools_location) 391 | 392 | # if --root or --preix was provided, and if 393 | # setuptools is not located in them, we don't patch it 394 | if not _under_prefix(setuptools_location): 395 | log.warn('Not patching, --root or --prefix is installing Distribute' 396 | ' in another location') 397 | return 398 | 399 | # let's see if its an egg 400 | if not setuptools_location.endswith('.egg'): 401 | log.warn('Non-egg installation') 402 | res = _remove_flat_installation(setuptools_location) 403 | if not res: 404 | return 405 | else: 406 | log.warn('Egg installation') 407 | pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') 408 | if (os.path.exists(pkg_info) and 409 | _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): 410 | log.warn('Already patched.') 411 | return 412 | log.warn('Patching...') 413 | # let's create a fake egg replacing setuptools one 414 | res = _patch_egg_dir(setuptools_location) 415 | if not res: 416 | return 417 | log.warn('Patched done.') 418 | _relaunch() 419 | 420 | 421 | def _relaunch(): 422 | log.warn('Relaunching...') 423 | # we have to relaunch the process 424 | # pip marker to avoid a relaunch bug 425 | if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: 426 | sys.argv[0] = 'setup.py' 427 | args = [sys.executable] + sys.argv 428 | sys.exit(subprocess.call(args)) 429 | 430 | 431 | def _extractall(self, path=".", members=None): 432 | """Extract all members from the archive to the current working 433 | directory and set owner, modification time and permissions on 434 | directories afterwards. `path' specifies a different directory 435 | to extract to. `members' is optional and must be a subset of the 436 | list returned by getmembers(). 437 | """ 438 | import copy 439 | import operator 440 | from tarfile import ExtractError 441 | directories = [] 442 | 443 | if members is None: 444 | members = self 445 | 446 | for tarinfo in members: 447 | if tarinfo.isdir(): 448 | # Extract directories with a safe mode. 449 | directories.append(tarinfo) 450 | tarinfo = copy.copy(tarinfo) 451 | tarinfo.mode = 448 # decimal for oct 0700 452 | self.extract(tarinfo, path) 453 | 454 | # Reverse sort directories. 455 | if sys.version_info < (2, 4): 456 | def sorter(dir1, dir2): 457 | return cmp(dir1.name, dir2.name) 458 | directories.sort(sorter) 459 | directories.reverse() 460 | else: 461 | directories.sort(key=operator.attrgetter('name'), reverse=True) 462 | 463 | # Set correct owner, mtime and filemode on directories. 464 | for tarinfo in directories: 465 | dirpath = os.path.join(path, tarinfo.name) 466 | try: 467 | self.chown(tarinfo, dirpath) 468 | self.utime(tarinfo, dirpath) 469 | self.chmod(tarinfo, dirpath) 470 | except ExtractError: 471 | e = sys.exc_info()[1] 472 | if self.errorlevel > 1: 473 | raise 474 | else: 475 | self._dbg(1, "tarfile: %s" % e) 476 | 477 | 478 | def main(argv, version=DEFAULT_VERSION): 479 | """Install or upgrade setuptools and EasyInstall""" 480 | tarball = download_setuptools() 481 | _install(tarball) 482 | 483 | 484 | if __name__ == '__main__': 485 | main(sys.argv[1:]) 486 | -------------------------------------------------------------------------------- /scripts/convert_lammps_tables.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This script is used for multiplying already written tabular forces by a conversion factor. 4 | # It relies on the plot_forces.py script. Execute with -h flag for usage information. 5 | # 6 | # 7 | # 8 | 9 | from plot_list import * 10 | import argparse, os 11 | 12 | parser = argparse.ArgumentParser(description='Given a directory which is the result of a ForcePy run and a new directory name, this script will convert the force tables by the given floating point conversion factors') 13 | parser.add_argument('input_directory') 14 | parser.add_argument('output_directory') 15 | parser.add_argument('-dist_convert', type=float, default=1.0) 16 | parser.add_argument('-force_convert', type=float, default=1.0) 17 | parser.add_argument('-energy_convert', type=float, default=1.0) 18 | 19 | pargs = parser.parse_args() 20 | 21 | 22 | #check directory 23 | if(not os.path.exists(pargs.input_directory)): 24 | raise RuntimeError('Could not locate input_directory {}'.format(pargs.input_directory)) 25 | 26 | #prepare output 27 | if(not os.path.exists(pargs.output_directory)): 28 | os.mkdir(pargs.output_directory) 29 | 30 | for k,v in pot_types.iteritems(): 31 | input = os.path.join(pargs.input_directory, v) 32 | output = os.path.join(pargs.output_directory, v) 33 | with open(output, 'w') as outf: 34 | with open(input, 'r') as inf: 35 | for matrix,name, lammps_header in gen_tables(inf, return_extra=True): 36 | write_table(outf, matrix, name, lammps_header, (pargs.dist_convert, pargs.force_convert, pargs.energy_convert)) 37 | 38 | -------------------------------------------------------------------------------- /scripts/plot_forces.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This script will plot a series of force-matched potentials if they are written out as lammps directores. 4 | # Here's an example of how to generate the directories: 5 | # for i in range(10): 6 | # fm.force_match() 7 | # fm.write_lammps_directory(folder='batch_%d' % i) 8 | # 9 | # A plot may be generated by running plot_forces.py "baatch_" from the directory containing the batch directories. 10 | # This code requires matplotlib. 11 | # 12 | # 13 | # 14 | # 15 | # 16 | 17 | import os 18 | import numpy as np 19 | import matplotlib.pyplot as plt 20 | 21 | pot_types = {'bond': 'cg_force_bond.table', 22 | 'pair': 'cg_force_pair.table'} 23 | 24 | def remaining_file_len(f): 25 | pos = f.tell() 26 | i = 0 27 | for l in f: 28 | i += 1 29 | f.seek(pos) 30 | return i 31 | 32 | def count_remaining_tables(file, table_points=10000, header_length=5, footer_length=2): 33 | count = remaining_file_len(file) / (header_length + footer_length + table_points) 34 | assert count * (header_length + footer_length + table_points) == remaining_file_len(file) 35 | return count 36 | 37 | def walk_files(pot_type, dir_prefix="batch_"): 38 | if not pot_type in pot_types: 39 | print '{} is not a valid force type.'.format(pot_type) 40 | 41 | i = 0 42 | while True: 43 | pass_dir = '{}{}'.format(dir_prefix, i) 44 | if not os.path.exists(pass_dir): 45 | break 46 | yield open(os.path.join(pass_dir, pot_types[pot_type])) 47 | i += 1 48 | 49 | def consume_lines(f,l): 50 | for i in range(l): 51 | f.readline() 52 | 53 | 54 | def gen_tables(files, index = -1, table_points=10000, header_length=5, footer_length=2, columns=3, return_extra=False): 55 | if(type(files) == file): 56 | files = [files] 57 | for f in files: 58 | f.seek(0) 59 | if index >= count_remaining_tables(f, table_points, header_length, footer_length): 60 | raise IOError('Not enough tables in {}'.format(index)) 61 | for tindex in range(count_remaining_tables(f, table_points, header_length, footer_length)): 62 | if(tindex == index or index == -1): 63 | matrix = np.empty( (table_points, columns) ) 64 | 65 | #THIS STUFF HERE IS NOT KINDA LAZY. SHOULD ACTUALLY INFER THIS STUFF 66 | name = f.readline().split('#')[1] 67 | f.readline() 68 | lammps_header = ''.join([f.readline() for x in range(2)]) 69 | f.readline() 70 | 71 | for i in range(table_points): 72 | sline = f.readline().split() 73 | matrix[i,] = [float(x) for x in sline[1:]] 74 | consume_lines(f, footer_length) 75 | if(return_extra): 76 | yield matrix, name, lammps_header 77 | else: 78 | yield matrix 79 | else: 80 | consume_lines(f, table_points + header_length + footer_length) 81 | 82 | def write_table(f, matrix, name, lammps_header, conversion_factors): 83 | f.write('#{}\n'.format(name)) 84 | f.write(lammps_header) 85 | f.write('\n') 86 | for i,v in enumerate(matrix): 87 | f.write('{}'.format(i)) 88 | for conversion, vi in zip(v, conversion_factors): 89 | f.write(' {}'.format(conversion * vi)) 90 | f.write('\n') 91 | f.write('\n\n') 92 | 93 | def plot_tables(ms, N=15, plot_column=1, cmap='RdBu'): 94 | color = plt.get_cmap(cmap, N) 95 | index = 0 96 | for m in ms: 97 | plt.plot(m[:,0], m[:,plot_column], color=color(index)) 98 | plt.axis([0, max(m[:,0]), -20, 20]) 99 | #plt.axis([0, max(m[:,0]), -np.amax(m[:,plot_column]), np.amax(m[:,plot_column])]) 100 | index += 1 101 | yield plt 102 | 103 | 104 | if __name__ == "__main__": 105 | 106 | import argparse 107 | parser = argparse.ArgumentParser(description="Plot multiple ForcePy force-mathcing lammps directories over time") 108 | parser.add_argument('directory_prefix', help='directories containing lammps tables should be prefix0 prefix1 prefix2... etc. Note that the first directory must be prefix0') 109 | parser.add_argument('-table_points', type=int, default=10000) 110 | pargs = parser.parse_args() 111 | prefix = pargs.directory_prefix 112 | 113 | 114 | for k in pot_types.iterkeys(): 115 | ntables = max((count_remaining_tables(x, table_points=pargs.table_points) for x in walk_files(k, prefix))) 116 | passes = sum(1 for x in walk_files(k,prefix)) 117 | print 'There are {} potentials for the {} type which were refined {} times'.format(ntables, k, passes) 118 | for i in range(ntables): 119 | plt.figure(figsize=(16,9)) 120 | for p in plot_tables(gen_tables(walk_files(k, prefix), i, table_points=pargs.table_points), N=passes): 121 | pass 122 | p.savefig('{}_potential_{}.pdf'.format(k, i), bbox_inches=0) 123 | 124 | plt.figure(figsize=(16,9)) 125 | for p in plot_tables(gen_tables(walk_files(k, prefix), i, table_points=pargs.table_points), N=passes, plot_column=2): 126 | pass 127 | p.savefig('{}_force_{}.pdf'.format(k, i), bbox_inches=0) 128 | 129 | 130 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | """Setuptools-based setup script for ForcePy. 3 | 4 | A working installation of NumPy is required. 5 | 6 | For a basic installation just type the command:: 7 | 8 | python setup.py install 9 | 10 | This script was adapted from MDAnalysis setup.py by Naveen Michaud-Agrawal. 11 | """ 12 | from setuptools import setup, Extension 13 | from distutils.ccompiler import new_compiler 14 | # 15 | #------------------------------------------------------------ 16 | 17 | import sys, os 18 | import glob 19 | 20 | # Make sure I have the right Python version. 21 | if sys.version_info[:2] < (2, 5): 22 | print "ForcePy requires Python 2.5 or better. Python %d.%d detected" % \ 23 | sys.version_info[:2] 24 | print "Please upgrade your version of Python." 25 | sys.exit(-1) 26 | 27 | try: 28 | # Obtain the numpy include directory. This logic works across numpy versions. 29 | import numpy 30 | except ImportError: 31 | print "*** package 'numpy' not found ***" 32 | print "MDAnalysis requires a version of NumPy (>=1.0.3), even for setup." 33 | print "Please get it from http://numpy.scipy.org/ or install it through your package manager." 34 | sys.exit(-1) 35 | 36 | try: 37 | numpy_include = numpy.get_include() 38 | except AttributeError: 39 | numpy_include = numpy.get_numpy_include() 40 | 41 | include_dirs = [numpy_include] 42 | 43 | # Handle cython modules 44 | try: 45 | from Cython.Distutils import build_ext 46 | use_cython = True 47 | cmdclass = {'build_ext': build_ext} 48 | except ImportError: 49 | use_cython = False 50 | cmdclass = {} 51 | 52 | if use_cython: 53 | # cython has to be >=0.16 to support cython.parallel 54 | import Cython 55 | required_version = "0.16" 56 | cython_version = ''.join(c for c in Cython.__version__ if c.isdigit()) 57 | if not int(cython_version) >= int(required_version.replace(".", "")): 58 | raise ImportError("Cython version %s (found %s) is required because it offers a handy parallelisation module" % (required_version, Cython.__version__)) 59 | del Cython 60 | 61 | if __name__ == '__main__': 62 | RELEASE = "0.1" 63 | with open("README.md") as summary: 64 | LONG_DESCRIPTION = summary.read() 65 | CLASSIFIERS = ['Development Status :: 4 - Beta', 66 | 'Environment :: Console', 67 | 'Intended Audience :: Science/Research', 68 | 'License :: OSI Approved :: GNU General Public License (GPL)', 69 | 'Operating System :: POSIX', 70 | 'Operating System :: MacOS :: MacOS X', 71 | 'Programming Language :: Python', 72 | 'Programming Language :: C', 73 | 'Topic :: Scientific/Engineering :: Bio-Informatics', 74 | 'Topic :: Scientific/Engineering :: Chemistry', 75 | 'Topic :: Software Development :: Libraries :: Python Modules', 76 | ] 77 | 78 | if 'DEBUG_CFLAGS' in os.environ: 79 | extra_compile_args = '\ 80 | -std=c99 -pedantic -Wall -Wcast-align -Wcast-qual -Wpointer-arith \ 81 | -Wchar-subscripts -Winline -Wnested-externs -Wbad-function-cast \ 82 | -Wunreachable-code -Werror' 83 | define_macros = [('DEBUG', '1')] 84 | else: 85 | extra_compile_args = ['-O3'] 86 | define_macros = [] 87 | 88 | extensions = [Extension('ForcePy.Util', ['ForcePy/Util.%s' % ("pyx" if use_cython else "c")], 89 | include_dirs=include_dirs , 90 | libraries = ['m'], 91 | extra_compile_args=extra_compile_args), 92 | Extension('ForcePy.NeighborList', ['ForcePy/NeighborList.%s' % ("pyx" if use_cython else "c")], 93 | include_dirs=include_dirs , 94 | libraries = ['m'], 95 | extra_compile_args=extra_compile_args), 96 | Extension('ForcePy.Mesh', ['ForcePy/Mesh.%s' % ("pyx" if use_cython else "c")], 97 | include_dirs=include_dirs , 98 | libraries = [], 99 | extra_compile_args=extra_compile_args), 100 | 101 | Extension('ForcePy.Basis', ['ForcePy/Basis.%s' % ("pyx" if use_cython else "c")], 102 | include_dirs=include_dirs , 103 | libraries = [], 104 | extra_compile_args=extra_compile_args)] 105 | 106 | 107 | setup(name = 'ForcePy', 108 | version = RELEASE, 109 | description = 'Object-oriented Force Matching and coarse-graining toolkit', 110 | author = 'Andrew White', 111 | author_email = 'white.d.andrew@gmail.com', 112 | url = 'http://github.com/whitead/ForcePy/', 113 | requires = ['numpy (>=1.0.3)', 'biopython', 'MDAnalysis'], 114 | provides = ['ForcePy'], 115 | license = 'GPL 2', 116 | packages = [ 'ForcePy'], 117 | package_dir = {'ForcePy': 'ForcePy'}, 118 | ext_modules = extensions, 119 | classifiers = CLASSIFIERS, 120 | long_description = LONG_DESCRIPTION, 121 | cmdclass = cmdclass, 122 | # all standard requirements are available through PyPi and 123 | # typically can be installed without difficulties through setuptools 124 | install_requires = ['numpy>=1.0.3', # currently not useful because without numpy we don't get here 125 | 'MDAnalysis>=0.15' # Development version is currently required, so not good. 126 | ], 127 | # extras can be difficult to install through setuptools and/or 128 | # you might prefer to use the version available through your 129 | # packaging system 130 | extras_require = { 131 | 'analysis': ['matplotlib', 132 | ], 133 | }, 134 | zip_safe = False, # as a zipped egg the *.so files are not found (at least in Ubuntu/Linux) 135 | ) 136 | -------------------------------------------------------------------------------- /test/lj/lj.json: -------------------------------------------------------------------------------- 1 | { 2 | "kT" : 0.7, 3 | "box": [5.2, 5.2, 5.2] 4 | } -------------------------------------------------------------------------------- /test/lj/lj.obs: -------------------------------------------------------------------------------- 1 | 0.303888741697622 2 | 0.0895184006230837 3 | 2.22047092575047 4 | 0.171559970480399 5 | 0.117410373946687 6 | 0.0320348767880456 7 | 0.0212832692462179 8 | 1.97215456722955 9 | 1.05960462119079 10 | 0.504281429941472 11 | 0.763184783996611 12 | 0.0122307865222768 13 | 4.87802413813279 14 | 0.631308296995676 15 | 0.302935249196534 16 | 0.49644718754104 17 | 1.83597679207291 18 | 1.04416526324138 19 | 0.000697463543481388 20 | 0.127302743152654 21 | 0.042652341651943 22 | 0.00199960749108849 23 | 0.373139471473074 24 | 0.559695389215307 25 | 0.81710915516741 26 | 4.70951976788164 27 | 0.0335421162144889 28 | 0.824375288503122 29 | 0.136366142174427 30 | 2.53370329072711 31 | 1.30346440235275 32 | 3.1565479161847 33 | 0.23354180680626 34 | 1.11982423425127 35 | 0.0484099614640327 36 | 1.9900027262282 37 | 1.65001629408131 38 | 2.7003064068413 39 | 0.537182418687473 40 | 3.8182616828087 41 | 0.0151245280059279 42 | 0.591303812174879 43 | 0.206963822767908 44 | 0.96587530922475 45 | 3.47109436266101 46 | 0.0449411533538263 47 | 0.133158514490717 48 | 0.0734355583884428 49 | 0.238944574255214 50 | 0.158528036045584 51 | 5.48930941253992 52 | 0.0395863115689798 53 | 0.352094305342226 54 | 0.00537563330527893 55 | 5.60996246590148 56 | 0.236574837286485 57 | 3.05485352349671 58 | 0.182648264119637 59 | 1.85069886473443 60 | 0.0634240225393898 61 | 0.117273836562301 62 | 0.134379715942564 63 | 0.272578436988407 64 | 0.0993188318259463 65 | 0.84226555617555 66 | 0.774717878198027 67 | 0.0410123693072852 68 | 1.07140753460251 69 | 0.0569096046903533 70 | 0.190678266550341 71 | 0.00959114520710149 72 | 0.0939628370924266 73 | 0.154828259324181 74 | 1.96555892838347 75 | 0.248482444897487 76 | 0.94449968770797 77 | 0.000409416396918259 78 | 0.722344001906374 79 | 0.189931555783976 80 | 8.00821859098928 81 | 0.608340658557424 82 | 1.42975120779626 83 | 0.0403779571077181 84 | 0.362349157122941 85 | 0.206463070686294 86 | 0.0367580665406257 87 | 0.0181429777708237 88 | 1.70865086923236 89 | 0.194583382718841 90 | 0.102375036924695 91 | 0.398613838495738 92 | 0.0121982924692265 93 | 0.0835806641805969 94 | 4.94093969214448 95 | 0.469668751479047 96 | 2.37538034897883 97 | 1.20332594523473 98 | 3.38939467756898 99 | 0.129472806215527 100 | 0.298756377112712 101 | 0.592371938628128 102 | 0.299708423264935 103 | 0.0846527648698618 104 | 3.43723970566174 105 | 0.331720505563045 106 | 3.24842777089324 107 | 2.06965636245574 108 | 0.897591361890648 109 | 0.218584725663786 110 | 1.04342828836266 111 | 0.0548081301043332 112 | 0.137489485580818 113 | 0.190090606912393 114 | 4.08032444560953 115 | 1.79418612509449 116 | 0.450826463440398 117 | 0.410742721270612 118 | 0.00106148329094945 119 | 0.00113891204158005 120 | 0.0766582502400174 121 | 2.54117857451985 122 | 1.6861365981331 123 | 0.757116368891369 124 | 0.0482836612680033 125 | 0.657831780013377 126 | 5.63683527704651 127 | 0.0423065608108478 128 | 0.282344509659708 129 | 0.717620595015292 130 | 0.313232859139064 131 | 9.96799140827705 132 | 0.0259709596372017 133 | 1.57886031288892 134 | 0.200466457509184 135 | 2.89591658511513 136 | 0.214105854882499 137 | 0.0110506364893989 138 | 0.0955990888486645 139 | 0.0350240848660221 140 | 1.45981419024937 141 | 0.119986911470381 142 | 0.856366157680112 143 | 0.0212977633093676 144 | 0.00758843836197437 145 | 0.128745766423075 146 | 2.90765757643891 147 | 4.96797927395023 148 | 0.190717128230927 149 | 1.82775053966357 150 | 2.51429974251435 151 | 0.314569651058037 152 | 5.14103000557474 153 | 2.7044933391962 154 | 0.118495454310515 155 | 0.145440802774386 156 | 0.120048900559597 157 | 0.610213106669446 158 | 0.116929516096214 159 | 1.53159062880623 160 | 0.364046918510018 161 | 0.00283717037518083 162 | 0.0645880806208255 163 | 0.464610844081247 164 | 2.10327112332384 165 | 0.456147622177205 166 | 3.0625747082199 167 | 2.7933374699112 168 | 5.36310112763946 169 | 0.162951512273505 170 | 0.120949587880773 171 | 0.292449626981975 172 | 0.660497606647661 173 | 0.970968974925818 174 | 0.940459849401632 175 | 0.1212138260166 176 | 0.0525569488162905 177 | 0.525415979884808 178 | 1.29782329035984 179 | 0.0214008721521574 180 | 0.0387346939504741 181 | 0.908321371399204 182 | 0.166665268116148 183 | 6.47794256711257 184 | 0.0747516775248202 185 | 0.357362259130729 186 | 3.00377467868002 187 | 0.435181224056387 188 | 0.95973720804914 189 | 0.430482852705548 190 | 0.928131056245186 191 | 0.00191263226220695 192 | 0.181335523034495 193 | 5.21302093508095 194 | 0.617601415546931 195 | 1.84515401321028 196 | 0.0316881257542858 197 | 1.52567432361288 198 | 0.14631948405171 199 | 0.162228357719586 200 | 1.71028718395524 201 | 0.615515519686928 202 | 0.167337785614142 203 | 0.551251115920638 204 | 0.352467010504459 205 | 0.583110329177685 206 | 0.405730194831402 207 | 0.725972241041839 208 | 0.220514983815701 209 | 0.283537248318844 210 | 0.000741235411918992 211 | 0.0367008001679569 212 | 0.00709691193937484 213 | 3.63767804019409 214 | 0.000457016249813989 215 | 0.0780455630607505 216 | 0.727045192787823 217 | 12.0987918762419 218 | 0.14641933533927 219 | 2.87604029989528 220 | 0.0442583972264125 221 | 1.89868424252043 222 | 5.82299307599683 223 | 1.08419861708964 224 | 1.82968434708235 225 | 0.518511036513054 226 | 3.23448441348299e-05 227 | 0.00584085279350158 228 | 0.181764121516292 229 | 3.81905879170461 230 | 0.425314451769259 231 | 0.0808097826022158 232 | 1.91603519343317 233 | 0.252253269665109 234 | 0.724851823936851 235 | 1.94391325584944 236 | 0.125181740773815 237 | 0.538754152273422 238 | 0.00641734511330715 239 | 0.240832271850656 240 | 0.000133492558575914 241 | 0.788662301982888 242 | 1.03966658143728 243 | 0.121403524396999 244 | 0.114667896917409 245 | 2.8934180826843 246 | 0.0633161727735344 247 | 0.366170833705999 248 | 0.59993496439656 249 | 0.00531702998975774 250 | 0.159000973325332 251 | 0.205195805937273 252 | 2.25829277405101 253 | 1.18328177397758 254 | 1.84733420587419 255 | 3.10706666722827 256 | 0.0515028344821038 257 | 0.0491440203664578 258 | 0.606503454161386 259 | 0.0989088699019368 260 | 0.757965243798747 261 | 0.0440333753734868 262 | 4.00989863156981 263 | 0.133685084127734 264 | 0.185688678359641 265 | 2.8943010815595 266 | 0.479414163256801 267 | 2.21793924718878 268 | 0.0133261296461696 269 | 0.602197360506501 270 | 0.100977152476824 271 | 0.153613045014949 272 | 0.432550549596614 273 | 0.560966334962664 274 | 0.0296260934079023 275 | 0.567851346636134 276 | 1.86563646047914 277 | 2.86173431889241 278 | 0.0141878326541594 279 | 0.00161371860579652 280 | 0.203741856607415 281 | 0.479672831154989 282 | 6.5174387284359 283 | 1.18794984529707 284 | 0.031344641538939 285 | 0.133964629323271 286 | 0.590025959454113 287 | 0.227980797608705 288 | 2.31743109623152 289 | 0.1008891091423 290 | 1.07101784223051 291 | 0.100289782366083 292 | 0.592550118390308 293 | 2.17737341802236 294 | 0.787143114804794 295 | 0.000309249500963281 296 | 0.324309403476033 297 | 0.158038543377626 298 | 0.226755639661884 299 | 1.13288734864076 300 | 0.140809866028957 301 | 0.367514969716843 302 | 0.0257671894033564 303 | 1.40720044643382 304 | 0.0379294813395686 305 | 0.183787089167274 306 | 0.191105164818603 307 | 0.538568734344032 308 | 0.0159823793441658 309 | 0.68011637510542 310 | 0.000289209224852551 311 | 1.62704490207507 312 | 1.25657760843503 313 | 0.00826536996911634 314 | 0.170219836798622 315 | 0.000234106702246074 316 | 0.0301855455406214 317 | 1.32118888764402 318 | 0.330371782221612 319 | 0.179398891633371 320 | 0.249483424296961 321 | 0.113048907974983 322 | 0.0083800453571269 323 | 1.15230554900899 324 | 1.43788918347897 325 | 0.11744584782861 326 | 0.46795078149752 327 | 1.56200628128986 328 | 0.349765585559927 329 | 0.805060702163744 330 | 2.99861577080578 331 | 0.823772589304861 332 | 0.566279309298856 333 | 0.196312710291769 334 | 0.0708247822878296 335 | 0.020248666076931 336 | 0.0550022160322329 337 | 0.661357639191928 338 | 0.0241220230038392 339 | 3.58522307157317 340 | 2.90742379434392 341 | 1.29666774421011 342 | 0.734368687669159 343 | 1.86659869647346 344 | 0.0945225374603852 345 | 0.370036205066296 346 | 5.09910256055523 347 | 0.282448653468617 348 | 1.77517951241623 349 | 0.0173334085117382 350 | 0.282017321279023 351 | 2.55679575478424 352 | 0.334900398483024 353 | 1.89508041697519 354 | 1.01432751402419 355 | 1.34511492331955 356 | 3.49480394075078 357 | 0.394252539085503 358 | 0.12729680346285 359 | 0.25391444679196 360 | 0.102442963448759 361 | 0.784840475798053 362 | 2.76368262403234 363 | 2.30775301501866 364 | 0.0230174889582261 365 | 0.0114234604286964 366 | 0.427733595192073 367 | 0.00827125820120787 368 | 1.48295189394173 369 | 1.3351214270638 370 | 0.00689204386659153 371 | 0.262978335424537 372 | 3.24554194617507 373 | 0.00990815060861081 374 | 0.618049740684984 375 | 0.107065411401574 376 | 1.11547192882877 377 | 0.31123262856622 378 | 0.289937517080769 379 | 0.127891709911975 380 | 2.41663663125942 381 | 0.153776236248997 382 | 0.219334814062548 383 | 0.249641730117854 384 | 0.0143195275640219 385 | 0.106388065610539 386 | 0.266503398865876 387 | 0.470265732852594 388 | 0.0855214546501036 389 | 0.0980475328781586 390 | 0.228052615523285 391 | 1.13447844320562 392 | 0.629888257267693 393 | 0.0158047968166259 394 | 3.91655254839971 395 | 0.00159032017302196 396 | 0.0318806485546272 397 | 0.91684143123799 398 | 1.67671089145299 399 | 2.23295556599571 400 | 1.02995488620039 401 | 0.0778693432207115 402 | 0.0952562538771239 403 | 0.130853815715347 404 | 0.530131064640709 405 | 4.16270620942254 406 | 1.46634972916997 407 | 0.718579342318119 408 | 0.379424792662518 409 | 1.63379123131405 410 | 0.135756518011949 411 | 7.6090081853111e-07 412 | 2.09877403040844 413 | 1.04865277641984 414 | 0.0125894741707153 415 | 0.33610567387813 416 | 0.0874638968542585 417 | 0.670191541754651 418 | 0.508298741338702 419 | 3.72330057609825e-06 420 | 0.371165875832888 421 | 0.134144320731381 422 | 0.0923750581707972 423 | 0.46399450746392 424 | 7.23095599220306 425 | 1.03472047439789 426 | 3.64086262946197 427 | 0.929716935400353 428 | 2.96377786839909 429 | 1.87414811698681 430 | 0.629898192892559 431 | 2.3101658299169 432 | 0.504410856198178 433 | 3.70473902952981 434 | 2.4646514844287 435 | 0.789745285084617 436 | 2.40603805288999 437 | 1.48980305050585 438 | 0.0919917400085448 439 | 0.607657218888079 440 | 0.278301465747736 441 | 1.75245215446585 442 | 1.10662975783128 443 | 0.00417832906378701 444 | 8.22168104300303 445 | 0.229392955431411 446 | 0.527622139571428 447 | 0.160830765728718 448 | 0.931031195392529 449 | 0.612036699549437 450 | 0.024279986517067 451 | 1.39318261061763 452 | 4.42951500441346 453 | 0.00379630447776413 454 | 1.78801646572775 455 | 0.0311568343324596 456 | 0.496525347555633 457 | 0.0331576817862399 458 | 0.00903081505608975 459 | 0.00123222923929209 460 | 0.019692694435362 461 | 0.00541264665589471 462 | 2.21935624053136 463 | 0.560618570905274 464 | 5.10251763273536 465 | 0.397170278388795 466 | 2.23976537658817 467 | 1.66979002364716 468 | 1.25342917147998 469 | 0.133645128039946 470 | 0.225631281011618 471 | 0.000584055468446041 472 | 1.96096265601426 473 | 0.0604164837124823 474 | 0.0789705075902164 475 | 11.0657482326018 476 | 0.508649961914154 477 | 0.0551475144764476 478 | 0.197694843868748 479 | 0.00159232580842508 480 | 2.22636260375165 481 | 0.809873126450819 482 | 2.04166715321744 483 | 0.000142445918070929 484 | 0.0246847731527487 485 | 0.0412076198185947 486 | 0.028776125668428 487 | 0.370708643867255 488 | 0.103777300386298 489 | 0.493988092974206 490 | 2.02735058525816 491 | 0.043602251637737 492 | 0.00777136924312077 493 | 1.85587796856107 494 | 0.0140419011444967 495 | 1.98759467693459 496 | 1.46547314341421 497 | 0.0131469748846531 498 | 3.85073631322112 499 | 4.15794597586703 500 | 0.294538085729476 501 | 0.303888741697622 502 | 0.0895184006230837 503 | 2.22047092575047 504 | 0.171559970480399 505 | 0.117410373946687 506 | 0.0320348767880456 507 | 0.0212832692462179 508 | 1.97215456722955 509 | 1.05960462119079 510 | 0.504281429941472 511 | 0.763184783996611 512 | 0.0122307865222768 513 | 4.87802413813279 514 | 0.631308296995676 515 | 0.302935249196534 516 | 0.49644718754104 517 | 1.83597679207291 518 | 1.04416526324138 519 | 0.000697463543481388 520 | 0.127302743152654 521 | 0.042652341651943 522 | 0.00199960749108849 523 | 0.373139471473074 524 | 0.559695389215307 525 | 0.81710915516741 526 | 4.70951976788164 527 | 0.0335421162144889 528 | 0.824375288503122 529 | 0.136366142174427 530 | 2.53370329072711 531 | 1.30346440235275 532 | 3.1565479161847 533 | 0.23354180680626 534 | 1.11982423425127 535 | 0.0484099614640327 536 | 1.9900027262282 537 | 1.65001629408131 538 | 2.7003064068413 539 | 0.537182418687473 540 | 3.8182616828087 541 | 0.0151245280059279 542 | 0.591303812174879 543 | 0.206963822767908 544 | 0.96587530922475 545 | 3.47109436266101 546 | 0.0449411533538263 547 | 0.133158514490717 548 | 0.0734355583884428 549 | 0.238944574255214 550 | 0.158528036045584 551 | 5.48930941253992 552 | 0.0395863115689798 553 | 0.352094305342226 554 | 0.00537563330527893 555 | 5.60996246590148 556 | 0.236574837286485 557 | 3.05485352349671 558 | 0.182648264119637 559 | 1.85069886473443 560 | 0.0634240225393898 561 | 0.117273836562301 562 | 0.134379715942564 563 | 0.272578436988407 564 | 0.0993188318259463 565 | 0.84226555617555 566 | 0.774717878198027 567 | 0.0410123693072852 568 | 1.07140753460251 569 | 0.0569096046903533 570 | 0.190678266550341 571 | 0.00959114520710149 572 | 0.0939628370924266 573 | 0.154828259324181 574 | 1.96555892838347 575 | 0.248482444897487 576 | 0.94449968770797 577 | 0.000409416396918259 578 | 0.722344001906374 579 | 0.189931555783976 580 | 8.00821859098928 581 | 0.608340658557424 582 | 1.42975120779626 583 | 0.0403779571077181 584 | 0.362349157122941 585 | 0.206463070686294 586 | 0.0367580665406257 587 | 0.0181429777708237 588 | 1.70865086923236 589 | 0.194583382718841 590 | 0.102375036924695 591 | 0.398613838495738 592 | 0.0121982924692265 593 | 0.0835806641805969 594 | 4.94093969214448 595 | 0.469668751479047 596 | 2.37538034897883 597 | 1.20332594523473 598 | 3.38939467756898 599 | 0.129472806215527 600 | 0.298756377112712 601 | 0.592371938628128 602 | 0.299708423264935 603 | 0.0846527648698618 604 | 3.43723970566174 605 | 0.331720505563045 606 | 3.24842777089324 607 | 2.06965636245574 608 | 0.897591361890648 609 | 0.218584725663786 610 | 1.04342828836266 611 | 0.0548081301043332 612 | 0.137489485580818 613 | 0.190090606912393 614 | 4.08032444560953 615 | 1.79418612509449 616 | 0.450826463440398 617 | 0.410742721270612 618 | 0.00106148329094945 619 | 0.00113891204158005 620 | 0.0766582502400174 621 | 2.54117857451985 622 | 1.6861365981331 623 | 0.757116368891369 624 | 0.0482836612680033 625 | 0.657831780013377 626 | 5.63683527704651 627 | 0.0423065608108478 628 | 0.282344509659708 629 | 0.717620595015292 630 | 0.313232859139064 631 | 9.96799140827705 632 | 0.0259709596372017 633 | 1.57886031288892 634 | 0.200466457509184 635 | 2.89591658511513 636 | 0.214105854882499 637 | 0.0110506364893989 638 | 0.0955990888486645 639 | 0.0350240848660221 640 | 1.45981419024937 641 | 0.119986911470381 642 | 0.856366157680112 643 | 0.0212977633093676 644 | 0.00758843836197437 645 | 0.128745766423075 646 | 2.90765757643891 647 | 4.96797927395023 648 | 0.190717128230927 649 | 1.82775053966357 650 | 2.51429974251435 651 | 0.314569651058037 652 | 5.14103000557474 653 | 2.7044933391962 654 | 0.118495454310515 655 | 0.145440802774386 656 | 0.120048900559597 657 | 0.610213106669446 658 | 0.116929516096214 659 | 1.53159062880623 660 | 0.364046918510018 661 | 0.00283717037518083 662 | 0.0645880806208255 663 | 0.464610844081247 664 | 2.10327112332384 665 | 0.456147622177205 666 | 3.0625747082199 667 | 2.7933374699112 668 | 5.36310112763946 669 | 0.162951512273505 670 | 0.120949587880773 671 | 0.292449626981975 672 | 0.660497606647661 673 | 0.970968974925818 674 | 0.940459849401632 675 | 0.1212138260166 676 | 0.0525569488162905 677 | 0.525415979884808 678 | 1.29782329035984 679 | 0.0214008721521574 680 | 0.0387346939504741 681 | 0.908321371399204 682 | 0.166665268116148 683 | 6.47794256711257 684 | 0.0747516775248202 685 | 0.357362259130729 686 | 3.00377467868002 687 | 0.435181224056387 688 | 0.95973720804914 689 | 0.430482852705548 690 | 0.928131056245186 691 | 0.00191263226220695 692 | 0.181335523034495 693 | 5.21302093508095 694 | 0.617601415546931 695 | 1.84515401321028 696 | 0.0316881257542858 697 | 1.52567432361288 698 | 0.14631948405171 699 | 0.162228357719586 700 | 1.71028718395524 701 | 0.615515519686928 702 | 0.167337785614142 703 | 0.551251115920638 704 | 0.352467010504459 705 | 0.583110329177685 706 | 0.405730194831402 707 | 0.725972241041839 708 | 0.220514983815701 709 | 0.283537248318844 710 | 0.000741235411918992 711 | 0.0367008001679569 712 | 0.00709691193937484 713 | 3.63767804019409 714 | 0.000457016249813989 715 | 0.0780455630607505 716 | 0.727045192787823 717 | 12.0987918762419 718 | 0.14641933533927 719 | 2.87604029989528 720 | 0.0442583972264125 721 | 1.89868424252043 722 | 5.82299307599683 723 | 1.08419861708964 724 | 1.82968434708235 725 | 0.518511036513054 726 | 3.23448441348299e-05 727 | 0.00584085279350158 728 | 0.181764121516292 729 | 3.81905879170461 730 | 0.425314451769259 731 | 0.0808097826022158 732 | 1.91603519343317 733 | 0.252253269665109 734 | 0.724851823936851 735 | 1.94391325584944 736 | 0.125181740773815 737 | 0.538754152273422 738 | 0.00641734511330715 739 | 0.240832271850656 740 | 0.000133492558575914 741 | 0.788662301982888 742 | 1.03966658143728 743 | 0.121403524396999 744 | 0.114667896917409 745 | 2.8934180826843 746 | 0.0633161727735344 747 | 0.366170833705999 748 | 0.59993496439656 749 | 0.00531702998975774 750 | 0.159000973325332 751 | 0.205195805937273 752 | 2.25829277405101 753 | 1.18328177397758 754 | 1.84733420587419 755 | 3.10706666722827 756 | 0.0515028344821038 757 | 0.0491440203664578 758 | 0.606503454161386 759 | 0.0989088699019368 760 | 0.757965243798747 761 | 0.0440333753734868 762 | 4.00989863156981 763 | 0.133685084127734 764 | 0.185688678359641 765 | 2.8943010815595 766 | 0.479414163256801 767 | 2.21793924718878 768 | 0.0133261296461696 769 | 0.602197360506501 770 | 0.100977152476824 771 | 0.153613045014949 772 | 0.432550549596614 773 | 0.560966334962664 774 | 0.0296260934079023 775 | 0.567851346636134 776 | 1.86563646047914 777 | 2.86173431889241 778 | 0.0141878326541594 779 | 0.00161371860579652 780 | 0.203741856607415 781 | 0.479672831154989 782 | 6.5174387284359 783 | 1.18794984529707 784 | 0.031344641538939 785 | 0.133964629323271 786 | 0.590025959454113 787 | 0.227980797608705 788 | 2.31743109623152 789 | 0.1008891091423 790 | 1.07101784223051 791 | 0.100289782366083 792 | 0.592550118390308 793 | 2.17737341802236 794 | 0.787143114804794 795 | 0.000309249500963281 796 | 0.324309403476033 797 | 0.158038543377626 798 | 0.226755639661884 799 | 1.13288734864076 800 | 0.140809866028957 801 | 0.367514969716843 802 | 0.0257671894033564 803 | 1.40720044643382 804 | 0.0379294813395686 805 | 0.183787089167274 806 | 0.191105164818603 807 | 0.538568734344032 808 | 0.0159823793441658 809 | 0.68011637510542 810 | 0.000289209224852551 811 | 1.62704490207507 812 | 1.25657760843503 813 | 0.00826536996911634 814 | 0.170219836798622 815 | 0.000234106702246074 816 | 0.0301855455406214 817 | 1.32118888764402 818 | 0.330371782221612 819 | 0.179398891633371 820 | 0.249483424296961 821 | 0.113048907974983 822 | 0.0083800453571269 823 | 1.15230554900899 824 | 1.43788918347897 825 | 0.11744584782861 826 | 0.46795078149752 827 | 1.56200628128986 828 | 0.349765585559927 829 | 0.805060702163744 830 | 2.99861577080578 831 | 0.823772589304861 832 | 0.566279309298856 833 | 0.196312710291769 834 | 0.0708247822878296 835 | 0.020248666076931 836 | 0.0550022160322329 837 | 0.661357639191928 838 | 0.0241220230038392 839 | 3.58522307157317 840 | 2.90742379434392 841 | 1.29666774421011 842 | 0.734368687669159 843 | 1.86659869647346 844 | 0.0945225374603852 845 | 0.370036205066296 846 | 5.09910256055523 847 | 0.282448653468617 848 | 1.77517951241623 849 | 0.0173334085117382 850 | 0.282017321279023 851 | 2.55679575478424 852 | 0.334900398483024 853 | 1.89508041697519 854 | 1.01432751402419 855 | 1.34511492331955 856 | 3.49480394075078 857 | 0.394252539085503 858 | 0.12729680346285 859 | 0.25391444679196 860 | 0.102442963448759 861 | 0.784840475798053 862 | 2.76368262403234 863 | 2.30775301501866 864 | 0.0230174889582261 865 | 0.0114234604286964 866 | 0.427733595192073 867 | 0.00827125820120787 868 | 1.48295189394173 869 | 1.3351214270638 870 | 0.00689204386659153 871 | 0.262978335424537 872 | 3.24554194617507 873 | 0.00990815060861081 874 | 0.618049740684984 875 | 0.107065411401574 876 | 1.11547192882877 877 | 0.31123262856622 878 | 0.289937517080769 879 | 0.127891709911975 880 | 2.41663663125942 881 | 0.153776236248997 882 | 0.219334814062548 883 | 0.249641730117854 884 | 0.0143195275640219 885 | 0.106388065610539 886 | 0.266503398865876 887 | 0.470265732852594 888 | 0.0855214546501036 889 | 0.0980475328781586 890 | 0.228052615523285 891 | 1.13447844320562 892 | 0.629888257267693 893 | 0.0158047968166259 894 | 3.91655254839971 895 | 0.00159032017302196 896 | 0.0318806485546272 897 | 0.91684143123799 898 | 1.67671089145299 899 | 2.23295556599571 900 | 1.02995488620039 901 | 0.0778693432207115 902 | 0.0952562538771239 903 | 0.130853815715347 904 | 0.530131064640709 905 | 4.16270620942254 906 | 1.46634972916997 907 | 0.718579342318119 908 | 0.379424792662518 909 | 1.63379123131405 910 | 0.135756518011949 911 | 7.6090081853111e-07 912 | 2.09877403040844 913 | 1.04865277641984 914 | 0.0125894741707153 915 | 0.33610567387813 916 | 0.0874638968542585 917 | 0.670191541754651 918 | 0.508298741338702 919 | 3.72330057609825e-06 920 | 0.371165875832888 921 | 0.134144320731381 922 | 0.0923750581707972 923 | 0.46399450746392 924 | 7.23095599220306 925 | 1.03472047439789 926 | 3.64086262946197 927 | 0.929716935400353 928 | 2.96377786839909 929 | 1.87414811698681 930 | 0.629898192892559 931 | 2.3101658299169 932 | 0.504410856198178 933 | 3.70473902952981 934 | 2.4646514844287 935 | 0.789745285084617 936 | 2.40603805288999 937 | 1.48980305050585 938 | 0.0919917400085448 939 | 0.607657218888079 940 | 0.278301465747736 941 | 1.75245215446585 942 | 1.10662975783128 943 | 0.00417832906378701 944 | 8.22168104300303 945 | 0.229392955431411 946 | 0.527622139571428 947 | 0.160830765728718 948 | 0.931031195392529 949 | 0.612036699549437 950 | 0.024279986517067 951 | 1.39318261061763 952 | 4.42951500441346 953 | 0.00379630447776413 954 | 1.78801646572775 955 | 0.0311568343324596 956 | 0.496525347555633 957 | 0.0331576817862399 958 | 0.00903081505608975 959 | 0.00123222923929209 960 | 0.019692694435362 961 | 0.00541264665589471 962 | 2.21935624053136 963 | 0.560618570905274 964 | 5.10251763273536 965 | 0.397170278388795 966 | 2.23976537658817 967 | 1.66979002364716 968 | 1.25342917147998 969 | 0.133645128039946 970 | 0.225631281011618 971 | 0.000584055468446041 972 | 1.96096265601426 973 | 0.0604164837124823 974 | 0.0789705075902164 975 | 11.0657482326018 976 | 0.508649961914154 977 | 0.0551475144764476 978 | 0.197694843868748 979 | 0.00159232580842508 980 | 2.22636260375165 981 | 0.809873126450819 982 | 2.04166715321744 983 | 0.000142445918070929 984 | 0.0246847731527487 985 | 0.0412076198185947 986 | 0.028776125668428 987 | 0.370708643867255 988 | 0.103777300386298 989 | 0.493988092974206 990 | 2.02735058525816 991 | 0.043602251637737 992 | 0.00777136924312077 993 | 1.85587796856107 994 | 0.0140419011444967 995 | 1.98759467693459 996 | 1.46547314341421 997 | 0.0131469748846531 998 | 3.85073631322112 999 | 4.15794597586703 1000 | 0.294538085729476 1001 | -------------------------------------------------------------------------------- /test/lj/lj.pdb: -------------------------------------------------------------------------------- 1 | COMPND UNNAMED 2 | AUTHOR GENERATED BY OPEN BABEL 2.3.0 3 | HETATM 1 AR LIG 1 1.204 3.740 3.480 1.00 0.00 Ar 4 | HETATM 2 AR LIG 2 3.668 2.903 4.645 1.00 0.00 Ar 5 | HETATM 3 AR LIG 3 1.302 0.092 4.842 1.00 0.00 Ar 6 | HETATM 4 AR LIG 4 1.490 2.164 4.042 1.00 0.00 Ar 7 | HETATM 5 AR LIG 5 3.220 4.650 3.970 1.00 0.00 Ar 8 | HETATM 6 AR LIG 6 3.940 4.620 2.142 1.00 0.00 Ar 9 | HETATM 7 AR LIG 7 3.518 2.800 3.367 1.00 0.00 Ar 10 | HETATM 8 AR LIG 8 1.404 3.289 4.288 1.00 0.00 Ar 11 | HETATM 9 AR LIG 9 3.552 5.162 4.905 1.00 0.00 Ar 12 | HETATM 10 AR LIG 10 0.064 0.970 1.713 1.00 0.00 Ar 13 | HETATM 11 AR LIG 11 4.674 0.820 5.136 1.00 0.00 Ar 14 | HETATM 12 AR LIG 12 1.340 2.128 5.114 1.00 0.00 Ar 15 | HETATM 13 AR LIG 13 0.243 3.438 3.900 1.00 0.00 Ar 16 | HETATM 14 AR LIG 14 4.972 1.959 1.244 1.00 0.00 Ar 17 | HETATM 15 AR LIG 15 4.451 4.485 0.340 1.00 0.00 Ar 18 | HETATM 16 AR LIG 16 4.967 3.020 2.522 1.00 0.00 Ar 19 | HETATM 17 AR LIG 17 4.618 3.826 3.250 1.00 0.00 Ar 20 | HETATM 18 AR LIG 18 4.711 4.760 3.972 1.00 0.00 Ar 21 | HETATM 19 AR LIG 19 1.073 1.037 4.283 1.00 0.00 Ar 22 | HETATM 20 AR LIG 20 2.186 0.178 4.200 1.00 0.00 Ar 23 | HETATM 21 AR LIG 21 3.609 3.739 2.746 1.00 0.00 Ar 24 | HETATM 22 AR LIG 22 0.711 3.906 2.203 1.00 0.00 Ar 25 | HETATM 23 AR LIG 23 1.641 3.531 2.155 1.00 0.00 Ar 26 | HETATM 24 AR LIG 24 2.769 4.089 0.788 1.00 0.00 Ar 27 | HETATM 25 AR LIG 25 3.669 1.286 5.024 1.00 0.00 Ar 28 | HETATM 26 AR LIG 26 5.004 4.280 2.401 1.00 0.00 Ar 29 | HETATM 27 AR LIG 27 5.077 4.661 1.238 1.00 0.00 Ar 30 | HETATM 28 AR LIG 28 0.268 2.368 3.546 1.00 0.00 Ar 31 | HETATM 29 AR LIG 29 0.312 0.551 0.819 1.00 0.00 Ar 32 | HETATM 30 AR LIG 30 1.872 0.971 1.457 1.00 0.00 Ar 33 | HETATM 31 AR LIG 31 1.166 1.006 2.251 1.00 0.00 Ar 34 | HETATM 32 AR LIG 32 4.288 2.290 2.920 1.00 0.00 Ar 35 | HETATM 33 AR LIG 33 3.307 0.553 1.769 1.00 0.00 Ar 36 | HETATM 34 AR LIG 34 0.337 1.827 2.282 1.00 0.00 Ar 37 | HETATM 35 AR LIG 35 2.620 0.803 2.715 1.00 0.00 Ar 38 | HETATM 36 AR LIG 36 2.093 4.139 4.198 1.00 0.00 Ar 39 | HETATM 37 AR LIG 37 2.074 4.448 1.795 1.00 0.00 Ar 40 | HETATM 38 AR LIG 38 2.330 1.583 3.489 1.00 0.00 Ar 41 | HETATM 39 AR LIG 39 0.003 1.372 3.871 1.00 0.00 Ar 42 | HETATM 40 AR LIG 40 4.840 3.287 4.792 1.00 0.00 Ar 43 | HETATM 41 AR LIG 41 3.861 2.730 1.961 1.00 0.00 Ar 44 | HETATM 42 AR LIG 42 1.199 4.802 2.688 1.00 0.00 Ar 45 | HETATM 43 AR LIG 43 2.165 3.435 5.058 1.00 0.00 Ar 46 | HETATM 44 AR LIG 44 1.095 0.021 1.260 1.00 0.00 Ar 47 | HETATM 45 AR LIG 45 4.372 0.316 1.696 1.00 0.00 Ar 48 | HETATM 46 AR LIG 46 4.268 0.452 3.892 1.00 0.00 Ar 49 | HETATM 47 AR LIG 47 0.858 2.722 2.833 1.00 0.00 Ar 50 | HETATM 48 AR LIG 48 3.142 1.705 4.181 1.00 0.00 Ar 51 | HETATM 49 AR LIG 49 0.967 1.553 3.250 1.00 0.00 Ar 52 | HETATM 50 AR LIG 50 3.641 4.837 1.113 1.00 0.00 Ar 53 | HETATM 51 AR LIG 51 2.728 3.571 3.477 1.00 0.00 Ar 54 | HETATM 52 AR LIG 52 4.430 1.561 2.098 1.00 0.00 Ar 55 | HETATM 53 AR LIG 53 3.853 1.774 1.048 1.00 0.00 Ar 56 | HETATM 54 AR LIG 54 0.300 2.707 0.431 1.00 0.00 Ar 57 | HETATM 55 AR LIG 55 0.758 4.602 4.032 1.00 0.00 Ar 58 | HETATM 56 AR LIG 56 2.742 1.991 1.506 1.00 0.00 Ar 59 | HETATM 57 AR LIG 57 0.810 3.747 4.923 1.00 0.00 Ar 60 | HETATM 58 AR LIG 58 0.437 0.142 2.269 1.00 0.00 Ar 61 | HETATM 59 AR LIG 59 0.627 0.405 3.361 1.00 0.00 Ar 62 | HETATM 60 AR LIG 60 3.466 3.054 0.994 1.00 0.00 Ar 63 | HETATM 61 AR LIG 61 2.950 4.171 4.821 1.00 0.00 Ar 64 | HETATM 62 AR LIG 62 4.798 5.139 2.803 1.00 0.00 Ar 65 | HETATM 63 AR LIG 63 1.896 4.422 0.128 1.00 0.00 Ar 66 | HETATM 64 AR LIG 64 1.428 2.856 0.759 1.00 0.00 Ar 67 | MASTER 0 0 0 0 0 0 0 0 100 0 100 0 68 | END 69 | -------------------------------------------------------------------------------- /test/lj/lj.py: -------------------------------------------------------------------------------- 1 | from MDAnalysis import Universe 2 | from ForcePy import * 3 | 4 | fm = ForceMatch(Universe("lj.pdb", "lj.xyz"), "lj.json") 5 | fm.u.trajectory.periodic = True 6 | ff = LJForce(3) 7 | mesh = Mesh.UniformMesh(0.5,3,0.005) 8 | #pwf = SpectralForce(Pairwise, mesh, Basis.Gaussian(mesh, 0.1)) 9 | pwf = SpectralForce(Pairwise, mesh, Basis.UnitStep) 10 | pwf.fill_with_repulsion() 11 | fm.add_ref_force(ff) 12 | fm.add_and_type_pair(pwf) 13 | fm.force_match(500) 14 | fm.finalize() 15 | fm.write() 16 | -------------------------------------------------------------------------------- /test/lj/lj_parallel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from MDAnalysis import Universe 4 | from ForcePy import * 5 | import numpy as np 6 | import pickle 7 | 8 | fm = ForceMatch(Universe("lj.pdb", "lj.xyz"), "lj.json") 9 | ff = LJForce(3) 10 | mesh = Mesh.UniformMesh(0,3,0.005) 11 | #pwf = SpectralForce(Pairwise, mesh, Basis.Gaussian(mesh, 0.1)) 12 | pwf = SpectralForce(Pairwise, mesh, Basis.UnitStep) 13 | pwf.add_regularizer(SmoothRegularizer) 14 | fm.add_ref_force(ff) 15 | fm.add_and_type_pair(pwf) 16 | fm.force_match_mpi(do_plots=True, repeats=5, batch_size=5) 17 | -------------------------------------------------------------------------------- /test/methanol/cg.trr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitead/ForcePy/de7858e2e3fb56e24794beee367d36516cc1eb20/test/methanol/cg.trr -------------------------------------------------------------------------------- /test/methanol/cg.xyz: -------------------------------------------------------------------------------- 1 | 1000 2 | foo 3 | Ar 0.0 0.0 0.0 4 | Ar 1.0 0.0 0.0 5 | Ar 2.0 0.0 0.0 6 | Ar 3.0 0.0 0.0 7 | Ar 4.0 0.0 0.0 8 | Ar 5.0 0.0 0.0 9 | Ar 6.0 0.0 0.0 10 | Ar 7.0 0.0 0.0 11 | Ar 8.0 0.0 0.0 12 | Ar 9.0 0.0 0.0 13 | Ar 0.0 1.0 0.0 14 | Ar 1.0 1.0 0.0 15 | Ar 2.0 1.0 0.0 16 | Ar 3.0 1.0 0.0 17 | Ar 4.0 1.0 0.0 18 | Ar 5.0 1.0 0.0 19 | Ar 6.0 1.0 0.0 20 | Ar 7.0 1.0 0.0 21 | Ar 8.0 1.0 0.0 22 | Ar 9.0 1.0 0.0 23 | Ar 0.0 2.0 0.0 24 | Ar 1.0 2.0 0.0 25 | Ar 2.0 2.0 0.0 26 | Ar 3.0 2.0 0.0 27 | Ar 4.0 2.0 0.0 28 | Ar 5.0 2.0 0.0 29 | Ar 6.0 2.0 0.0 30 | Ar 7.0 2.0 0.0 31 | Ar 8.0 2.0 0.0 32 | Ar 9.0 2.0 0.0 33 | Ar 0.0 3.0 0.0 34 | Ar 1.0 3.0 0.0 35 | Ar 2.0 3.0 0.0 36 | Ar 3.0 3.0 0.0 37 | Ar 4.0 3.0 0.0 38 | Ar 5.0 3.0 0.0 39 | Ar 6.0 3.0 0.0 40 | Ar 7.0 3.0 0.0 41 | Ar 8.0 3.0 0.0 42 | Ar 9.0 3.0 0.0 43 | Ar 0.0 4.0 0.0 44 | Ar 1.0 4.0 0.0 45 | Ar 2.0 4.0 0.0 46 | Ar 3.0 4.0 0.0 47 | Ar 4.0 4.0 0.0 48 | Ar 5.0 4.0 0.0 49 | Ar 6.0 4.0 0.0 50 | Ar 7.0 4.0 0.0 51 | Ar 8.0 4.0 0.0 52 | Ar 9.0 4.0 0.0 53 | Ar 0.0 5.0 0.0 54 | Ar 1.0 5.0 0.0 55 | Ar 2.0 5.0 0.0 56 | Ar 3.0 5.0 0.0 57 | Ar 4.0 5.0 0.0 58 | Ar 5.0 5.0 0.0 59 | Ar 6.0 5.0 0.0 60 | Ar 7.0 5.0 0.0 61 | Ar 8.0 5.0 0.0 62 | Ar 9.0 5.0 0.0 63 | Ar 0.0 6.0 0.0 64 | Ar 1.0 6.0 0.0 65 | Ar 2.0 6.0 0.0 66 | Ar 3.0 6.0 0.0 67 | Ar 4.0 6.0 0.0 68 | Ar 5.0 6.0 0.0 69 | Ar 6.0 6.0 0.0 70 | Ar 7.0 6.0 0.0 71 | Ar 8.0 6.0 0.0 72 | Ar 9.0 6.0 0.0 73 | Ar 0.0 7.0 0.0 74 | Ar 1.0 7.0 0.0 75 | Ar 2.0 7.0 0.0 76 | Ar 3.0 7.0 0.0 77 | Ar 4.0 7.0 0.0 78 | Ar 5.0 7.0 0.0 79 | Ar 6.0 7.0 0.0 80 | Ar 7.0 7.0 0.0 81 | Ar 8.0 7.0 0.0 82 | Ar 9.0 7.0 0.0 83 | Ar 0.0 8.0 0.0 84 | Ar 1.0 8.0 0.0 85 | Ar 2.0 8.0 0.0 86 | Ar 3.0 8.0 0.0 87 | Ar 4.0 8.0 0.0 88 | Ar 5.0 8.0 0.0 89 | Ar 6.0 8.0 0.0 90 | Ar 7.0 8.0 0.0 91 | Ar 8.0 8.0 0.0 92 | Ar 9.0 8.0 0.0 93 | Ar 0.0 9.0 0.0 94 | Ar 1.0 9.0 0.0 95 | Ar 2.0 9.0 0.0 96 | Ar 3.0 9.0 0.0 97 | Ar 4.0 9.0 0.0 98 | Ar 5.0 9.0 0.0 99 | Ar 6.0 9.0 0.0 100 | Ar 7.0 9.0 0.0 101 | Ar 8.0 9.0 0.0 102 | Ar 9.0 9.0 0.0 103 | Ar 0.0 0.0 1.0 104 | Ar 1.0 0.0 1.0 105 | Ar 2.0 0.0 1.0 106 | Ar 3.0 0.0 1.0 107 | Ar 4.0 0.0 1.0 108 | Ar 5.0 0.0 1.0 109 | Ar 6.0 0.0 1.0 110 | Ar 7.0 0.0 1.0 111 | Ar 8.0 0.0 1.0 112 | Ar 9.0 0.0 1.0 113 | Ar 0.0 1.0 1.0 114 | Ar 1.0 1.0 1.0 115 | Ar 2.0 1.0 1.0 116 | Ar 3.0 1.0 1.0 117 | Ar 4.0 1.0 1.0 118 | Ar 5.0 1.0 1.0 119 | Ar 6.0 1.0 1.0 120 | Ar 7.0 1.0 1.0 121 | Ar 8.0 1.0 1.0 122 | Ar 9.0 1.0 1.0 123 | Ar 0.0 2.0 1.0 124 | Ar 1.0 2.0 1.0 125 | Ar 2.0 2.0 1.0 126 | Ar 3.0 2.0 1.0 127 | Ar 4.0 2.0 1.0 128 | Ar 5.0 2.0 1.0 129 | Ar 6.0 2.0 1.0 130 | Ar 7.0 2.0 1.0 131 | Ar 8.0 2.0 1.0 132 | Ar 9.0 2.0 1.0 133 | Ar 0.0 3.0 1.0 134 | Ar 1.0 3.0 1.0 135 | Ar 2.0 3.0 1.0 136 | Ar 3.0 3.0 1.0 137 | Ar 4.0 3.0 1.0 138 | Ar 5.0 3.0 1.0 139 | Ar 6.0 3.0 1.0 140 | Ar 7.0 3.0 1.0 141 | Ar 8.0 3.0 1.0 142 | Ar 9.0 3.0 1.0 143 | Ar 0.0 4.0 1.0 144 | Ar 1.0 4.0 1.0 145 | Ar 2.0 4.0 1.0 146 | Ar 3.0 4.0 1.0 147 | Ar 4.0 4.0 1.0 148 | Ar 5.0 4.0 1.0 149 | Ar 6.0 4.0 1.0 150 | Ar 7.0 4.0 1.0 151 | Ar 8.0 4.0 1.0 152 | Ar 9.0 4.0 1.0 153 | Ar 0.0 5.0 1.0 154 | Ar 1.0 5.0 1.0 155 | Ar 2.0 5.0 1.0 156 | Ar 3.0 5.0 1.0 157 | Ar 4.0 5.0 1.0 158 | Ar 5.0 5.0 1.0 159 | Ar 6.0 5.0 1.0 160 | Ar 7.0 5.0 1.0 161 | Ar 8.0 5.0 1.0 162 | Ar 9.0 5.0 1.0 163 | Ar 0.0 6.0 1.0 164 | Ar 1.0 6.0 1.0 165 | Ar 2.0 6.0 1.0 166 | Ar 3.0 6.0 1.0 167 | Ar 4.0 6.0 1.0 168 | Ar 5.0 6.0 1.0 169 | Ar 6.0 6.0 1.0 170 | Ar 7.0 6.0 1.0 171 | Ar 8.0 6.0 1.0 172 | Ar 9.0 6.0 1.0 173 | Ar 0.0 7.0 1.0 174 | Ar 1.0 7.0 1.0 175 | Ar 2.0 7.0 1.0 176 | Ar 3.0 7.0 1.0 177 | Ar 4.0 7.0 1.0 178 | Ar 5.0 7.0 1.0 179 | Ar 6.0 7.0 1.0 180 | Ar 7.0 7.0 1.0 181 | Ar 8.0 7.0 1.0 182 | Ar 9.0 7.0 1.0 183 | Ar 0.0 8.0 1.0 184 | Ar 1.0 8.0 1.0 185 | Ar 2.0 8.0 1.0 186 | Ar 3.0 8.0 1.0 187 | Ar 4.0 8.0 1.0 188 | Ar 5.0 8.0 1.0 189 | Ar 6.0 8.0 1.0 190 | Ar 7.0 8.0 1.0 191 | Ar 8.0 8.0 1.0 192 | Ar 9.0 8.0 1.0 193 | Ar 0.0 9.0 1.0 194 | Ar 1.0 9.0 1.0 195 | Ar 2.0 9.0 1.0 196 | Ar 3.0 9.0 1.0 197 | Ar 4.0 9.0 1.0 198 | Ar 5.0 9.0 1.0 199 | Ar 6.0 9.0 1.0 200 | Ar 7.0 9.0 1.0 201 | Ar 8.0 9.0 1.0 202 | Ar 9.0 9.0 1.0 203 | Ar 0.0 0.0 2.0 204 | Ar 1.0 0.0 2.0 205 | Ar 2.0 0.0 2.0 206 | Ar 3.0 0.0 2.0 207 | Ar 4.0 0.0 2.0 208 | Ar 5.0 0.0 2.0 209 | Ar 6.0 0.0 2.0 210 | Ar 7.0 0.0 2.0 211 | Ar 8.0 0.0 2.0 212 | Ar 9.0 0.0 2.0 213 | Ar 0.0 1.0 2.0 214 | Ar 1.0 1.0 2.0 215 | Ar 2.0 1.0 2.0 216 | Ar 3.0 1.0 2.0 217 | Ar 4.0 1.0 2.0 218 | Ar 5.0 1.0 2.0 219 | Ar 6.0 1.0 2.0 220 | Ar 7.0 1.0 2.0 221 | Ar 8.0 1.0 2.0 222 | Ar 9.0 1.0 2.0 223 | Ar 0.0 2.0 2.0 224 | Ar 1.0 2.0 2.0 225 | Ar 2.0 2.0 2.0 226 | Ar 3.0 2.0 2.0 227 | Ar 4.0 2.0 2.0 228 | Ar 5.0 2.0 2.0 229 | Ar 6.0 2.0 2.0 230 | Ar 7.0 2.0 2.0 231 | Ar 8.0 2.0 2.0 232 | Ar 9.0 2.0 2.0 233 | Ar 0.0 3.0 2.0 234 | Ar 1.0 3.0 2.0 235 | Ar 2.0 3.0 2.0 236 | Ar 3.0 3.0 2.0 237 | Ar 4.0 3.0 2.0 238 | Ar 5.0 3.0 2.0 239 | Ar 6.0 3.0 2.0 240 | Ar 7.0 3.0 2.0 241 | Ar 8.0 3.0 2.0 242 | Ar 9.0 3.0 2.0 243 | Ar 0.0 4.0 2.0 244 | Ar 1.0 4.0 2.0 245 | Ar 2.0 4.0 2.0 246 | Ar 3.0 4.0 2.0 247 | Ar 4.0 4.0 2.0 248 | Ar 5.0 4.0 2.0 249 | Ar 6.0 4.0 2.0 250 | Ar 7.0 4.0 2.0 251 | Ar 8.0 4.0 2.0 252 | Ar 9.0 4.0 2.0 253 | Ar 0.0 5.0 2.0 254 | Ar 1.0 5.0 2.0 255 | Ar 2.0 5.0 2.0 256 | Ar 3.0 5.0 2.0 257 | Ar 4.0 5.0 2.0 258 | Ar 5.0 5.0 2.0 259 | Ar 6.0 5.0 2.0 260 | Ar 7.0 5.0 2.0 261 | Ar 8.0 5.0 2.0 262 | Ar 9.0 5.0 2.0 263 | Ar 0.0 6.0 2.0 264 | Ar 1.0 6.0 2.0 265 | Ar 2.0 6.0 2.0 266 | Ar 3.0 6.0 2.0 267 | Ar 4.0 6.0 2.0 268 | Ar 5.0 6.0 2.0 269 | Ar 6.0 6.0 2.0 270 | Ar 7.0 6.0 2.0 271 | Ar 8.0 6.0 2.0 272 | Ar 9.0 6.0 2.0 273 | Ar 0.0 7.0 2.0 274 | Ar 1.0 7.0 2.0 275 | Ar 2.0 7.0 2.0 276 | Ar 3.0 7.0 2.0 277 | Ar 4.0 7.0 2.0 278 | Ar 5.0 7.0 2.0 279 | Ar 6.0 7.0 2.0 280 | Ar 7.0 7.0 2.0 281 | Ar 8.0 7.0 2.0 282 | Ar 9.0 7.0 2.0 283 | Ar 0.0 8.0 2.0 284 | Ar 1.0 8.0 2.0 285 | Ar 2.0 8.0 2.0 286 | Ar 3.0 8.0 2.0 287 | Ar 4.0 8.0 2.0 288 | Ar 5.0 8.0 2.0 289 | Ar 6.0 8.0 2.0 290 | Ar 7.0 8.0 2.0 291 | Ar 8.0 8.0 2.0 292 | Ar 9.0 8.0 2.0 293 | Ar 0.0 9.0 2.0 294 | Ar 1.0 9.0 2.0 295 | Ar 2.0 9.0 2.0 296 | Ar 3.0 9.0 2.0 297 | Ar 4.0 9.0 2.0 298 | Ar 5.0 9.0 2.0 299 | Ar 6.0 9.0 2.0 300 | Ar 7.0 9.0 2.0 301 | Ar 8.0 9.0 2.0 302 | Ar 9.0 9.0 2.0 303 | Ar 0.0 0.0 3.0 304 | Ar 1.0 0.0 3.0 305 | Ar 2.0 0.0 3.0 306 | Ar 3.0 0.0 3.0 307 | Ar 4.0 0.0 3.0 308 | Ar 5.0 0.0 3.0 309 | Ar 6.0 0.0 3.0 310 | Ar 7.0 0.0 3.0 311 | Ar 8.0 0.0 3.0 312 | Ar 9.0 0.0 3.0 313 | Ar 0.0 1.0 3.0 314 | Ar 1.0 1.0 3.0 315 | Ar 2.0 1.0 3.0 316 | Ar 3.0 1.0 3.0 317 | Ar 4.0 1.0 3.0 318 | Ar 5.0 1.0 3.0 319 | Ar 6.0 1.0 3.0 320 | Ar 7.0 1.0 3.0 321 | Ar 8.0 1.0 3.0 322 | Ar 9.0 1.0 3.0 323 | Ar 0.0 2.0 3.0 324 | Ar 1.0 2.0 3.0 325 | Ar 2.0 2.0 3.0 326 | Ar 3.0 2.0 3.0 327 | Ar 4.0 2.0 3.0 328 | Ar 5.0 2.0 3.0 329 | Ar 6.0 2.0 3.0 330 | Ar 7.0 2.0 3.0 331 | Ar 8.0 2.0 3.0 332 | Ar 9.0 2.0 3.0 333 | Ar 0.0 3.0 3.0 334 | Ar 1.0 3.0 3.0 335 | Ar 2.0 3.0 3.0 336 | Ar 3.0 3.0 3.0 337 | Ar 4.0 3.0 3.0 338 | Ar 5.0 3.0 3.0 339 | Ar 6.0 3.0 3.0 340 | Ar 7.0 3.0 3.0 341 | Ar 8.0 3.0 3.0 342 | Ar 9.0 3.0 3.0 343 | Ar 0.0 4.0 3.0 344 | Ar 1.0 4.0 3.0 345 | Ar 2.0 4.0 3.0 346 | Ar 3.0 4.0 3.0 347 | Ar 4.0 4.0 3.0 348 | Ar 5.0 4.0 3.0 349 | Ar 6.0 4.0 3.0 350 | Ar 7.0 4.0 3.0 351 | Ar 8.0 4.0 3.0 352 | Ar 9.0 4.0 3.0 353 | Ar 0.0 5.0 3.0 354 | Ar 1.0 5.0 3.0 355 | Ar 2.0 5.0 3.0 356 | Ar 3.0 5.0 3.0 357 | Ar 4.0 5.0 3.0 358 | Ar 5.0 5.0 3.0 359 | Ar 6.0 5.0 3.0 360 | Ar 7.0 5.0 3.0 361 | Ar 8.0 5.0 3.0 362 | Ar 9.0 5.0 3.0 363 | Ar 0.0 6.0 3.0 364 | Ar 1.0 6.0 3.0 365 | Ar 2.0 6.0 3.0 366 | Ar 3.0 6.0 3.0 367 | Ar 4.0 6.0 3.0 368 | Ar 5.0 6.0 3.0 369 | Ar 6.0 6.0 3.0 370 | Ar 7.0 6.0 3.0 371 | Ar 8.0 6.0 3.0 372 | Ar 9.0 6.0 3.0 373 | Ar 0.0 7.0 3.0 374 | Ar 1.0 7.0 3.0 375 | Ar 2.0 7.0 3.0 376 | Ar 3.0 7.0 3.0 377 | Ar 4.0 7.0 3.0 378 | Ar 5.0 7.0 3.0 379 | Ar 6.0 7.0 3.0 380 | Ar 7.0 7.0 3.0 381 | Ar 8.0 7.0 3.0 382 | Ar 9.0 7.0 3.0 383 | Ar 0.0 8.0 3.0 384 | Ar 1.0 8.0 3.0 385 | Ar 2.0 8.0 3.0 386 | Ar 3.0 8.0 3.0 387 | Ar 4.0 8.0 3.0 388 | Ar 5.0 8.0 3.0 389 | Ar 6.0 8.0 3.0 390 | Ar 7.0 8.0 3.0 391 | Ar 8.0 8.0 3.0 392 | Ar 9.0 8.0 3.0 393 | Ar 0.0 9.0 3.0 394 | Ar 1.0 9.0 3.0 395 | Ar 2.0 9.0 3.0 396 | Ar 3.0 9.0 3.0 397 | Ar 4.0 9.0 3.0 398 | Ar 5.0 9.0 3.0 399 | Ar 6.0 9.0 3.0 400 | Ar 7.0 9.0 3.0 401 | Ar 8.0 9.0 3.0 402 | Ar 9.0 9.0 3.0 403 | Ar 0.0 0.0 4.0 404 | Ar 1.0 0.0 4.0 405 | Ar 2.0 0.0 4.0 406 | Ar 3.0 0.0 4.0 407 | Ar 4.0 0.0 4.0 408 | Ar 5.0 0.0 4.0 409 | Ar 6.0 0.0 4.0 410 | Ar 7.0 0.0 4.0 411 | Ar 8.0 0.0 4.0 412 | Ar 9.0 0.0 4.0 413 | Ar 0.0 1.0 4.0 414 | Ar 1.0 1.0 4.0 415 | Ar 2.0 1.0 4.0 416 | Ar 3.0 1.0 4.0 417 | Ar 4.0 1.0 4.0 418 | Ar 5.0 1.0 4.0 419 | Ar 6.0 1.0 4.0 420 | Ar 7.0 1.0 4.0 421 | Ar 8.0 1.0 4.0 422 | Ar 9.0 1.0 4.0 423 | Ar 0.0 2.0 4.0 424 | Ar 1.0 2.0 4.0 425 | Ar 2.0 2.0 4.0 426 | Ar 3.0 2.0 4.0 427 | Ar 4.0 2.0 4.0 428 | Ar 5.0 2.0 4.0 429 | Ar 6.0 2.0 4.0 430 | Ar 7.0 2.0 4.0 431 | Ar 8.0 2.0 4.0 432 | Ar 9.0 2.0 4.0 433 | Ar 0.0 3.0 4.0 434 | Ar 1.0 3.0 4.0 435 | Ar 2.0 3.0 4.0 436 | Ar 3.0 3.0 4.0 437 | Ar 4.0 3.0 4.0 438 | Ar 5.0 3.0 4.0 439 | Ar 6.0 3.0 4.0 440 | Ar 7.0 3.0 4.0 441 | Ar 8.0 3.0 4.0 442 | Ar 9.0 3.0 4.0 443 | Ar 0.0 4.0 4.0 444 | Ar 1.0 4.0 4.0 445 | Ar 2.0 4.0 4.0 446 | Ar 3.0 4.0 4.0 447 | Ar 4.0 4.0 4.0 448 | Ar 5.0 4.0 4.0 449 | Ar 6.0 4.0 4.0 450 | Ar 7.0 4.0 4.0 451 | Ar 8.0 4.0 4.0 452 | Ar 9.0 4.0 4.0 453 | Ar 0.0 5.0 4.0 454 | Ar 1.0 5.0 4.0 455 | Ar 2.0 5.0 4.0 456 | Ar 3.0 5.0 4.0 457 | Ar 4.0 5.0 4.0 458 | Ar 5.0 5.0 4.0 459 | Ar 6.0 5.0 4.0 460 | Ar 7.0 5.0 4.0 461 | Ar 8.0 5.0 4.0 462 | Ar 9.0 5.0 4.0 463 | Ar 0.0 6.0 4.0 464 | Ar 1.0 6.0 4.0 465 | Ar 2.0 6.0 4.0 466 | Ar 3.0 6.0 4.0 467 | Ar 4.0 6.0 4.0 468 | Ar 5.0 6.0 4.0 469 | Ar 6.0 6.0 4.0 470 | Ar 7.0 6.0 4.0 471 | Ar 8.0 6.0 4.0 472 | Ar 9.0 6.0 4.0 473 | Ar 0.0 7.0 4.0 474 | Ar 1.0 7.0 4.0 475 | Ar 2.0 7.0 4.0 476 | Ar 3.0 7.0 4.0 477 | Ar 4.0 7.0 4.0 478 | Ar 5.0 7.0 4.0 479 | Ar 6.0 7.0 4.0 480 | Ar 7.0 7.0 4.0 481 | Ar 8.0 7.0 4.0 482 | Ar 9.0 7.0 4.0 483 | Ar 0.0 8.0 4.0 484 | Ar 1.0 8.0 4.0 485 | Ar 2.0 8.0 4.0 486 | Ar 3.0 8.0 4.0 487 | Ar 4.0 8.0 4.0 488 | Ar 5.0 8.0 4.0 489 | Ar 6.0 8.0 4.0 490 | Ar 7.0 8.0 4.0 491 | Ar 8.0 8.0 4.0 492 | Ar 9.0 8.0 4.0 493 | Ar 0.0 9.0 4.0 494 | Ar 1.0 9.0 4.0 495 | Ar 2.0 9.0 4.0 496 | Ar 3.0 9.0 4.0 497 | Ar 4.0 9.0 4.0 498 | Ar 5.0 9.0 4.0 499 | Ar 6.0 9.0 4.0 500 | Ar 7.0 9.0 4.0 501 | Ar 8.0 9.0 4.0 502 | Ar 9.0 9.0 4.0 503 | Ar 0.0 0.0 5.0 504 | Ar 1.0 0.0 5.0 505 | Ar 2.0 0.0 5.0 506 | Ar 3.0 0.0 5.0 507 | Ar 4.0 0.0 5.0 508 | Ar 5.0 0.0 5.0 509 | Ar 6.0 0.0 5.0 510 | Ar 7.0 0.0 5.0 511 | Ar 8.0 0.0 5.0 512 | Ar 9.0 0.0 5.0 513 | Ar 0.0 1.0 5.0 514 | Ar 1.0 1.0 5.0 515 | Ar 2.0 1.0 5.0 516 | Ar 3.0 1.0 5.0 517 | Ar 4.0 1.0 5.0 518 | Ar 5.0 1.0 5.0 519 | Ar 6.0 1.0 5.0 520 | Ar 7.0 1.0 5.0 521 | Ar 8.0 1.0 5.0 522 | Ar 9.0 1.0 5.0 523 | Ar 0.0 2.0 5.0 524 | Ar 1.0 2.0 5.0 525 | Ar 2.0 2.0 5.0 526 | Ar 3.0 2.0 5.0 527 | Ar 4.0 2.0 5.0 528 | Ar 5.0 2.0 5.0 529 | Ar 6.0 2.0 5.0 530 | Ar 7.0 2.0 5.0 531 | Ar 8.0 2.0 5.0 532 | Ar 9.0 2.0 5.0 533 | Ar 0.0 3.0 5.0 534 | Ar 1.0 3.0 5.0 535 | Ar 2.0 3.0 5.0 536 | Ar 3.0 3.0 5.0 537 | Ar 4.0 3.0 5.0 538 | Ar 5.0 3.0 5.0 539 | Ar 6.0 3.0 5.0 540 | Ar 7.0 3.0 5.0 541 | Ar 8.0 3.0 5.0 542 | Ar 9.0 3.0 5.0 543 | Ar 0.0 4.0 5.0 544 | Ar 1.0 4.0 5.0 545 | Ar 2.0 4.0 5.0 546 | Ar 3.0 4.0 5.0 547 | Ar 4.0 4.0 5.0 548 | Ar 5.0 4.0 5.0 549 | Ar 6.0 4.0 5.0 550 | Ar 7.0 4.0 5.0 551 | Ar 8.0 4.0 5.0 552 | Ar 9.0 4.0 5.0 553 | Ar 0.0 5.0 5.0 554 | Ar 1.0 5.0 5.0 555 | Ar 2.0 5.0 5.0 556 | Ar 3.0 5.0 5.0 557 | Ar 4.0 5.0 5.0 558 | Ar 5.0 5.0 5.0 559 | Ar 6.0 5.0 5.0 560 | Ar 7.0 5.0 5.0 561 | Ar 8.0 5.0 5.0 562 | Ar 9.0 5.0 5.0 563 | Ar 0.0 6.0 5.0 564 | Ar 1.0 6.0 5.0 565 | Ar 2.0 6.0 5.0 566 | Ar 3.0 6.0 5.0 567 | Ar 4.0 6.0 5.0 568 | Ar 5.0 6.0 5.0 569 | Ar 6.0 6.0 5.0 570 | Ar 7.0 6.0 5.0 571 | Ar 8.0 6.0 5.0 572 | Ar 9.0 6.0 5.0 573 | Ar 0.0 7.0 5.0 574 | Ar 1.0 7.0 5.0 575 | Ar 2.0 7.0 5.0 576 | Ar 3.0 7.0 5.0 577 | Ar 4.0 7.0 5.0 578 | Ar 5.0 7.0 5.0 579 | Ar 6.0 7.0 5.0 580 | Ar 7.0 7.0 5.0 581 | Ar 8.0 7.0 5.0 582 | Ar 9.0 7.0 5.0 583 | Ar 0.0 8.0 5.0 584 | Ar 1.0 8.0 5.0 585 | Ar 2.0 8.0 5.0 586 | Ar 3.0 8.0 5.0 587 | Ar 4.0 8.0 5.0 588 | Ar 5.0 8.0 5.0 589 | Ar 6.0 8.0 5.0 590 | Ar 7.0 8.0 5.0 591 | Ar 8.0 8.0 5.0 592 | Ar 9.0 8.0 5.0 593 | Ar 0.0 9.0 5.0 594 | Ar 1.0 9.0 5.0 595 | Ar 2.0 9.0 5.0 596 | Ar 3.0 9.0 5.0 597 | Ar 4.0 9.0 5.0 598 | Ar 5.0 9.0 5.0 599 | Ar 6.0 9.0 5.0 600 | Ar 7.0 9.0 5.0 601 | Ar 8.0 9.0 5.0 602 | Ar 9.0 9.0 5.0 603 | Ar 0.0 0.0 6.0 604 | Ar 1.0 0.0 6.0 605 | Ar 2.0 0.0 6.0 606 | Ar 3.0 0.0 6.0 607 | Ar 4.0 0.0 6.0 608 | Ar 5.0 0.0 6.0 609 | Ar 6.0 0.0 6.0 610 | Ar 7.0 0.0 6.0 611 | Ar 8.0 0.0 6.0 612 | Ar 9.0 0.0 6.0 613 | Ar 0.0 1.0 6.0 614 | Ar 1.0 1.0 6.0 615 | Ar 2.0 1.0 6.0 616 | Ar 3.0 1.0 6.0 617 | Ar 4.0 1.0 6.0 618 | Ar 5.0 1.0 6.0 619 | Ar 6.0 1.0 6.0 620 | Ar 7.0 1.0 6.0 621 | Ar 8.0 1.0 6.0 622 | Ar 9.0 1.0 6.0 623 | Ar 0.0 2.0 6.0 624 | Ar 1.0 2.0 6.0 625 | Ar 2.0 2.0 6.0 626 | Ar 3.0 2.0 6.0 627 | Ar 4.0 2.0 6.0 628 | Ar 5.0 2.0 6.0 629 | Ar 6.0 2.0 6.0 630 | Ar 7.0 2.0 6.0 631 | Ar 8.0 2.0 6.0 632 | Ar 9.0 2.0 6.0 633 | Ar 0.0 3.0 6.0 634 | Ar 1.0 3.0 6.0 635 | Ar 2.0 3.0 6.0 636 | Ar 3.0 3.0 6.0 637 | Ar 4.0 3.0 6.0 638 | Ar 5.0 3.0 6.0 639 | Ar 6.0 3.0 6.0 640 | Ar 7.0 3.0 6.0 641 | Ar 8.0 3.0 6.0 642 | Ar 9.0 3.0 6.0 643 | Ar 0.0 4.0 6.0 644 | Ar 1.0 4.0 6.0 645 | Ar 2.0 4.0 6.0 646 | Ar 3.0 4.0 6.0 647 | Ar 4.0 4.0 6.0 648 | Ar 5.0 4.0 6.0 649 | Ar 6.0 4.0 6.0 650 | Ar 7.0 4.0 6.0 651 | Ar 8.0 4.0 6.0 652 | Ar 9.0 4.0 6.0 653 | Ar 0.0 5.0 6.0 654 | Ar 1.0 5.0 6.0 655 | Ar 2.0 5.0 6.0 656 | Ar 3.0 5.0 6.0 657 | Ar 4.0 5.0 6.0 658 | Ar 5.0 5.0 6.0 659 | Ar 6.0 5.0 6.0 660 | Ar 7.0 5.0 6.0 661 | Ar 8.0 5.0 6.0 662 | Ar 9.0 5.0 6.0 663 | Ar 0.0 6.0 6.0 664 | Ar 1.0 6.0 6.0 665 | Ar 2.0 6.0 6.0 666 | Ar 3.0 6.0 6.0 667 | Ar 4.0 6.0 6.0 668 | Ar 5.0 6.0 6.0 669 | Ar 6.0 6.0 6.0 670 | Ar 7.0 6.0 6.0 671 | Ar 8.0 6.0 6.0 672 | Ar 9.0 6.0 6.0 673 | Ar 0.0 7.0 6.0 674 | Ar 1.0 7.0 6.0 675 | Ar 2.0 7.0 6.0 676 | Ar 3.0 7.0 6.0 677 | Ar 4.0 7.0 6.0 678 | Ar 5.0 7.0 6.0 679 | Ar 6.0 7.0 6.0 680 | Ar 7.0 7.0 6.0 681 | Ar 8.0 7.0 6.0 682 | Ar 9.0 7.0 6.0 683 | Ar 0.0 8.0 6.0 684 | Ar 1.0 8.0 6.0 685 | Ar 2.0 8.0 6.0 686 | Ar 3.0 8.0 6.0 687 | Ar 4.0 8.0 6.0 688 | Ar 5.0 8.0 6.0 689 | Ar 6.0 8.0 6.0 690 | Ar 7.0 8.0 6.0 691 | Ar 8.0 8.0 6.0 692 | Ar 9.0 8.0 6.0 693 | Ar 0.0 9.0 6.0 694 | Ar 1.0 9.0 6.0 695 | Ar 2.0 9.0 6.0 696 | Ar 3.0 9.0 6.0 697 | Ar 4.0 9.0 6.0 698 | Ar 5.0 9.0 6.0 699 | Ar 6.0 9.0 6.0 700 | Ar 7.0 9.0 6.0 701 | Ar 8.0 9.0 6.0 702 | Ar 9.0 9.0 6.0 703 | Ar 0.0 0.0 7.0 704 | Ar 1.0 0.0 7.0 705 | Ar 2.0 0.0 7.0 706 | Ar 3.0 0.0 7.0 707 | Ar 4.0 0.0 7.0 708 | Ar 5.0 0.0 7.0 709 | Ar 6.0 0.0 7.0 710 | Ar 7.0 0.0 7.0 711 | Ar 8.0 0.0 7.0 712 | Ar 9.0 0.0 7.0 713 | Ar 0.0 1.0 7.0 714 | Ar 1.0 1.0 7.0 715 | Ar 2.0 1.0 7.0 716 | Ar 3.0 1.0 7.0 717 | Ar 4.0 1.0 7.0 718 | Ar 5.0 1.0 7.0 719 | Ar 6.0 1.0 7.0 720 | Ar 7.0 1.0 7.0 721 | Ar 8.0 1.0 7.0 722 | Ar 9.0 1.0 7.0 723 | Ar 0.0 2.0 7.0 724 | Ar 1.0 2.0 7.0 725 | Ar 2.0 2.0 7.0 726 | Ar 3.0 2.0 7.0 727 | Ar 4.0 2.0 7.0 728 | Ar 5.0 2.0 7.0 729 | Ar 6.0 2.0 7.0 730 | Ar 7.0 2.0 7.0 731 | Ar 8.0 2.0 7.0 732 | Ar 9.0 2.0 7.0 733 | Ar 0.0 3.0 7.0 734 | Ar 1.0 3.0 7.0 735 | Ar 2.0 3.0 7.0 736 | Ar 3.0 3.0 7.0 737 | Ar 4.0 3.0 7.0 738 | Ar 5.0 3.0 7.0 739 | Ar 6.0 3.0 7.0 740 | Ar 7.0 3.0 7.0 741 | Ar 8.0 3.0 7.0 742 | Ar 9.0 3.0 7.0 743 | Ar 0.0 4.0 7.0 744 | Ar 1.0 4.0 7.0 745 | Ar 2.0 4.0 7.0 746 | Ar 3.0 4.0 7.0 747 | Ar 4.0 4.0 7.0 748 | Ar 5.0 4.0 7.0 749 | Ar 6.0 4.0 7.0 750 | Ar 7.0 4.0 7.0 751 | Ar 8.0 4.0 7.0 752 | Ar 9.0 4.0 7.0 753 | Ar 0.0 5.0 7.0 754 | Ar 1.0 5.0 7.0 755 | Ar 2.0 5.0 7.0 756 | Ar 3.0 5.0 7.0 757 | Ar 4.0 5.0 7.0 758 | Ar 5.0 5.0 7.0 759 | Ar 6.0 5.0 7.0 760 | Ar 7.0 5.0 7.0 761 | Ar 8.0 5.0 7.0 762 | Ar 9.0 5.0 7.0 763 | Ar 0.0 6.0 7.0 764 | Ar 1.0 6.0 7.0 765 | Ar 2.0 6.0 7.0 766 | Ar 3.0 6.0 7.0 767 | Ar 4.0 6.0 7.0 768 | Ar 5.0 6.0 7.0 769 | Ar 6.0 6.0 7.0 770 | Ar 7.0 6.0 7.0 771 | Ar 8.0 6.0 7.0 772 | Ar 9.0 6.0 7.0 773 | Ar 0.0 7.0 7.0 774 | Ar 1.0 7.0 7.0 775 | Ar 2.0 7.0 7.0 776 | Ar 3.0 7.0 7.0 777 | Ar 4.0 7.0 7.0 778 | Ar 5.0 7.0 7.0 779 | Ar 6.0 7.0 7.0 780 | Ar 7.0 7.0 7.0 781 | Ar 8.0 7.0 7.0 782 | Ar 9.0 7.0 7.0 783 | Ar 0.0 8.0 7.0 784 | Ar 1.0 8.0 7.0 785 | Ar 2.0 8.0 7.0 786 | Ar 3.0 8.0 7.0 787 | Ar 4.0 8.0 7.0 788 | Ar 5.0 8.0 7.0 789 | Ar 6.0 8.0 7.0 790 | Ar 7.0 8.0 7.0 791 | Ar 8.0 8.0 7.0 792 | Ar 9.0 8.0 7.0 793 | Ar 0.0 9.0 7.0 794 | Ar 1.0 9.0 7.0 795 | Ar 2.0 9.0 7.0 796 | Ar 3.0 9.0 7.0 797 | Ar 4.0 9.0 7.0 798 | Ar 5.0 9.0 7.0 799 | Ar 6.0 9.0 7.0 800 | Ar 7.0 9.0 7.0 801 | Ar 8.0 9.0 7.0 802 | Ar 9.0 9.0 7.0 803 | Ar 0.0 0.0 8.0 804 | Ar 1.0 0.0 8.0 805 | Ar 2.0 0.0 8.0 806 | Ar 3.0 0.0 8.0 807 | Ar 4.0 0.0 8.0 808 | Ar 5.0 0.0 8.0 809 | Ar 6.0 0.0 8.0 810 | Ar 7.0 0.0 8.0 811 | Ar 8.0 0.0 8.0 812 | Ar 9.0 0.0 8.0 813 | Ar 0.0 1.0 8.0 814 | Ar 1.0 1.0 8.0 815 | Ar 2.0 1.0 8.0 816 | Ar 3.0 1.0 8.0 817 | Ar 4.0 1.0 8.0 818 | Ar 5.0 1.0 8.0 819 | Ar 6.0 1.0 8.0 820 | Ar 7.0 1.0 8.0 821 | Ar 8.0 1.0 8.0 822 | Ar 9.0 1.0 8.0 823 | Ar 0.0 2.0 8.0 824 | Ar 1.0 2.0 8.0 825 | Ar 2.0 2.0 8.0 826 | Ar 3.0 2.0 8.0 827 | Ar 4.0 2.0 8.0 828 | Ar 5.0 2.0 8.0 829 | Ar 6.0 2.0 8.0 830 | Ar 7.0 2.0 8.0 831 | Ar 8.0 2.0 8.0 832 | Ar 9.0 2.0 8.0 833 | Ar 0.0 3.0 8.0 834 | Ar 1.0 3.0 8.0 835 | Ar 2.0 3.0 8.0 836 | Ar 3.0 3.0 8.0 837 | Ar 4.0 3.0 8.0 838 | Ar 5.0 3.0 8.0 839 | Ar 6.0 3.0 8.0 840 | Ar 7.0 3.0 8.0 841 | Ar 8.0 3.0 8.0 842 | Ar 9.0 3.0 8.0 843 | Ar 0.0 4.0 8.0 844 | Ar 1.0 4.0 8.0 845 | Ar 2.0 4.0 8.0 846 | Ar 3.0 4.0 8.0 847 | Ar 4.0 4.0 8.0 848 | Ar 5.0 4.0 8.0 849 | Ar 6.0 4.0 8.0 850 | Ar 7.0 4.0 8.0 851 | Ar 8.0 4.0 8.0 852 | Ar 9.0 4.0 8.0 853 | Ar 0.0 5.0 8.0 854 | Ar 1.0 5.0 8.0 855 | Ar 2.0 5.0 8.0 856 | Ar 3.0 5.0 8.0 857 | Ar 4.0 5.0 8.0 858 | Ar 5.0 5.0 8.0 859 | Ar 6.0 5.0 8.0 860 | Ar 7.0 5.0 8.0 861 | Ar 8.0 5.0 8.0 862 | Ar 9.0 5.0 8.0 863 | Ar 0.0 6.0 8.0 864 | Ar 1.0 6.0 8.0 865 | Ar 2.0 6.0 8.0 866 | Ar 3.0 6.0 8.0 867 | Ar 4.0 6.0 8.0 868 | Ar 5.0 6.0 8.0 869 | Ar 6.0 6.0 8.0 870 | Ar 7.0 6.0 8.0 871 | Ar 8.0 6.0 8.0 872 | Ar 9.0 6.0 8.0 873 | Ar 0.0 7.0 8.0 874 | Ar 1.0 7.0 8.0 875 | Ar 2.0 7.0 8.0 876 | Ar 3.0 7.0 8.0 877 | Ar 4.0 7.0 8.0 878 | Ar 5.0 7.0 8.0 879 | Ar 6.0 7.0 8.0 880 | Ar 7.0 7.0 8.0 881 | Ar 8.0 7.0 8.0 882 | Ar 9.0 7.0 8.0 883 | Ar 0.0 8.0 8.0 884 | Ar 1.0 8.0 8.0 885 | Ar 2.0 8.0 8.0 886 | Ar 3.0 8.0 8.0 887 | Ar 4.0 8.0 8.0 888 | Ar 5.0 8.0 8.0 889 | Ar 6.0 8.0 8.0 890 | Ar 7.0 8.0 8.0 891 | Ar 8.0 8.0 8.0 892 | Ar 9.0 8.0 8.0 893 | Ar 0.0 9.0 8.0 894 | Ar 1.0 9.0 8.0 895 | Ar 2.0 9.0 8.0 896 | Ar 3.0 9.0 8.0 897 | Ar 4.0 9.0 8.0 898 | Ar 5.0 9.0 8.0 899 | Ar 6.0 9.0 8.0 900 | Ar 7.0 9.0 8.0 901 | Ar 8.0 9.0 8.0 902 | Ar 9.0 9.0 8.0 903 | Ar 0.0 0.0 9.0 904 | Ar 1.0 0.0 9.0 905 | Ar 2.0 0.0 9.0 906 | Ar 3.0 0.0 9.0 907 | Ar 4.0 0.0 9.0 908 | Ar 5.0 0.0 9.0 909 | Ar 6.0 0.0 9.0 910 | Ar 7.0 0.0 9.0 911 | Ar 8.0 0.0 9.0 912 | Ar 9.0 0.0 9.0 913 | Ar 0.0 1.0 9.0 914 | Ar 1.0 1.0 9.0 915 | Ar 2.0 1.0 9.0 916 | Ar 3.0 1.0 9.0 917 | Ar 4.0 1.0 9.0 918 | Ar 5.0 1.0 9.0 919 | Ar 6.0 1.0 9.0 920 | Ar 7.0 1.0 9.0 921 | Ar 8.0 1.0 9.0 922 | Ar 9.0 1.0 9.0 923 | Ar 0.0 2.0 9.0 924 | Ar 1.0 2.0 9.0 925 | Ar 2.0 2.0 9.0 926 | Ar 3.0 2.0 9.0 927 | Ar 4.0 2.0 9.0 928 | Ar 5.0 2.0 9.0 929 | Ar 6.0 2.0 9.0 930 | Ar 7.0 2.0 9.0 931 | Ar 8.0 2.0 9.0 932 | Ar 9.0 2.0 9.0 933 | Ar 0.0 3.0 9.0 934 | Ar 1.0 3.0 9.0 935 | Ar 2.0 3.0 9.0 936 | Ar 3.0 3.0 9.0 937 | Ar 4.0 3.0 9.0 938 | Ar 5.0 3.0 9.0 939 | Ar 6.0 3.0 9.0 940 | Ar 7.0 3.0 9.0 941 | Ar 8.0 3.0 9.0 942 | Ar 9.0 3.0 9.0 943 | Ar 0.0 4.0 9.0 944 | Ar 1.0 4.0 9.0 945 | Ar 2.0 4.0 9.0 946 | Ar 3.0 4.0 9.0 947 | Ar 4.0 4.0 9.0 948 | Ar 5.0 4.0 9.0 949 | Ar 6.0 4.0 9.0 950 | Ar 7.0 4.0 9.0 951 | Ar 8.0 4.0 9.0 952 | Ar 9.0 4.0 9.0 953 | Ar 0.0 5.0 9.0 954 | Ar 1.0 5.0 9.0 955 | Ar 2.0 5.0 9.0 956 | Ar 3.0 5.0 9.0 957 | Ar 4.0 5.0 9.0 958 | Ar 5.0 5.0 9.0 959 | Ar 6.0 5.0 9.0 960 | Ar 7.0 5.0 9.0 961 | Ar 8.0 5.0 9.0 962 | Ar 9.0 5.0 9.0 963 | Ar 0.0 6.0 9.0 964 | Ar 1.0 6.0 9.0 965 | Ar 2.0 6.0 9.0 966 | Ar 3.0 6.0 9.0 967 | Ar 4.0 6.0 9.0 968 | Ar 5.0 6.0 9.0 969 | Ar 6.0 6.0 9.0 970 | Ar 7.0 6.0 9.0 971 | Ar 8.0 6.0 9.0 972 | Ar 9.0 6.0 9.0 973 | Ar 0.0 7.0 9.0 974 | Ar 1.0 7.0 9.0 975 | Ar 2.0 7.0 9.0 976 | Ar 3.0 7.0 9.0 977 | Ar 4.0 7.0 9.0 978 | Ar 5.0 7.0 9.0 979 | Ar 6.0 7.0 9.0 980 | Ar 7.0 7.0 9.0 981 | Ar 8.0 7.0 9.0 982 | Ar 9.0 7.0 9.0 983 | Ar 0.0 8.0 9.0 984 | Ar 1.0 8.0 9.0 985 | Ar 2.0 8.0 9.0 986 | Ar 3.0 8.0 9.0 987 | Ar 4.0 8.0 9.0 988 | Ar 5.0 8.0 9.0 989 | Ar 6.0 8.0 9.0 990 | Ar 7.0 8.0 9.0 991 | Ar 8.0 8.0 9.0 992 | Ar 9.0 8.0 9.0 993 | Ar 0.0 9.0 9.0 994 | Ar 1.0 9.0 9.0 995 | Ar 2.0 9.0 9.0 996 | Ar 3.0 9.0 9.0 997 | Ar 4.0 9.0 9.0 998 | Ar 5.0 9.0 9.0 999 | Ar 6.0 9.0 9.0 1000 | Ar 7.0 9.0 9.0 1001 | Ar 8.0 9.0 9.0 1002 | Ar 9.0 9.0 9.0 1003 | -------------------------------------------------------------------------------- /test/methanol/methanol.json: -------------------------------------------------------------------------------- 1 | { 2 | "structure" : "test/methanol/cg.pdb", 3 | "trajectory" : "test/methanol/cg.trr", 4 | "kT" : 0.7 5 | } -------------------------------------------------------------------------------- /test/methanol/methanol.py: -------------------------------------------------------------------------------- 1 | from MDAnalysis import Universe 2 | from ForcePy import * 3 | 4 | 5 | fm = ForceMatch(Universe("cg.pdb", "cg.trr")) 6 | fm.u.trajectory.periodic = True 7 | ff = FileForce() 8 | pmesh = Mesh.UniformMesh(2,12,0.1) 9 | #pwf = SpectralForce(Pairwise, pmesh, Basis.UnitStep, build_repulsion(pmesh, 3, 500, 3)) 10 | pwf = SpectralForce(Pairwise, pmesh, Basis.UnitStep) 11 | #pwf = SpectralForce(Pairwise, pmesh, Basis.Gaussian(pmesh, 0.1)) 12 | #pwf.add_regularizer(L2Regularizer) 13 | #pwf.add_regularizer(SmoothRegularizer) 14 | fm.add_ref_force(ff) 15 | fm.add_and_type_pair(pwf) 16 | 17 | fm.force_match(1) 18 | fm.finalize() 19 | fm.write('batch_1', table_points=100) 20 | fm.finalize() 21 | 22 | fm.force_match(10) 23 | fm.finalize() 24 | fm.write('batch_10', table_points=100) 25 | fm.finalize() 26 | 27 | fm.force_match(100) 28 | fm.finalize() 29 | fm.write('batch_100', table_points=100) 30 | fm.finalize() 31 | 32 | fm.force_match() 33 | fm.finalize() 34 | fm.write('batch_all', table_points=100) 35 | -------------------------------------------------------------------------------- /test/test_basis.py: -------------------------------------------------------------------------------- 1 | from ForcePy import * 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | mesh = UniformMesh(0,10,5) 6 | finemesh = UniformMesh(0,11,0.1) 7 | basis = Basis.Quartic(mesh, 1) 8 | 9 | xval = [finemesh[x] for x in range(len(finemesh))] 10 | yval = [np.sum(basis.potential(x, mesh)) for x in xval] 11 | 12 | plt.plot(xval, yval) 13 | plt.show() 14 | -------------------------------------------------------------------------------- /test/water/hbnum.obs: -------------------------------------------------------------------------------- 1 | 1451 3079 2 | 1317 3427 3 | 1269 3685 4 | 1269 3765 5 | 1245 3863 6 | 1248 3842 7 | 1286 3816 8 | 1238 3850 9 | 1266 3822 10 | 1239 3733 11 | 1259 3599 12 | 1232 3636 13 | 1264 3616 14 | 1275 3569 15 | 1286 3636 16 | 1284 3628 17 | 1308 3548 18 | 1277 3583 19 | 1298 3672 20 | 1305 3663 21 | 1337 3595 22 | 1311 3571 23 | 1320 3544 24 | 1317 3489 25 | 1305 3647 26 | 1352 3502 27 | 1320 3606 28 | 1361 3529 29 | 1350 3528 30 | 1341 3541 31 | 1356 3536 32 | 1358 3496 33 | 1371 3485 34 | 1346 3502 35 | 1368 3426 36 | 1358 3484 37 | 1369 3477 38 | 1392 3328 39 | 1354 3424 40 | 1362 3426 41 | 1360 3430 42 | 1385 3445 43 | 1371 3439 44 | 1383 3413 45 | 1367 3487 46 | 1364 3490 47 | 1404 3436 48 | 1414 3420 49 | 1387 3403 50 | 1398 3400 51 | 1403 3483 52 | 1404 3400 53 | 1418 3416 54 | 1420 3390 55 | 1400 3410 56 | 1416 3326 57 | 1439 3299 58 | 1446 3300 59 | 1406 3402 60 | 1429 3349 61 | 1449 3341 62 | 1455 3257 63 | 1443 3339 64 | 1453 3297 65 | 1459 3345 66 | 1459 3289 67 | 1445 3303 68 | 1450 3350 69 | 1485 3243 70 | 1458 3250 71 | 1452 3328 72 | 1456 3304 73 | 1452 3280 74 | 1444 3224 75 | 1493 3283 76 | 1487 3281 77 | 1465 3307 78 | 1472 3238 79 | 1506 3246 80 | 1511 3203 81 | 1495 3299 82 | 1473 3329 83 | 1493 3257 84 | 1503 3315 85 | 1468 3268 86 | 1470 3238 87 | 1496 3222 88 | 1500 3224 89 | 1506 3294 90 | 1485 3303 91 | 1494 3292 92 | 1484 3218 93 | 1499 3209 94 | 1504 3176 95 | 1517 3267 96 | 1518 3202 97 | 1509 3207 98 | 1502 3250 99 | 1513 3249 100 | 1537 3189 101 | 1508 3206 102 | -------------------------------------------------------------------------------- /test/water/lammps.inp: -------------------------------------------------------------------------------- 1 | #Energy minimization 2 | min_style cg 3 | 4 | compute bdist all bond/local dist 5 | compute bdist_avg all reduce ave c_bdist 6 | 7 | thermo_style custom step time temp pe ke etotal epair ebond c_bdist_avg 8 | 9 | thermo 50 10 | dump traj_dump all dcd 1 traj.dcd 11 | minimize 1.0e-6 0.001 1000 1000 12 | 13 | 14 | #fix O-H bond 15 | #fix oh_shake all shake 0.01 3 0 b 1 16 | 17 | #NVE 18 | velocity all create 300.0 49032 mom yes dist gaussian 19 | thermo 1 20 | timestep 2 # 2 fs 21 | fix all_nve all nve 22 | run 100 -------------------------------------------------------------------------------- /test/water/spc.tpr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitead/ForcePy/de7858e2e3fb56e24794beee367d36516cc1eb20/test/water/spc.tpr -------------------------------------------------------------------------------- /test/water/spc_cg.json: -------------------------------------------------------------------------------- 1 | { 2 | "kT" : 0.7, 3 | "observable": "test/water/hbnum.obs", 4 | "observable_set": 1400 5 | } -------------------------------------------------------------------------------- /test/water/spc_cg.py: -------------------------------------------------------------------------------- 1 | from MDAnalysis import Universe 2 | from ForcePy import * 3 | import random 4 | 5 | cgu = CGUniverse(Universe("spc.tpr", "traj.trr"), ['name OW', 'name HW1 or name HW2'], ['O', 'H2'], collapse_hydrogens=False) 6 | #cgu = CGUniverse(Universe("spc.tpr", "traj.trr"), ['all'], ['HOH'], collapse_hydrogens=False) 7 | add_residue_bonds(cgu, "name O", "name H2") 8 | fm = ForceMatch(cgu) 9 | ff = FileForce() 10 | pair_mesh = Mesh.UniformMesh(0.1,10,0.2) 11 | pwf = SpectralForce(Pairwise, pair_mesh, Basis.Quartic(pair_mesh, 0.5)) 12 | bwf = FixedHarmonicForce(Bond, 450*4.14) 13 | 14 | pwf.add_regularizer(SmoothRegularizer) 15 | fm.add_ref_force(ff) 16 | #fm.add_and_type_pair(pwf) 17 | fm.add_and_type_states(pwf,lambda x: random.choice([[1,0],[0.01, 0.99]]), ["A", "B"]) 18 | fm.add_and_type_pair(bwf) 19 | fm.force_match() 20 | cgu.write_lammps_scripts(fm, folder='lammps', table_points=100, lammps_input_file="lammps.inp") 21 | #fm.observation_match(obs_sweeps = 3, obs_samples = 5) 22 | -------------------------------------------------------------------------------- /test/water/traj.trr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitead/ForcePy/de7858e2e3fb56e24794beee367d36516cc1eb20/test/water/traj.trr --------------------------------------------------------------------------------