def _read_tortors(self, f): """ Read the Torsion-Torsion parameters """ self.tortor_list = TorsionTorsionList() # Eat the next 3 lines f.readline(); f.readline(); f.readline() line = f.readline() for i in range(self.pointers['ntortor']): try: int(line[0:6]) at1 = int(line[9:15]) - 1 at2 = int(line[15:21]) - 1 at3 = int(line[21:27]) - 1 at4 = int(line[27:33]) - 1 at5 = int(line[33:39]) - 1 dim1 = int(line[49:55]) dim2 = int(line[55:61]) self.tortor_list.add(self.atom_list[at1], self.atom_list[at2], self.atom_list[at3], self.atom_list[at4], self.atom_list[at5], dim1, dim2) except ValueError: raise TinkerError('Error parsing torsion-torsion term') line = f.readline() # The CMAP section was adjusted to print out the entire torsion # grid under each tor-tor definition. If this line has 3 words, we # need to eat the next dim1*dim2 lines if len(line.split()) == 3: gridvals = [] for j in range(dim1*dim2): gridvals.append(tuple([float(x) for x in line.split()])) line = f.readline() self.tortor_list[-1].type = TorsionTorsionGrid.new(gridvals)
def _process_cmap(struct, force): """ Adds CMAPs to the structure """ # store the list of cmap types cmap_types = [] for ii in range(force.getNumMaps()): size, grid = force.getMapParameters(ii) # Future-proof in case units start getting added to these maps if u.is_quantity(grid): typ = CmapType(size, grid) else: typ = CmapType(size, grid*u.kilojoules_per_mole) cmap_types.append(typ) typ.grid = typ.grid.T.switch_range() typ.used = False # Add all cmaps for ii in range(force.getNumTorsions()): mapidx, ii, ij, ik, il, ji, jj, jk, jl = force.getTorsionParameters(ii) if ij != ji or ik != jj or il != jk: warnings.warn('Non-continuous CMAP torsions detected. Not ' 'supported.', OpenMMWarning) continue ai, aj, ak = struct.atoms[ii], struct.atoms[ij], struct.atoms[ik] al, am = struct.atoms[il], struct.atoms[jl] cmap_type = cmap_types[mapidx] cmap_type.used = True struct.cmaps.append(Cmap(ai, aj, ak, al, am, type=cmap_type)) for cmap_type in cmap_types: if cmap_type.used: struct.cmap_types.append(cmap_type) struct.cmap_types.claim()
def _initializeConstants(self, simulation): """ Initialize a set of constants required for the reports Parameters ---------- simulation : Simulation The simulation to generate a report for """ import simtk.openmm as mm system = simulation.system frclist = system.getForces() if self._temperature: # Compute the number of degrees of freedom. dof = 0 for i in range(system.getNumParticles()): if system.getParticleMass(i) > 0*u.dalton: dof += 3 dof -= system.getNumConstraints() if any(isinstance(frc, mm.CMMotionRemover) for frc in frclist): dof -= 3 self._dof = dof if self._density: if self._totalMass is None: # Compute the total system mass. self._totalMass = 0*u.dalton for i in range(system.getNumParticles()): self._totalMass += system.getParticleMass(i) elif not u.is_quantity(self._totalMass): self._totalMass = self._totalMass*u.dalton
def __mul__(self, rhs): """ Matrix multiplication. >>> a = MyMatrix([[1,2],[3,4]]) >>> b = MyMatrix([[5,6],[7,8]]) >>> print(a) [[1, 2] [3, 4]] >>> print(b) [[5, 6] [7, 8]] >>> print(a*b) [[19, 22] [43, 50]] """ m = self.numRows() n = len(rhs[0]) r = len(rhs) if self.numCols() != r: raise ArithmeticError("Matrix multplication size mismatch (%d vs %d)" % (self.numCols(), r)) result = zeros(m, n) for i in range(m): for j in range(n): for k in range(r): result[i][j] += self[i][k]*rhs[k][j] return result
def id_format(filename): """ Identifies the file type as an Amber mdcrd file Parameters ---------- filename : str Name of the file to check format for Returns ------- is_fmt : bool True if it is an Amber mdcrd file. False otherwise """ f = genopen(filename, 'r') lines = [f.readline() for i in range(5)] f.close() # Next 4 lines, make sure we have %8.3f format try: for i in range(4): i += 1 for j in range(10): j8 = j * 8 if lines[i][j8+4] != '.': return False float(lines[i][j8:j8+8]) if lines[i][j8+7] not in '0123456789': return False except (IndexError, ValueError): return False # Must be a mdcrd return True
def test_atom_serialization(self): """ Tests the serialization of Atom """ atom = pmd.Atom( atomic_number=random.randint(1, 100), name=random.choice(uppercase) + random.choice(uppercase), type=random.choice(uppercase) + random.choice(uppercase), charge=random.random() * 2 - 1, mass=random.random() * 30 + 1, nb_idx=random.randint(1, 20), radii=random.random() * 2, screen=random.random() * 2, tree="M", join=random.random() * 2, irotat=random.random(), occupancy=random.random(), bfactor=random.random() * 10, altloc=random.choice(uppercase), rmin=random.random() * 2, epsilon=random.random() / 2, rmin14=random.random() * 2, epsilon14=random.random() / 2, ) atom.xx, atom.xy, atom.xz = (random.random() * 100 - 50 for i in range(3)) atom.number = random.randint(1, 100) atom.vx, atom.vy, atom.vz = (random.random() * 100 - 50 for i in range(3)) atom.multipoles = np.random.rand(10) * 10 fobj = BytesIO() pickle.dump(atom, fobj) fobj.seek(0) unpickled = pickle.load(fobj) self.assertIsInstance(unpickled, pmd.Atom) self._equal_atoms(unpickled, atom)
def _write_omm_cmaps(self, dest, skip_types): if not self.cmap_types: return dest.write(" <CmapTorsionForce>\n") maps = dict() counter = 0 econv = u.kilocalorie.conversion_factor_to(u.kilojoule) for _, cmap in iteritems(self.cmap_types): if id(cmap) in maps: continue maps[id(cmap)] = counter counter += 1 dest.write(" <Map>\n") grid = cmap.grid.switch_range().T for i in range(cmap.resolution): dest.write(" ") base = i * cmap.resolution for j in range(cmap.resolution): dest.write(" %s" % (grid[base + j] * econv)) dest.write("\n") dest.write(" </Map>\n") used_torsions = set() for (a1, a2, a3, a4, _, _, _, a5), cmap in iteritems(self.cmap_types): if any((a in skip_types for a in (a1, a2, a3, a4, a5))): continue if (a1, a2, a3, a4, a5) in used_torsions: continue used_torsions.add((a1, a2, a3, a4, a5)) used_torsions.add((a5, a4, a3, a2, a1)) dest.write( ' <Torsion map="%d" type1="%s" type2="%s" ' 'type3="%s" type4="%s" type5="%s"/>\n' % (maps[id(cmap)], a1, a2, a3, a4, a5) ) dest.write(" </CmapTorsionForce>\n")
def _check_written_mdcrds(self, box): # Now try to read them and verify the information crd = asciicrd.AmberMdcrd(get_fn('testc.mdcrd', written=True), 15, False, 'r') self.assertEqual(crd.title, 'Test file') self.assertFalse(crd.hasbox) for i in range(crd.frame): shape = crd.coordinates[i].shape refcrd = np.arange(45) + i np.testing.assert_equal(crd.coordinates[i], refcrd.reshape(shape)) for i, array in enumerate(crd.coordinates): refcrd = (np.arange(45) + i).reshape(array.shape) np.testing.assert_equal(array, refcrd) crd.close() crd = asciicrd.AmberMdcrd(get_fn('testcb.mdcrd', written=True), 18, True, 'r') self.assertEqual(crd.title, 'Test file') self.assertTrue(crd.hasbox) for i in range(crd.frame): refcrd = (np.arange(54) + i).reshape(crd.coordinates[i].shape) np.testing.assert_equal(crd.coordinates[i], refcrd) np.testing.assert_equal(crd.box[i], box) for i, (coords, mybox) in enumerate(zip(crd.coordinates, crd.box)): np.testing.assert_equal(coords, (np.arange(54)+i).reshape(coords.shape)) np.testing.assert_equal(mybox, box)
def _write_omm_cmaps(self, xml_root, skip_types): if not self.cmap_types: return xml_force = etree.SubElement(xml_root, 'CMAPTorsionForce') maps = dict() counter = 0 econv = u.kilocalorie.conversion_factor_to(u.kilojoule) for _, cmap in iteritems(self.cmap_types): if id(cmap) in maps: continue maps[id(cmap)] = counter counter += 1 xml_map = etree.SubElement(xml_force, 'Map') grid = cmap.grid.switch_range().T map_string = '' for i in range(cmap.resolution): base = i * cmap.resolution for j in range(cmap.resolution): map_string += ' %s' % (grid[base+j]*econv) map_string += '\n' xml_map.text = map_string used_torsions = set() for (a1, a2, a3, a4, _, _, _, a5), cmap in iteritems(self.cmap_types): if any((a in skip_types for a in (a1, a2, a3, a4, a5))): continue if (a1, a2, a3, a4, a5) in used_torsions: continue used_torsions.add((a1, a2, a3, a4, a5)) used_torsions.add((a5, a4, a3, a2, a1)) etree.SubElement(xml_force, 'Torsion', map=str(maps[id(cmap)]), type1=a1, type2=a2, type3=a3, type4=a4, type5=a5)
def _check_written_restarts(self, box): # Now try to read them and verify the information (keep in mind that the # restart velocities are scaled down then back up, so you'll need to use # assertAlmostEqual in this case). rst = readparm.Rst7.open(get_fn('testc.rst7', written=True)) self.assertFalse(rst.hasbox) self.assertFalse(rst.hasvels) np.testing.assert_equal(rst.coordinates.flatten(), list(range(27))) rst = readparm.Rst7.open(get_fn('testcb.rst7', written=True)) self.assertTrue(rst.hasbox) self.assertFalse(rst.hasvels) np.testing.assert_equal(rst.coordinates.flatten(), list(range(21))) np.testing.assert_equal(rst.box.flatten(), box) rst = readparm.Rst7.open(get_fn('testcv.rst7', written=True)) self.assertTrue(rst.hasvels) self.assertFalse(rst.hasbox) np.testing.assert_equal(rst.coordinates, np.arange(60).reshape(rst.coordinates.shape)) np.testing.assert_allclose(rst.velocities, np.array(list(reversed(range(60)))).reshape(rst.velocities.shape)) rst = readparm.Rst7.open(get_fn('testcvb.rst7', written=True)) self.assertTrue(rst.hasvels) self.assertTrue(rst.hasbox) np.testing.assert_equal(rst.coordinates, np.arange(45).reshape(rst.coordinates.shape)) np.testing.assert_allclose(rst.velocities, np.array(list(reversed(range(45)))).reshape(rst.velocities.shape)) np.testing.assert_equal(rst.box, box)
def setValue(self,value,attributeName=None,rowIndex=None): if attributeName is None: attribute=self.__currentAttribute else: attribute=attributeName if rowIndex is None: rowI = self.__currentRowIndex else: rowI = rowIndex if isinstance(attribute, str) and isinstance(rowI,int): try: # if row index is out of range - add the rows - for ii in range(rowI+1 - len(self._rowList)): self._rowList.append(self.__emptyRow()) # self._rowList[rowI][attribute]=value ll=len(self._rowList[rowI]) ind=self._attributeNameList.index(attribute) # extend the list if needed - if ( ind >= ll): self._rowList[rowI].extend([None for ii in range(2*ind -ll)]) self._rowList[rowI][ind]=value except (IndexError): self.__lfh.write("DataCategory(setvalue) index error category %s attribute %s index %d value %r\n" % (self._name,attribute,rowI,value)) traceback.print_exc(file=self.__lfh) #raise IndexError except (ValueError): self.__lfh.write("DataCategory(setvalue) value error category %s attribute %s index %d value %r\n" % (self._name,attribute,rowI,value)) traceback.print_exc(file=self.__lfh)
def testAmberMdcrd(self): """ Test writing ASCII trajectory file """ box = [15, 15, 15] Mdcrd = asciicrd.AmberMdcrd crd = Mdcrd(get_fn('testc.mdcrd', written=True), natom=15, hasbox=False, mode='w', title='Test file') crd.add_coordinates(list(range(45))) crd.add_coordinates([x+1 for x in range(45)]) crd.add_coordinates([x+2 for x in range(45)]) crd.add_coordinates([x+3 for x in range(45)]) crd.add_coordinates([x+4 for x in range(45)]) crd.close() crd = Mdcrd(get_fn('testcb.mdcrd', written=True), natom=18, hasbox=True, mode='w', title='Test file') crd.add_coordinates(list(range(54))) crd.add_box(box) crd.add_coordinates([x+1 for x in range(54)]) crd.add_box(box) crd.add_coordinates([x+2 for x in range(54)]) crd.add_box(box) crd.add_coordinates([x+3 for x in range(54)]) crd.add_box(box) crd.add_coordinates([x+4 for x in range(54)]) crd.add_box(box) crd.close() self._check_written_mdcrds(box)
def getAttributeValueMaxLengthList(self,steps=1): mList=[0 for i in range(len(self._attributeNameList))] for row in self._rowList[::steps]: for indx in range(len(self._attributeNameList)): val=row[indx] mList[indx] = max(mList[indx],len(str(val))) return mList
def getFormatTypeList(self,steps=1): try: curDataTypeList=['DT_NULL_VALUE' for i in range(len(self._attributeNameList))] for row in self._rowList[::steps]: for indx in range(len(self._attributeNameList)): val=row[indx] # print "index ",indx," val ",val dType=self.__dataTypePdbx(val) dIndx=self.__dataTypeList.index(dType) # print "d type", dType, " d type index ",dIndx cType=curDataTypeList[indx] cIndx=self.__dataTypeList.index(cType) cIndx= max(cIndx,dIndx) curDataTypeList[indx]=self.__dataTypeList[cIndx] # Map the format types to the data types curFormatTypeList=[] for dt in curDataTypeList: ii=self.__dataTypeList.index(dt) curFormatTypeList.append(self.__formatTypeList[ii]) except: self.__lfh.write("PdbxDataCategory(getFormatTypeList) ++Index error at index %d in row %r\n" % (indx,row)) return curFormatTypeList,curDataTypeList
def test_tracked_list(self): """ Test the TrackedList class """ items = TrackedList() items.extend([Atom() for i in range(100)]) for atom in items: self.assertEqual(atom.idx, -1) self.assertIs(atom.list, None) items.claim() for i, atom in enumerate(items): self.assertEqual(atom.idx, i) self.assertTrue(items.changed) self.assertFalse(items.needs_indexing) items.changed = False self.assertFalse(items.changed) all_atoms = items[:] for atom in all_atoms: self.assertIsNot(atom.list, all_atoms) self.assertIn(atom, items) while items: atom = items.pop(random.choice(range(len(items)))) for item in items: self.assertIsNot(item, atom) self.assertEqual(atom.idx, -1) self.assertTrue(items.changed) items.changed = False # Now test the remove method self.assertFalse(items.changed) items.append(Atom()) self.assertTrue(items.changed) items.changed = False items.remove(items[0]) self.assertTrue(items.changed)
def id_format(filename): """ Identifies the file type as either Amber-format file (like prmtop) or an old-style topology file. Parameters ---------- filename : str Name of the file to check format for Returns ------- is_fmt : bool True if it is an Amber-style format, False otherwise """ if isinstance(filename, string_types): with closing(genopen(filename, 'r')) as f: lines = [f.readline() for i in range(5)] elif (hasattr(filename, 'readline') and hasattr(filename, 'seek') and hasattr(filename, 'tell')): cur = filename.tell() lines = [filename.readline() for i in range(5)] filename.seek(cur) if lines[0].startswith('%VERSION'): return True # Try old-style format try: return AmberFormat().rdparm_old(lines, check=True) except ValueError: return False
def __init__(self, units): self.units = units self._unit_conversion_cache = {} # Create a set of base units to be used for dimension conversion base_units = {} for unit in self.units: for base_unit, exponent in unit.iter_base_units(): d = base_unit.dimension if d not in base_units: base_units[d] = base_unit self.base_units = base_units if not len(self.base_units) == len(self.units): raise ArithmeticError("UnitSystem must have same number of units as base dimensions") # self.dimensions is a dict of {BaseDimension: index} dimensions = list(base_units.keys()) dimensions.sort() self.dimensions = {} for d in range(len(dimensions)): self.dimensions[dimensions[d]] = d # Create units->base units exponent matrix to_base_units = zeros(len(self.units)) for m in range(len(self.units)): unit = self.units[m] for dim, power in unit.iter_base_dimensions(): n = self.dimensions[dim] to_base_units[m][n] = power try: self.from_base_units = ~to_base_units except ArithmeticError as e: raise ArithmeticError("UnitSystem is not a valid basis set. " + str(e))
def __neg__(self): m = self.numRows() n = self.numCols() result = zeros(m, n) for i in range(m): for j in range(n): result[i][j] = -self[i][j] return result
def id_format(filename): """ Identifies the file type as an Amber restart/inpcrd file Parameters ---------- filename : str Name of the file to check format for Returns ------- is_fmt : bool True if it is an Amber restart/inpcrd file. False otherwise """ f = genopen(filename, 'r') lines = [f.readline() for i in range(5)] f.close() # Look for natom words = lines[1].split() if len(words) > 2 or len(words) < 1: return False try: natom = int(words[0]) float(words[1]) except ValueError: return False except IndexError: pass # Next 3 lines, make sure we have %12.7f format. This only works if we # have at least 6 atoms. Any fewer than that means the restart file is # shorter than that. try: if natom <= 0: return False i = 0 for line in lines[2:]: i += 1 if i > natom: break for j in range(3): j12 = j * 12 if line[j12+4] != '.': return False float(line[j12:j12+12]) if line[j12+11] not in '0123456789': return False i += 1 if i > natom: break for j in range(3): j12 = j * 12 + 36 if line[j12+4] != '.': return False float(line[j12:j12+12]) if line[j12+11] not in '0123456789': return False except (IndexError, ValueError): return False # Must be a restart... return True
def getFullRow(self,index): """ Return a full row based on the length of the the attribute list. """ try: if (len(self._rowList[index]) < self._numAttributes): for ii in range( self._numAttributes-len(self._rowList[index])): self._rowList[index].append('?') return self._rowList[index] except: return ['?' for ii in range(self._numAttributes)]
def testCoordinates(self): """ Tests coordinate handling in Structure """ s = create_random_structure(parametrized=False) self.assertIs(s.coordinates, None) natom = len(s.atoms) # Make sure coordinates will be generated from Atom.xx, xy, xz if not # otherwise generated xyz = np.random.random((natom, 3)) for a, x in zip(s.atoms, xyz): a.xx, a.xy, a.xz = x self.assertEqual(s.coordinates.shape, (natom, 3)) np.testing.assert_equal(s.coordinates, xyz[:,:]) self.assertIs(s._coordinates, None) # Now set multiple frames xyz = np.random.random((5, natom, 3)).tolist() s.coordinates = xyz self.assertIsInstance(s.coordinates, np.ndarray) self.assertEqual(s.coordinates.shape, (natom, 3)) for a, x in zip(s.atoms, xyz[0]): self.assertEqual(a.xx, x[0]) self.assertEqual(a.xy, x[1]) self.assertEqual(a.xz, x[2]) np.testing.assert_equal(s.get_coordinates('all'), xyz) for i in range(5): np.testing.assert_equal(s.get_coordinates(i), xyz[i]) # Now try setting with units xyz = u.Quantity(np.random.random((3, natom, 3)), u.nanometers) s.coordinates = xyz self.assertIsInstance(s.coordinates, np.ndarray) self.assertEqual(s.coordinates.shape, (natom, 3)) for a, x in zip(s.atoms, xyz[0]._value): self.assertEqual(a.xx, x[0]*10) self.assertEqual(a.xy, x[1]*10) self.assertEqual(a.xz, x[2]*10) # Now check setting None s.coordinates = None for a in s.atoms: self.assertFalse(hasattr(a, 'xx')) self.assertFalse(hasattr(a, 'xy')) self.assertFalse(hasattr(a, 'xz')) self.assertIs(s.coordinates, None) # Now check setting flattened arrays s.coordinates = np.random.random((natom, 3)) self.assertEqual(s.coordinates.shape, (natom, 3)) s.coordinates = np.random.random(natom*3) self.assertEqual(s.coordinates.shape, (natom, 3)) s.coordinates = np.random.random(natom*3*10) self.assertEqual(s.coordinates.shape, (natom, 3)) # Now check other iterables old_crds = s.coordinates s.coordinates = (random.random() for i in range(3*len(s.atoms))) self.assertEqual(s.coordinates.shape, (natom, 3)) diff = (old_crds - s.coordinates).ravel()**2 self.assertGreater(diff.sum(), 0.01)
def read(self, line): """ Reads the line and returns the converted data """ line = line.rstrip() nitems = int(ceil(len(line) / self.itemlen)) ret = [0 for i in range(nitems)] start, end = 0, self.itemlen for i in range(nitems): ret[i] = self.process_method(self.type(line[start:end])) start = end end += self.itemlen return ret
def _parse(self): """ This method parses the data out of the mdcrd file and creates self._coordinates as a list of np.ndarray(1, natom, 3) and self.cell_lengths as a list of np.ndarray(3) for each frame in the trajectory. This method is called automatically for 'old' trajectory files and should not be called by external callers. """ line = self._file.readline() self.title = line.strip() self._coordinates = np.ndarray(0) self.cell_lengths = np.ndarray(0) mainiter = range(0, 8*self.CRDS_PER_LINE, 8) extraiter = range(0, 8*self._nextras, 8) try: while line: frame = np.zeros(self.natom*3) if not line: raise _FileEOF() cell = np.zeros(3) idx = 0 line = self._file.readline() if not line: raise StopIteration() for i in range(self._full_lines_per_frame): if not line: raise _FileEOF() frame[idx:idx+10] = [float(line[j:j+8]) for j in mainiter] idx += 10 line = self._file.readline() if self._nextras: frame[idx:idx+self._nextras] = \ [float(line[j:j+8]) for j in extraiter] if self.hasbox: line = self._file.readline() if not line: raise _FileEOF() cell[0] = float(line[:8]) cell[1] = float(line[8:16]) cell[2] = float(line[16:24]) self._coordinates = np.concatenate((self._coordinates, frame)) self.cell_lengths = np.concatenate((self.cell_lengths, cell)) except _FileEOF: _warnings.warn('Unexpected EOF in parsing mdcrd. natom and/or ' 'hasbox are likely wrong', RuntimeWarning) except StopIteration: pass self._coordinates = self._coordinates.reshape((-1, self.natom, 3)) self.cell_lengths = self.cell_lengths.reshape((-1, 3)) self._file.close()
def write_parm(self, name): """ Writes the current data in parm_data into a new topology file with the given name Parameters ---------- name : str Name of the file to write the topology file to """ # now that we know we will write the new prmtop file, open the new file if isinstance(name, string_types): new_prm = genopen(name, 'w') own_handle = True else: new_prm = name own_handle = False try: # get current time to put into new prmtop file if we had a %VERSION self.set_version() # convert charges back to amber charges... if 'CTITLE' in self.parm_data: CHARGE_SCALE = CHARMM_ELECTROSTATIC else: CHARGE_SCALE = AMBER_ELECTROSTATIC if self.charge_flag in self.parm_data.keys(): for i in range(len(self.parm_data[self.charge_flag])): self.parm_data[self.charge_flag][i] *= CHARGE_SCALE # write version to top of prmtop file new_prm.write('%s\n' % self.version) # write data to prmtop file, inserting blank line if it's an empty field for flag in self.flag_list: new_prm.write('%%FLAG %s\n' % flag) # Insert any comments before the %FORMAT specifier for comment in self.parm_comments[flag]: new_prm.write('%%COMMENT %s\n' % comment) new_prm.write('%%FORMAT(%s)\n' % self.formats[flag]) if len(self.parm_data[flag]) == 0: # empty field... new_prm.write('\n') continue self.formats[flag].write(self.parm_data[flag], new_prm) finally: if own_handle: new_prm.close() if self.charge_flag in self.parm_data.keys(): # Convert charges back to electron-units for i in range(len(self.parm_data[self.charge_flag])): self.parm_data[self.charge_flag][i] /= CHARGE_SCALE
def add_coordinates(self, stuff): """ Prints 'stuff' (which must be either an iterable of 3*natom or have an attribute 'flatten' that converts it into an iterable of 3*natom) to the open file handler. Can only be called on a 'new' mdcrd, and adds these coordinates to the current end of the file. Parameters ---------- stuff : array or iterable This must be an iterable of length 3*natom or a numpy array that can be flattened to a 3*natom-length array Raises ------ If the coordinate file is an old one being parsed or if you are currently expected to provide unit cell dimensions, a RuntimeError is raised. If the provided coordinate data does not have length 3*natom, or cannot be ``flatten()``ed to create a 3*natom array, a ValueError is raised. """ # Make sure we can write the coordinates right now if not self._status == 'new': raise RuntimeError('Cannot print frames to an old mdcrd') try: stuff = stuff.flatten() except AttributeError: pass if self._writebox: raise RuntimeError('Box information not written for last frame') if len(stuff) != 3*self.natom: raise ValueError('add_coordinates requires an array of length ' 'natom*3') # If we can, write the coordinates i = 0 j = -1 for i in range(self._full_lines_per_frame): i10 = i * 10 for j in range(10): self._file.write('%8.3f' % stuff[i10+j]) self._file.write('\n') if self._nextras: extra = i*10+j + 1 while extra < self.natom*3: self._file.write('%8.3f' % stuff[extra]) extra += 1 self._file.write('\n') # Now it's time to write the box info if necessary self._writebox = self.hasbox self._file.flush()
def _read_nostrip(self, line): """ Reads the line and returns converted data. Special-cased for flags that may contain 'blank' data. ugh. """ line = line.rstrip('\n') nitems = int(ceil(len(line) / self.itemlen)) ret = [0 for i in range(nitems)] start, end = 0, self.itemlen for i in range(nitems): ret[i] = self.process_method(self.type(line[start:end])) start = end end += self.itemlen return ret
def test_atom_list(self): """ Tests the AtomList class """ atoms = AtomList() res = Residue('ALA') atoms.extend([Atom() for i in range(15)]) for atom in atoms: res.add_atom(atom) self.assertIs(atom.list, atoms) # Test the indexing for i, atom in enumerate(atoms): self.assertEqual(atom.idx, i) atoms.changed = False i = 0 # Test both pop and remove while atoms: if i % 2 == 0: atom = atoms.pop() else: atom = atoms[-1] atoms.remove(atom) self.assertEqual(atom.idx, -1) self.assertIs(atom.residue, None) self.assertIs(atom.list, None) self.assertTrue(atoms.changed) atoms.changed = False i += 1 atoms.extend([Atom() for i in range(15)]) self.assertTrue(res.is_empty()) for i, atom in enumerate(atoms): self.assertEqual(atom.idx, i) self.assertIs(atom.residue, None) atoms.changed = False # Tests the insertion atoms.insert(2, Atom(name='TOK')) self.assertTrue(atoms.changed) self.assertEqual(len(atoms), 16) for i, atom in enumerate(atoms): self.assertEqual(atom.idx, i) self.assertIs(atom.list, atoms) self.assertEqual(atoms[2].name, 'TOK') atoms.changed = False # Tests appending atoms.append(Atom(name='TOK2')) self.assertTrue(atoms.changed) self.assertEqual(len(atoms), 17) for i, atom in enumerate(atoms): self.assertEqual(atom.idx, i) self.assertEqual(atoms[2].name, 'TOK') self.assertEqual(atoms[-1].name, 'TOK2')
def _process_urey_bradley(struct, force): """ Adds Urey-Bradley parameters to the structure """ if not struct.angles: warnings.warn('Adding what seems to be Urey-Bradley terms before ' 'Angles. This is unexpected, but the parameters will ' 'all be present in one form or another.', OpenMMWarning) typemap = dict() for ii in range(force.getNumBonds()): i, j, req, k = force.getBondParameters(ii) ai, aj = struct.atoms[i], struct.atoms[j] key = (req._value, k._value) if struct.angles and ai not in aj.angle_partners: warnings.warn('Adding what seems to be Urey-Bradley terms, but ' 'atoms %d and %d do not appear to be angled to each ' 'other. Parameters will all be present, but may not ' 'be in expected places.' % (ai.idx, aj.idx), OpenMMWarning) if key in typemap: urey_type = typemap[key] else: urey_type = BondType(k*0.5, req) typemap[key] = urey_type struct.urey_bradley_types.append(urey_type) struct.urey_bradleys.append(UreyBradley(ai, aj, type=urey_type)) struct.urey_bradley_types.claim()
def _condense_types(typedict): """ Loops through the given dict and condenses all types. Parameter --------- typedict : dict Type dictionary to condense """ keylist = list(typedict.keys()) for i in range(len(keylist) - 1): key1 = keylist[i] for j in range(i+1, len(keylist)): key2 = keylist[j] if typedict[key1] == typedict[key2]: typedict[key2] = typedict[key1]
def __init__(self, atom1, atom2, atom3, atom4, args): self.atom1 = atom1 self.atom2 = atom2 self.atom3 = atom3 self.atom4 = atom4 # We don't know how many terms to expect, but it will be a multiple of 3 if len(args) % 3 != 0: raise TypeError('TorsionAngle expects an equal number of ' 'Amplitudes, phases, and periodicities.') nterms = len(args) // 3 self.amplitude = tuple([float(args[3*i]) for i in range(nterms)]) self.phase = tuple([float(args[3*i+1]) for i in range(nterms)]) self.periodicity = tuple([int(args[3*i+2]) for i in range(nterms)]) if (len(self.amplitude) != len(self.phase) or len(self.amplitude) != len(self.periodicity)): raise RuntimeError('BUGBUG!! Inconsistent # of terms in torsion')
def read(self, filename='mdin'): lines = open(filename, 'r').readlines() # split up input file into separate fields by comma blocks = [] # namelists in the order they appear block_fields = [] # array of arrays that correspond to entries in # namelists found in "blocks" above inblock = False lead_comment = True for i in range(len(lines)): if not inblock and not lines[i].strip().startswith('&') and \ lead_comment: continue elif not inblock and not lines[i].strip().startswith('&') and \ not lead_comment: final_ended = True for j in range(i, len(lines)): if lines[j].strip().startswith('&'): final_ended = False if final_ended and len(lines[i].strip()) != 0: self.cards.append(lines[i]) elif not inblock and lines[i].strip().startswith('&'): lead_comment = False inblock = True block = lines[i].strip()[1:].lower() blocks.append( block) # add the name of the namelist to "blocks" block_fields.append( []) # add empty array to be filled with entries # for given namelist if not block in self.valid_namelists: raise InputError( 'Invalid namelist (%s) in input ' 'file (%s) for %s' % (lines[i].strip(), filename, self.program)) elif inblock and (lines[i].strip() == '/' or lines[i].strip() == '&end'): inblock = False elif inblock and lines[i].strip().startswith('&'): raise InputError('Invalid input file (%s). Terminate each ' 'namelist before another is started' % filename) elif inblock: items = lines[i].strip().split(',') j = 0 while j < len(items): items[j] = items[j].strip() if len(items[j]) == 0: items.pop(j) else: j += 1 block_fields[len(block_fields) - 1].extend(items) # take out the last END in the cards if it's there if len(self.cards) != 0 and \ self.cards[len(self.cards)-1].strip().upper() == 'END': self.cards.pop() # combine any multi-element fields: e.g. rstwt=1,2,3, begin_field = -1 for i in range(len(block_fields)): for j in range(len(block_fields[i])): if not '=' in block_fields[i][j]: if begin_field == -1: raise InputError('Invalid input file (%s).' % filename) else: block_fields[i][ begin_field] += ',' + block_fields[i][j] else: begin_field = j # now parse through the options and add them to the dictionaries for i in range(len(block_fields)): for j in range(len(block_fields[i])): if not '=' in block_fields[i][j]: continue else: var = block_fields[i][j].split('=') self.change(blocks[i], var[0].strip(), var[1].strip())
def write(self, filename='mdin'): # open the file for writing and write the header and &cntrl namelist file = open(filename, 'w') file.write(self.title + '\n') file.write('&cntrl\n') # automatic indent of single space line = ' ' # add any variable that is different from the default to the mdin file for var in self.cntrl_nml.keys(): if self.cntrl_nml[var] == self.cntrl_nml_defaults[var]: continue if isinstance(self.cntrl_nml[var], string_types): line = addOn(line, "%s='%s', " % (var, self.cntrl_nml[var]), file) else: line = addOn(line, "%s=%s, " % (var, self.cntrl_nml[var]), file) # flush any remaining items that haven't yet been printed to the mdin file if len(line.strip()) != 0: file.write(line + '\n') # end the namelist file.write('/\n') # print the ewald namelist if any variables differ from the default line = ' ' has_been_printed = False # keep track if this namelist has been printed for var in self.ewald_nml.keys(): if self.ewald_nml[var] == self.ewald_nml_defaults[var]: continue if not has_been_printed: file.write('&ewald\n') has_been_printed = True if isinstance(self.ewald_nml_defaults[var], string_types): line = addOn(line, "%s='%s', " % (var, self.ewald_nml[var]), file) else: line = addOn(line, "%s='%s', " % (var, self.ewald_nml[var]), file) # flush any remaining items that haven't been printed to the mdin file if len(line.strip()) != 0: file.write(line + '\n') # end the namelist if has_been_printed: file.write('/\n') # print the pb namelist if any variables differ from the original line = ' ' has_been_printed = False # keep track if this namelist has been printed for var in self.pb_nml.keys(): if self.pb_nml[var] == self.pb_nml_defaults[var]: continue if not has_been_printed: if self.program == 'sander.APBS': file.write('&apbs\n') else: file.write('&pb\n') has_been_printed = True if isinstance(self.pb_nml[var], string_types): line = addOn(line, "%s='%s', " % (var, self.pb_nml[var]), file) else: line = addOn(line, "%s=%s, " % (var, self.pb_nml[var]), file) # flush any remaining items that haven't been printed to the mdin file if len(line.strip()) != 0: file.write(line + '\n') # end the namelist if has_been_printed: file.write('/\n') # print the qmmm namelist if any variables differ from the original line = ' ' has_been_printed = False # keep track if this namelist has been printed if self.cntrl_nml['ifqnt'] == 1: for var in self.qmmm_nml.keys(): if self.qmmm_nml[var] == self.qmmm_nml_defaults[var]: continue if not has_been_printed: file.write('&qmmm\n') has_been_printed = True if isinstance(self.qmmm_nml_defaults[var], string_types): line = addOn(line, "%s='%s', " % (var, self.qmmm_nml[var]), file) else: line = addOn(line, "%s=%s, " % (var, self.qmmm_nml[var]), file) # flush any remaining items that haven't been printed to the mdin file if len(line.strip()) != 0: file.write(line + '\n') # end the namelist if has_been_printed: file.write('/\n') # Write the cards to the input file for i in range(len(self.cards)): file.write(self.cards[i].strip() + '\n') if len(self.cards) != 0: file.write('END\n') file.close()
def test_state_data_reporter(self): """ Test StateDataReporter with various options """ system = amber_gas.createSystem() integrator = mm.LangevinIntegrator(300*u.kelvin, 5.0/u.picoseconds, 1.0*u.femtoseconds) sim = app.Simulation(amber_gas.topology, system, integrator, platform=CPU) sim.context.setPositions(amber_gas.positions) f = open(get_fn('akma5.dat', written=True), 'w') sim.reporters.extend([ StateDataReporter(get_fn('akma1.dat', written=True), 10), StateDataReporter(get_fn('akma2.dat', written=True), 10, time=False, potentialEnergy=False, kineticEnergy=False, totalEnergy=False, temperature=False), StateDataReporter(get_fn('akma3.dat', written=True), 10, volume=True, density=True), StateDataReporter(get_fn('akma4.dat', written=True), 10, separator='\t'), StateDataReporter(get_fn('units.dat', written=True), 10, volume=True, density=True, energyUnit=u.kilojoules_per_mole, volumeUnit=u.nanometers**3), StateDataReporter(f, 10) ]) sim.step(500) f.close() # Now open all of the reporters and check that the information in there # is what we expect it to be akma1 = open(get_fn('akma1.dat', written=True), 'r') akma2 = open(get_fn('akma2.dat', written=True), 'r') akma3 = open(get_fn('akma3.dat', written=True), 'r') akma4 = open(get_fn('akma4.dat', written=True), 'r') akma5 = open(get_fn('akma5.dat', written=True), 'r') units = open(get_fn('units.dat', written=True), 'r') # AKMA 1 first header = akma1.readline().strip()[1:].split(',') self.assertEqual(len(header), 6) for i, label in enumerate(('Step', 'Time', 'Potential Energy', 'Kinetic Energy', 'Total Energy', 'Temperature')): self.assertTrue(label in header[i]) for i, line in enumerate(akma1): words = line.replace('\n', '').split(',') self.assertEqual(i*10+10, int(words[0])) # step akma1.close() # AKMA 2 header = akma2.readline().strip()[1:].split(',') self.assertEqual(len(header), 1) self.assertTrue('Step' in header[0]) for i, line in enumerate(akma2): self.assertEqual(int(line.replace('\n', '').split(',')[0]), 10*i+10) akma2.close() # AKMA 3 -- save energies so we can compare to the file with different # units header = akma3.readline().strip()[1:].split(',') self.assertEqual(len(header), 8) for i, label in enumerate(('Step', 'Time', 'Potential Energy', 'Kinetic Energy', 'Total Energy', 'Temperature', 'Box Volume', 'Density')): self.assertTrue(label in header[i]) akma_energies = [[0.0 for i in range(8)] for j in range(50)] for i, line in enumerate(akma3): words = line.replace('\n', '').split(',') akma_energies[i][0] = int(words[0]) for j in range(1, 8): akma_energies[i][j] = float(words[j]) akma3.close() # AKMA 4 -- tab delimiter header = akma4.readline().strip()[1:].split('\t') self.assertEqual(len(header), 6) for i, label in enumerate(('Step', 'Time', 'Potential Energy', 'Kinetic Energy', 'Total Energy', 'Temperature')): self.assertTrue(label in header[i]) for i, line in enumerate(akma4): words = line.replace('\n', '').split('\t') self.assertEqual(i*10+10, int(words[0])) # step akma4.close() # AKMA 5 -- write to open file handle header = akma5.readline().strip()[1:].split(',') self.assertEqual(len(header), 6) for i, label in enumerate(('Step', 'Time', 'Potential Energy', 'Kinetic Energy', 'Total Energy', 'Temperature')): self.assertTrue(label in header[i]) for i, line in enumerate(akma5): words = line.replace('\n', '').split(',') self.assertEqual(i*10+10, int(words[0])) # step akma5.close() # UNITS -- compare other units ene = u.kilojoule_per_mole.conversion_factor_to(u.kilocalorie_per_mole) volume = u.nanometers.conversion_factor_to(u.angstroms)**3 conversions = [1, 1, ene, ene, ene, 1, volume, 1] headers = units.readline().strip()[1:].split(',') self.assertEqual(len(headers), 8) for i, line in enumerate(units): words = line.replace('\n', '').split(',') self.assertEqual(int(words[0]), akma_energies[i][0]) for j in range(1, 8): self.assertAlmostEqual(float(words[j])*conversions[j], akma_energies[i][j], places=5) units.close()
def test_reporters(self): """ Test NetCDF and ASCII restart and trajectory reporters (no PBC) """ self.assertRaises(ValueError, lambda: NetCDFReporter(get_fn('blah', written=True), 1, crds=False)) self.assertRaises(ValueError, lambda: MdcrdReporter(get_fn('blah', written=True), 1, crds=False)) self.assertRaises(ValueError, lambda: MdcrdReporter(get_fn('blah', written=True), 1, crds=True, vels=True)) system = amber_gas.createSystem() integrator = mm.LangevinIntegrator(300*u.kelvin, 5.0/u.picoseconds, 1.0*u.femtoseconds) sim = app.Simulation(amber_gas.topology, system, integrator, platform=CPU) sim.context.setPositions(amber_gas.positions) sim.reporters.extend([ NetCDFReporter(get_fn('traj1.nc', written=True), 10), NetCDFReporter(get_fn('traj2.nc', written=True), 10, vels=True), NetCDFReporter(get_fn('traj3.nc', written=True), 10, frcs=True), NetCDFReporter(get_fn('traj4.nc', written=True), 10, vels=True, frcs=True), NetCDFReporter(get_fn('traj5.nc', written=True), 10, crds=False, vels=True), NetCDFReporter(get_fn('traj6.nc', written=True), 10, crds=False, frcs=True), NetCDFReporter(get_fn('traj7.nc', written=True), 10, crds=False, vels=True, frcs=True), MdcrdReporter(get_fn('traj1.mdcrd', written=True), 10), MdcrdReporter(get_fn('traj2.mdcrd', written=True), 10, crds=False, vels=True), MdcrdReporter(get_fn('traj3.mdcrd', written=True), 10, crds=False, frcs=True), RestartReporter(get_fn('restart.ncrst', written=True), 10, write_multiple=True, netcdf=True), RestartReporter(get_fn('restart.rst7', written=True), 10) ]) sim.step(500) for reporter in sim.reporters: reporter.finalize() self.assertEqual(len(os.listdir(get_fn('writes'))), 61) ntraj = [NetCDFTraj.open_old(get_fn('traj1.nc', written=True)), NetCDFTraj.open_old(get_fn('traj2.nc', written=True)), NetCDFTraj.open_old(get_fn('traj3.nc', written=True)), NetCDFTraj.open_old(get_fn('traj4.nc', written=True)), NetCDFTraj.open_old(get_fn('traj5.nc', written=True)), NetCDFTraj.open_old(get_fn('traj6.nc', written=True)), NetCDFTraj.open_old(get_fn('traj7.nc', written=True))] atraj = [AmberMdcrd(get_fn('traj1.mdcrd', written=True), amber_gas.ptr('natom'), hasbox=False, mode='r'), AmberMdcrd(get_fn('traj2.mdcrd', written=True), amber_gas.ptr('natom'), hasbox=False, mode='r'), AmberMdcrd(get_fn('traj3.mdcrd', written=True), amber_gas.ptr('natom'), hasbox=False, mode='r')] for traj in ntraj: self.assertEqual(traj.frame, 50) self.assertEqual(traj.Conventions, 'AMBER') self.assertEqual(traj.ConventionVersion, '1.0') self.assertEqual(traj.application, 'AmberTools') self.assertEqual(traj.program, 'ParmEd') self.assertFalse(traj.hasbox) self.assertTrue(ntraj[0].hascrds) self.assertFalse(ntraj[0].hasvels) self.assertFalse(ntraj[0].hasfrcs) self.assertTrue(ntraj[1].hascrds) self.assertTrue(ntraj[1].hasvels) self.assertFalse(ntraj[1].hasfrcs) self.assertTrue(ntraj[2].hascrds) self.assertFalse(ntraj[2].hasvels) self.assertTrue(ntraj[2].hasfrcs) self.assertTrue(ntraj[3].hascrds) self.assertTrue(ntraj[3].hasvels) self.assertTrue(ntraj[3].hasfrcs) self.assertFalse(ntraj[4].hascrds) self.assertTrue(ntraj[4].hasvels) self.assertFalse(ntraj[4].hasfrcs) self.assertFalse(ntraj[5].hascrds) self.assertFalse(ntraj[5].hasvels) self.assertTrue(ntraj[5].hasfrcs) self.assertFalse(ntraj[6].hascrds) self.assertTrue(ntraj[6].hasvels) self.assertTrue(ntraj[6].hasfrcs) for i in (0, 2, 3, 4, 5, 6): ntraj[i].close() # still need the 2nd for traj in atraj: traj.close() # Now test the NetCDF restart files fn = get_fn('restart.ncrst.%d', written=True) for i, j in enumerate(range(10, 501, 10)): ncrst = NetCDFRestart.open_old(fn % j) self.assertEqual(ncrst.coordinates.shape, (1, 25, 3)) self.assertEqual(ncrst.velocities.shape, (1, 25, 3)) np.testing.assert_allclose(ncrst.coordinates[0], ntraj[1].coordinates[i]) np.testing.assert_allclose(ncrst.velocities[0], ntraj[1].velocities[i], rtol=1e-6) # Now test the ASCII restart file f = AmberAsciiRestart(get_fn('restart.rst7', written=True), 'r') # Compare to ncrst and make sure it's the same data np.testing.assert_allclose(ncrst.coordinates, f.coordinates, atol=1e-3) np.testing.assert_allclose(ncrst.velocities, f.velocities, rtol=1e-3) # Make sure the EnergyMinimizerReporter does not fail f = StringIO() rep = EnergyMinimizerReporter(f) rep.report(sim, frame=10) rep.finalize()
def __init__(self, psf_name=None): """ Opens and parses a PSF file, then instantiates a CharmmPsfFile instance from the data. """ global _resre Structure.__init__(self) # Bail out if we don't have a filename if psf_name is None: return conv = CharmmPsfFile._convert # Open the PSF and read the first line. It must start with "PSF" with closing(genopen(psf_name, 'r')) as psf: self.name = psf_name line = psf.readline() if not line.startswith('PSF'): raise CharmmError('Unrecognized PSF file. First line is %s' % line.strip()) # Store the flags psf_flags = line.split()[1:] # Now get all of the sections and store them in a dict psf.readline() # Now get all of the sections psfsections = _ZeroDict() while True: try: sec, ptr, data = CharmmPsfFile._parse_psf_section(psf) except _FileEOF: break psfsections[sec] = (ptr, data) # store the title self.title = psfsections['NTITLE'][1] # Next is the number of atoms natom = conv(psfsections['NATOM'][0], int, 'natom') # Parse all of the atoms for i in range(natom): words = psfsections['NATOM'][1][i].split() atid = int(words[0]) if atid != i + 1: raise CharmmError('Nonsequential atoms detected!') segid = words[1] rematch = _resre.match(words[2]) if not rematch: raise CharmmError('Could not interpret residue number %s' % # pragma: no cover words[2]) resid, inscode = rematch.groups() resid = conv(resid, int, 'residue number') resname = words[3] name = words[4] attype = words[5] # Try to convert the atom type to an integer a la CHARMM try: attype = int(attype) except ValueError: pass charge = conv(words[6], float, 'partial charge') mass = conv(words[7], float, 'atomic mass') props = words[8:] atom = Atom(name=name, type=attype, charge=charge, mass=mass) atom.props = props self.add_atom(atom, resname, resid, chain=segid, inscode=inscode, segid=segid) # Now get the number of bonds nbond = conv(psfsections['NBOND'][0], int, 'number of bonds') if len(psfsections['NBOND'][1]) != nbond * 2: raise CharmmError( 'Got %d indexes for %d bonds' % # pragma: no cover (len(psfsections['NBOND'][1]), nbond)) it = iter(psfsections['NBOND'][1]) for i, j in zip(it, it): self.bonds.append(Bond(self.atoms[i - 1], self.atoms[j - 1])) # Now get the number of angles and the angle list ntheta = conv(psfsections['NTHETA'][0], int, 'number of angles') if len(psfsections['NTHETA'][1]) != ntheta * 3: raise CharmmError( 'Got %d indexes for %d angles' % # pragma: no cover (len(psfsections['NTHETA'][1]), ntheta)) it = iter(psfsections['NTHETA'][1]) for i, j, k in zip(it, it, it): self.angles.append( Angle(self.atoms[i - 1], self.atoms[j - 1], self.atoms[k - 1])) self.angles[-1].funct = 5 # urey-bradley # Now get the number of torsions and the torsion list nphi = conv(psfsections['NPHI'][0], int, 'number of torsions') if len(psfsections['NPHI'][1]) != nphi * 4: raise CharmmError( 'Got %d indexes for %d torsions' % # pragma: no cover (len(psfsections['NPHI']), nphi)) it = iter(psfsections['NPHI'][1]) for i, j, k, l in zip(it, it, it, it): self.dihedrals.append( Dihedral(self.atoms[i - 1], self.atoms[j - 1], self.atoms[k - 1], self.atoms[l - 1])) self.dihedrals.split = False # Now get the number of improper torsions nimphi = conv(psfsections['NIMPHI'][0], int, 'number of impropers') if len(psfsections['NIMPHI'][1]) != nimphi * 4: raise CharmmError( 'Got %d indexes for %d impropers' % # pragma: no cover (len(psfsections['NIMPHI'][1]), nimphi)) it = iter(psfsections['NIMPHI'][1]) for i, j, k, l in zip(it, it, it, it): self.impropers.append( Improper(self.atoms[i - 1], self.atoms[j - 1], self.atoms[k - 1], self.atoms[l - 1])) # Now handle the donors (what is this used for??) ndon = conv(psfsections['NDON'][0], int, 'number of donors') if len(psfsections['NDON'][1]) != ndon * 2: raise CharmmError( 'Got %d indexes for %d donors' % # pragma: no cover (len(psfsections['NDON'][1]), ndon)) it = iter(psfsections['NDON'][1]) for i, j in zip(it, it): self.donors.append( AcceptorDonor(self.atoms[i - 1], self.atoms[j - 1])) # Now handle the acceptors (what is this used for??) nacc = conv(psfsections['NACC'][0], int, 'number of acceptors') if len(psfsections['NACC'][1]) != nacc * 2: raise CharmmError( 'Got %d indexes for %d acceptors' % # pragma: no cover (len(psfsections['NACC'][1]), nacc)) it = iter(psfsections['NACC'][1]) for i, j in zip(it, it): self.acceptors.append( AcceptorDonor(self.atoms[i - 1], self.atoms[j - 1])) # Now get the group sections try: ngrp, nst2 = psfsections['NGRP NST2'][0] except ValueError: # pragma: no cover raise CharmmError( 'Could not unpack GROUP pointers') # pragma: no cover tmp = psfsections['NGRP NST2'][1] self.groups.nst2 = nst2 # Now handle the groups if len(psfsections['NGRP NST2'][1]) != ngrp * 3: raise CharmmError( 'Got %d indexes for %d groups' % # pragma: no cover (len(tmp), ngrp)) it = iter(psfsections['NGRP NST2'][1]) for i, j, k in zip(it, it, it): self.groups.append(Group(self.atoms[i], j, k)) # Assign all of the atoms to molecules recursively tmp = psfsections['MOLNT'][1] set_molecules(self.atoms) molecule_list = [a.marked for a in self.atoms] if len(tmp) == len(self.atoms): if molecule_list != tmp: warnings.warn( 'Detected PSF molecule section that is WRONG. ' 'Resetting molecularity.', CharmmWarning) # We have a CHARMM PSF file; now do NUMLP/NUMLPH sections numlp, numlph = psfsections['NUMLP NUMLPH'][0] if numlp != 0 or numlph != 0: raise NotImplementedError( 'Cannot currently handle PSFs with ' 'lone pairs defined in the NUMLP/' 'NUMLPH section.') # Now do the CMAPs ncrterm = conv(psfsections['NCRTERM'][0], int, 'Number of cross-terms') if len(psfsections['NCRTERM'][1]) != ncrterm * 8: raise CharmmError('Got %d CMAP indexes for %d cmap terms' % # pragma: no cover (len(psfsections['NCRTERM']), ncrterm)) it = iter(psfsections['NCRTERM'][1]) for i, j, k, l, m, n, o, p in zip(it, it, it, it, it, it, it, it): self.cmaps.append( Cmap.extended(self.atoms[i - 1], self.atoms[j - 1], self.atoms[k - 1], self.atoms[l - 1], self.atoms[m - 1], self.atoms[n - 1], self.atoms[o - 1], self.atoms[p - 1])) self.unchange() self.flags = psf_flags
def _process_nonbonded(struct, force): """ Adds nonbonded parameters to the structure """ typemap = dict() element_typemap = defaultdict(int) assert force.getNumParticles() == len(struct.atoms), "Atom # mismatch" for i in range(force.getNumParticles()): atom = struct.atoms[i] chg, sig, eps = force.getParticleParameters(i) atype_name = Element[atom.atomic_number] key = (atype_name, sig._value, eps._value) if key in typemap: atom_type = typemap[key] else: element_typemap[atype_name] += 1 atype_name = '%s%d' % (atype_name, element_typemap[atype_name]) typemap[key] = atom_type = AtomType(atype_name, None, atom.mass, atom.atomic_number) atom.charge = chg.value_in_unit(u.elementary_charge) rmin = sig.value_in_unit(u.angstroms) * 2**(1.0 / 6) / 2 # to rmin/2 eps = eps.value_in_unit(u.kilocalories_per_mole) atom_type.set_lj_params(eps, rmin) atom.atom_type = atom_type atom.type = atom_type.name explicit_exceptions = defaultdict(set) bond_graph_exceptions = defaultdict(set) for atom in struct.atoms: for a2 in atom.bond_partners: if atom is not a2: bond_graph_exceptions[atom].add(a2) for a3 in a2.bond_partners: if a3 is atom: continue if atom is not a3: bond_graph_exceptions[atom].add(a3) # TODO should we compress exception types? for ii in range(force.getNumExceptions()): i, j, q, sig, eps = force.getExceptionParameters(ii) q = q.value_in_unit(u.elementary_charge**2) sig = sig.value_in_unit(u.angstrom) eps = eps.value_in_unit(u.kilocalorie_per_mole) ai, aj = struct.atoms[i], struct.atoms[j] if q == 0 and (sig == 0 or eps == 0): explicit_exceptions[ai].add(aj) explicit_exceptions[aj].add(ai) continue try: chgscale = q / (ai.charge * aj.charge) except ZeroDivisionError: if q != 0: raise TypeError('Cannot scale charge product of 0 to match ' '%s' % q) chgscale = 1 nbtype = NonbondedExceptionType(sig * 2**(1.0 / 6), eps, chgscale) struct.adjusts.append(NonbondedException(ai, aj, type=nbtype)) struct.adjust_types.append(nbtype) struct.adjust_types.claim() # Check that all of our exceptions are accounted for for ai, exceptions in iteritems(bond_graph_exceptions): if exceptions - explicit_exceptions[ai]: struct.unknown_functional = True warnings.warn('Detected incomplete exceptions. Not supported.', OpenMMWarning) break
def params1264(parm, mask, c4file, watermodel, polfile, tunfactor): from parmed import periodic_table as pt global DEFAULT_C4_PARAMS, WATER_POL try: pollist = _get_params(polfile) except ValueError: raise LJ12_6_4Error('Bad polarizability file %s. Expected a file with ' '2 columns: <Amber Atom Type> <Polarizability>' % polfile) if c4file is None: c4list = DEFAULT_C4_PARAMS[watermodel] else: try: c4list = _get_params(c4file) except ValueError: raise LJ12_6_4Error('Bad C4 parameter file %s. Expected a file ' 'with 2 columns: <Atom Element> ' '<C4 Parameter>' % c4file) print("***********************************************************") # Determine which atom type was treated as the center metal ion mettypdict = dict() for i in mask.Selected(): mettypind = parm.parm_data['ATOM_TYPE_INDEX'][i] metchg = parm.parm_data['CHARGE'][i] if mettypind in mettypdict: continue mettypdict[mettypind] = (parm.atoms[i].atomic_number, int(metchg)) print("The selected metal ion is %s" % pt.Element[parm.atoms[i].atomic_number]) mettypinds = sorted(mettypdict.keys()) # 1. Get the dict between AMBER_ATOM_TYPE and ATOM_TYPE_INDEX for all # the atoms in prmtop file ntypes = parm.pointers['NTYPES'] typs = parm.parm_data['AMBER_ATOM_TYPE'] typinds = parm.parm_data['ATOM_TYPE_INDEX'] typdict = {} for ty, ind in zip(typs, typinds): if ind not in typdict: typdict[ind] = [ty] elif ty in typdict[ind]: continue else: typdict[ind].append(ty) for i in range(1, ntypes + 1): if i not in typinds: typdict[i] = [] # 2.Generate the C4 term for each atom type pair result = [0.0 for i in parm.parm_data['LENNARD_JONES_ACOEF']] for mettypind in mettypinds: # Obtain the C4 parameters c4 = c4list[pt.Element[mettypdict[mettypind][0]] + str(mettypdict[mettypind][1])] i = mettypind - 1 for j in range(1, ntypes + 1): jj = j - 1 attypjs = typdict[j] if len(attypjs) >= 1: # Get polarizability for k, typjs in enumerate(attypjs): if k == 0: try: pol = pollist[typjs] except KeyError: raise LJ12_6_4Error( "Could not find parameters for " "ATOM_TYPE %s" % typjs) else: try: anthpol = pollist[typjs] if anthpol != pol: raise LJ12_6_4Error( 'Polarizability parameter of ' 'AMBER_ATOM_TYPE %s is not the same ' 'as that of AMBER_ATOM_TYPE %s, but ' 'their VDW parameters are the same. ' % (attypjs[0], typjs)) except KeyError: raise LJ12_6_4Error( "Could not find parameters for " "ATOM_TYPE %s" % typjs) # Get index if jj < i: idx = parm.parm_data['NONBONDED_PARM_INDEX'][ntypes * jj + i] - 1 else: idx = parm.parm_data['NONBONDED_PARM_INDEX'][ntypes * i + jj] - 1 # Caculate C4 terms if attypjs == ['OW']: # There is only one C4 term exist between water and a # certain ion result[idx] = c4 else: # There are two C4 terms need to add together between two # different ions result[idx] += c4 / WATER_POL * pol * tunfactor return result
def __invert__(self): """ >>> m = MyMatrix([[1,1],[0,1]]) >>> print(m) [[1, 1] [0, 1]] >>> print(~m) [[1.0, -1.0] [0.0, 1.0]] >>> print(m*~m) [[1.0, 0.0] [0.0, 1.0]] >>> print(~m*m) [[1.0, 0.0] [0.0, 1.0]] >>> m = MyMatrix([[1,0,0],[0,0,1],[0,-1,0]]) >>> print(m) [[1, 0, 0] [0, 0, 1] [0, -1, 0]] >>> print(~m) [[1.0, 0.0, 0.0] [0.0, 0.0, -1.0] [0.0, 1.0, 0.0]] >>> print(m*~m) [[1.0, 0.0, 0.0] [0.0, 1.0, 0.0] [0.0, 0.0, 1.0]] >>> print(~m*m) [[1.0, 0.0, 0.0] [0.0, 1.0, 0.0] [0.0, 0.0, 1.0]] """ assert self.is_square() if self.numRows() == 0: return self elif self.numRows() == 1: val = self[0][0] val = 1.0/val return MyMatrix([[val]]) elif self.numRows() == 2: # 2x2 is analytic # http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_2.C3.972_matrices a = self[0][0] b = self[0][1] c = self[1][0] d = self[1][1] determinant = a*d - b*c if determinant == 0: raise ArithmeticError("Cannot invert 2x2 matrix with zero determinant") else: return 1.0/(a*d - b*c) * MyMatrix([[d, -b],[-c, a]]) else: # Gauss Jordan elimination from numerical recipes n = self.numRows() m1 = self.numCols() assert n == m1 # Copy initial matrix into result matrix a = zeros(n, n) for i in range (0,n): for j in range (0,n): a[i][j] = self[i][j] # These arrays are used for bookkeeping on the pivoting indxc = [0] * n indxr = [0] * n ipiv = [0] * n for i in range (0,n): big = 0.0 for j in range (0,n): if ipiv[j] != 1: for k in range (0,n): if ipiv[k] == 0: if abs(a[j][k]) >= big: big = abs(a[j][k]) irow = j icol = k ipiv[icol] += 1 # We now have the pivot element, so we interchange rows... if irow != icol: for l in range(n): temp = a[irow][l] a[irow][l] = a[icol][l] a[icol][l] = temp indxr[i] = irow indxc[i] = icol if a[icol][icol] == 0: raise ArithmeticError("Cannot invert singular matrix") pivinv = 1.0/a[icol][icol] a[icol][icol] = 1.0 for l in range(n): a[icol][l] *= pivinv for ll in range(n): # next we reduce the rows if ll == icol: continue # except the pivot one, of course dum = a[ll][icol] a[ll][icol] = 0.0 for l in range(n): a[ll][l] -= a[icol][l]*dum # Unscramble the permuted columns for l in range(n-1, -1, -1): if indxr[l] == indxc[l]: continue for k in range(n): temp = a[k][indxr[l]] a[k][indxr[l]] = a[k][indxc[l]] a[k][indxc[l]] = temp return a
def write(struct, dest, vmd=False): """ Writes a PSF file from the stored molecule Parameters ---------- struct : :class:`Structure` The Structure instance from which the PSF should be written dest : str or file-like The place to write the output PSF file. If it has a "write" attribute, it will be used to print the PSF file. Otherwise, it will be treated like a string and a file will be opened, printed, then closed vmd : bool If True, it will write out a PSF in the format that VMD prints it in (i.e., no NUMLP/NUMLPH or MOLNT sections) Examples -------- >>> cs = CharmmPsfFile('testfiles/test.psf') >>> cs.write_psf('testfiles/test2.psf') """ # See if this is an extended format try: ext = 'EXT' in struct.flags except AttributeError: ext = True own_handle = False # Index the atoms and residues TODO delete if isinstance(dest, string_types): own_handle = True dest = genopen(dest, 'w') # Assign the formats we need to write with if ext: atmfmt1 = ('%10d %-8s %-8i %-8s %-8s %6d %10.6f %13.4f' + 11 * ' ') atmfmt2 = ('%10d %-8s %-8i %-8s %-8s %-6s %10.6f %13.4f' + 11 * ' ') intfmt = '%10d' # For pointers else: atmfmt1 = ('%8d %-4s %-4i %-4s %-4s %4d %10.6f %13.4f' + 11 * ' ') atmfmt2 = ('%8d %-4s %-4i %-4s %-4s %-4s %10.6f %13.4f' + 11 * ' ') intfmt = '%8d' # For pointers # Now print the header then the title dest.write('PSF CHEQ ') if hasattr(struct, 'flags'): dest.write(' '.join(f for f in struct.flags if f not in ('CHEQ', ))) else: dest.write('EXT') # EXT is always active if no flags present dest.write('\n\n') if isinstance(struct.title, string_types): dest.write(intfmt % 1 + ' !NTITLE\n') dest.write('%s\n\n' % struct.title) else: dest.write(intfmt % len(struct.title) + ' !NTITLE\n') dest.write('\n'.join(struct.title) + '\n\n') # Now time for the atoms dest.write(intfmt % len(struct.atoms) + ' !NATOM\n') # atmfmt1 is for CHARMM format (i.e., atom types are integers) # atmfmt is for XPLOR format (i.e., atom types are strings) add = 0 if struct.residues[ 0].number > 0 else 1 - struct.residues[0].number for i, atom in enumerate(struct.atoms): typ = atom.type if isinstance(atom.type, str): fmt = atmfmt2 if not atom.type: typ = atom.name else: fmt = atmfmt1 segid = atom.residue.segid or 'SYS' atmstr = fmt % (i + 1, segid, atom.residue.number + add, atom.residue.name, atom.name, typ, atom.charge, atom.mass) if hasattr(atom, 'props'): dest.write(atmstr + ' '.join(atom.props) + '\n') else: dest.write('%s\n' % atmstr) dest.write('\n') # Bonds dest.write(intfmt % len(struct.bonds) + ' !NBOND: bonds\n') for i, bond in enumerate(struct.bonds): dest.write((intfmt * 2) % (bond.atom1.idx + 1, bond.atom2.idx + 1)) if i % 4 == 3: # Write 4 bonds per line dest.write('\n') # See if we need to terminate if len(struct.bonds) % 4 != 0 or len(struct.bonds) == 0: dest.write('\n') dest.write('\n') # Angles dest.write(intfmt % len(struct.angles) + ' !NTHETA: angles\n') for i, angle in enumerate(struct.angles): dest.write( (intfmt * 3) % (angle.atom1.idx + 1, angle.atom2.idx + 1, angle.atom3.idx + 1)) if i % 3 == 2: # Write 3 angles per line dest.write('\n') # See if we need to terminate if len(struct.angles) % 3 != 0 or len(struct.angles) == 0: dest.write('\n') dest.write('\n') # Dihedrals # impropers need to be split off in the "improper" section. # PSF files need to have each dihedral listed *only* once. So count the # number of unique dihedrals nnormal = 0 torsions = set() for dih in struct.dihedrals: if dih.improper: continue a1, a2, a3, a4 = dih.atom1, dih.atom2, dih.atom3, dih.atom4 if (a1, a2, a3, a4) in torsions or (a4, a3, a2, a1) in torsions: continue nnormal += 1 torsions.add((a1, a2, a3, a4)) nimprop = sum(1 for dih in struct.dihedrals if dih.improper) dest.write(intfmt % nnormal + ' !NPHI: dihedrals\n') torsions = set() c = 0 for dih in struct.dihedrals: if dih.improper: continue a1, a2, a3, a4 = dih.atom1, dih.atom2, dih.atom3, dih.atom4 if (a1, a2, a3, a4) in torsions or (a4, a3, a2, a1) in torsions: continue dest.write((intfmt * 4) % (a1.idx + 1, a2.idx + 1, a3.idx + 1, a4.idx + 1)) torsions.add((a1, a2, a3, a4)) if c % 2 == 1: # Write 2 dihedrals per line dest.write('\n') c += 1 # See if we need to terminate if nnormal % 2 != 0 or nnormal == 0: dest.write('\n') dest.write('\n') # Impropers nimprop += len(struct.impropers) dest.write(intfmt % (nimprop) + ' !NIMPHI: impropers\n') def improp_gen(struct): for imp in struct.impropers: yield (imp.atom1, imp.atom2, imp.atom3, imp.atom4) for dih in struct.dihedrals: if dih.improper: # Central atom first yield canonical_improper_order( dih.atom3, dih.atom1, dih.atom2, dih.atom4, center=1, ) for i, (a1, a2, a3, a4) in enumerate(improp_gen(struct)): dest.write((intfmt * 4) % (a1.idx + 1, a2.idx + 1, a3.idx + 1, a4.idx + 1)) if i % 2 == 1: # Write 2 dihedrals per line dest.write('\n') # See if we need to terminate if nimprop % 2 != 0 or nimprop == 0: dest.write('\n') dest.write('\n') # Donor section dest.write(intfmt % len(struct.donors) + ' !NDON: donors\n') for i, don in enumerate(struct.donors): dest.write((intfmt * 2) % (don.atom1.idx + 1, don.atom2.idx + 1)) if i % 4 == 3: # 4 donors per line dest.write('\n') if len(struct.donors) % 4 != 0 or len(struct.donors) == 0: dest.write('\n') dest.write('\n') # Acceptor section dest.write(intfmt % len(struct.acceptors) + ' !NACC: acceptors\n') for i, acc in enumerate(struct.acceptors): dest.write((intfmt * 2) % (acc.atom1.idx + 1, acc.atom2.idx + 1)) if i % 4 == 3: # 4 donors per line dest.write('\n') if len(struct.acceptors) % 4 != 0 or len(struct.acceptors) == 0: dest.write('\n') dest.write('\n') # NNB section ?? dest.write(intfmt % 0 + ' !NNB\n\n') for i in range(len(struct.atoms)): dest.write(intfmt % 0) if i % 8 == 7: # Write 8 0's per line dest.write('\n') if len(struct.atoms) % 8 != 0: dest.write('\n') dest.write('\n') # Group section try: nst2 = struct.groups.nst2 except AttributeError: nst2 = 0 dest.write((intfmt * 2) % (len(struct.groups) or 1, nst2)) dest.write(' !NGRP NST2\n') if struct.groups: for i, gp in enumerate(struct.groups): dest.write((intfmt * 3) % (gp.atom.idx, gp.type, gp.move)) if i % 3 == 2: dest.write('\n') if len(struct.groups) % 3 != 0 or len(struct.groups) == 0: dest.write('\n') else: typ = 1 if abs(sum(a.charge for a in struct.atoms)) < 1e-4 else 2 dest.write((intfmt * 3) % (0, typ, 0)) dest.write('\n') dest.write('\n') # The next two sections are never found in VMD prmtops... if not vmd: # Molecule section; first set molecularity set_molecules(struct.atoms) mollist = [a.marked for a in struct.atoms] dest.write(intfmt % max(mollist) + ' !MOLNT\n') for i, atom in enumerate(struct.atoms): dest.write(intfmt % atom.marked) if i % 8 == 7: dest.write('\n') if len(struct.atoms) % 8 != 0: dest.write('\n') dest.write('\n') # NUMLP/NUMLPH section dest.write((intfmt * 2) % (0, 0) + ' !NUMLP NUMLPH\n') dest.write('\n') # CMAP section dest.write(intfmt % len(struct.cmaps) + ' !NCRTERM: cross-terms\n') for i, cmap in enumerate(struct.cmaps): dest.write( (intfmt * 8) % (cmap.atom1.idx + 1, cmap.atom2.idx + 1, cmap.atom3.idx + 1, cmap.atom4.idx + 1, cmap.atom2.idx + 1, cmap.atom3.idx + 1, cmap.atom4.idx + 1, cmap.atom5.idx + 1)) dest.write('\n') # Done! # If we opened our own handle, close it if own_handle: dest.close()
def from_structure(cls, struct, copy=False): """ Take a Structure instance and initialize a ChamberParm instance from that data. Parameters ---------- struct : Structure The input structure from which to construct a ChamberParm instance copy : bool If True, the input struct is deep-copied to make sure it does not share any objects with the original ``struct``. Default is False Returns ------- inst : :class:`ChamberParm` The ChamberParm instance derived from the input structure Notes ----- Due to the nature of the prmtop file, struct almost *always* returns a deep copy. The one exception is when struct is already of type :class:`ChamberParm`, in which case the original object is returned unless ``copy`` is ``True``. """ if isinstance(struct, cls): if copy: return _copy.copy(struct) return struct if (struct.rb_torsions or struct.trigonal_angles or struct.pi_torsions or struct.out_of_plane_bends or struct.stretch_bends or struct.torsion_torsions or struct.multipole_frames): raise TypeError('ChamberParm does not support all potential terms ' 'defined in the input Structure') inst = struct.copy(cls, split_dihedrals=True) inst.update_dihedral_exclusions() inst._add_missing_13_14(ignore_inconsistent_vdw=True) inst.pointers = {} inst.LJ_types = {} nbfixes = inst.atoms.assign_nbidx_from_types() # Give virtual sites a name that Amber understands for atom in inst.atoms: if isinstance(atom, ExtraPoint): atom.type = 'EP' # Fill the Lennard-Jones arrays/dicts ntyp = 0 for atom in inst.atoms: inst.LJ_types[atom.type] = atom.nb_idx ntyp = max(ntyp, atom.nb_idx) inst.LJ_radius = [0 for i in range(ntyp)] inst.LJ_depth = [0 for i in range(ntyp)] inst.LJ_14_radius = [0 for i in range(ntyp)] inst.LJ_14_depth = [0 for i in range(ntyp)] for atom in inst.atoms: inst.LJ_radius[atom.nb_idx - 1] = atom.rmin inst.LJ_depth[atom.nb_idx - 1] = atom.epsilon inst.LJ_14_radius[atom.nb_idx - 1] = atom.rmin_14 inst.LJ_14_depth[atom.nb_idx - 1] = atom.epsilon_14 inst._add_standard_flags() inst.pointers['NATOM'] = len(inst.atoms) inst.parm_data['POINTERS'][NATOM] = len(inst.atoms) inst.box = _copy.copy(struct.box) if struct.box is None: inst.parm_data['POINTERS'][IFBOX] = 0 inst.pointers['IFBOX'] = 0 elif (abs(struct.box[3] - 90) > TINY or abs(struct.box[4] - 90) > TINY or abs(struct.box[5] - 90) > TINY): inst.parm_data['POINTERS'][IFBOX] = 2 inst.pointers['IFBOX'] = 2 inst.parm_data['BOX_DIMENSIONS'] = ([struct.box[3]] + list(struct.box[:3])) else: inst.parm_data['POINTERS'][IFBOX] = 1 inst.pointers['IFBOX'] = 1 inst.parm_data['BOX_DIMENSIONS'] = [90] + list(struct.box[:3]) # pmemd likes to skip torsions with periodicities of 0, which may be # present as a way to hack entries into the 1-4 pairlist. See # https://github.com/ParmEd/ParmEd/pull/145 for discussion. The solution # here is to simply set that periodicity to 1. for dt in inst.dihedral_types: if dt.phi_k == 0 and dt.per == 0: dt.per = 1.0 elif dt.per == 0: warnings.warn( 'Periodicity of 0 detected with non-zero force ' 'constant. Changing periodicity to 1 and force ' 'constant to 0 to ensure 1-4 nonbonded pairs are ' 'properly identified. This might cause a shift ' 'in the energy, but will leave forces unaffected', AmberWarning) dt.phi_k = 0.0 dt.per = 1.0 inst.remake_parm() inst._set_nonbonded_tables(nbfixes) del inst.adjusts[:] inst.parm_data['FORCE_FIELD_TYPE'] = fftype = [] fftype.extend([1, 'CHARMM force field: No FF information parsed...']) return inst
def Not(self): new_mask = _mask(self.natom) for i in range(self.natom): new_mask[i] = 1 - self[i] return new_mask
def select_all(self): for i in range(self.natom): self[i] = 1
def load_parameters(self, parmset, copy_parameters=True): """ Loads parameters from a parameter set that was loaded via CHARMM RTF, PAR, and STR files. Parameters ---------- parmset : :class:`CharmmParameterSet` List of all parameters copy_parameters : bool, optional, default=True If False, parmset will not be copied. WARNING: ------- Not copying parmset will cause ParameterSet and Structure to share references to types. If you modify the original parameter set, the references in Structure list_types will be silently modified. However, if you change any reference in the parameter set, then that reference will no longer be shared with structure. Example where the reference in ParameterSet is changed. The following will NOT modify the parameters in the psf:: psf.load_parameters(parmset, copy_parameters=False) parmset.angle_types[('a1', 'a2', a3')] = AngleType(1, 2) The following WILL change the parameter in the psf because the reference has not been changed in ``ParameterSet``:: psf.load_parameters(parmset, copy_parameters=False) a = parmset.angle_types[('a1', 'a2', 'a3')] a.k = 10 a.theteq = 100 Extra care should be taken when trying this with dihedral_types. Since dihedral_type is a Fourier sequence, ParameterSet stores DihedralType for every term in DihedralTypeList. Therefore, the example below will STILL modify the type in the :class:`Structure` list_types:: parmset.dihedral_types[('a', 'b', 'c', 'd')][0] = DihedralType(1, 2, 3) This assigns a new instance of DihedralType to an existing DihedralTypeList that ParameterSet and Structure are tracking and the shared reference is NOT changed. Use with caution! Notes ----- - If any dihedral or improper parameters cannot be found, I will try inserting wildcards (at either end for dihedrals and as the two central atoms in impropers) and see if that matches. Wild-cards will apply ONLY if specific parameters cannot be found. - This method will expand the dihedrals attribute by adding a separate Dihedral object for each term for types that have a multi-term expansion Raises ------ ParameterError if any parameters cannot be found """ if copy_parameters: parmset = _copy(parmset) self.combining_rule = parmset.combining_rule # First load the atom types for atom in self.atoms: try: if isinstance(atom.type, int): atype = parmset.atom_types_int[atom.type] else: atype = parmset.atom_types_str[atom.type] except KeyError: raise ParameterError('Could not find atom type for %s' % atom.type) atom.atom_type = atype # Change to string type to look up the rest of the parameters atom.type = str(atom.atom_type) atom.atomic_number = atype.atomic_number # Next load all of the bonds for bond in self.bonds: # Construct the key key = (min(bond.atom1.type, bond.atom2.type), max(bond.atom1.type, bond.atom2.type)) try: bond.type = parmset.bond_types[key] except KeyError: raise ParameterError('Missing bond type for %r' % bond) bond.type.used = False # Build the bond_types list del self.bond_types[:] for bond in self.bonds: if bond.type.used: continue bond.type.used = True self.bond_types.append(bond.type) bond.type.list = self.bond_types # Next load all of the angles. If a Urey-Bradley term is defined for # this angle, also build the urey_bradley and urey_bradley_type lists del self.urey_bradleys[:] for ang in self.angles: # Construct the key key = (min(ang.atom1.type, ang.atom3.type), ang.atom2.type, max(ang.atom1.type, ang.atom3.type)) try: ang.type = parmset.angle_types[key] ang.type.used = False ubt = parmset.urey_bradley_types[key] if ubt is not NoUreyBradley: ub = UreyBradley(ang.atom1, ang.atom3, ubt) self.urey_bradleys.append(ub) ubt.used = False except KeyError: raise ParameterError('Missing angle type for %r' % ang) del self.urey_bradley_types[:] del self.angle_types[:] for ub in self.urey_bradleys: if ub.type.used: continue ub.type.used = True self.urey_bradley_types.append(ub.type) ub.type.list = self.urey_bradley_types for ang in self.angles: if ang.type.used: continue ang.type.used = True self.angle_types.append(ang.type) ang.type.list = self.angle_types # Next load all of the dihedrals. active_dih_list = set() for dih in self.dihedrals: # Store the atoms a1, a2, a3, a4 = dih.atom1, dih.atom2, dih.atom3, dih.atom4 key = (a1.type, a2.type, a3.type, a4.type) # First see if the exact dihedral is specified if not key in parmset.dihedral_types: # Check for wild-cards key = ('X', a2.type, a3.type, 'X') if not key in parmset.dihedral_types: raise ParameterError('No dihedral parameters found for ' '%r' % dih) dih.type = parmset.dihedral_types[key] dih.type.used = False pair = (dih.atom1.idx, dih.atom4.idx) # To determine exclusions if (dih.atom1 in dih.atom4.bond_partners or dih.atom1 in dih.atom4.angle_partners): dih.ignore_end = True elif pair in active_dih_list: dih.ignore_end = True else: active_dih_list.add(pair) active_dih_list.add((dih.atom4.idx, dih.atom1.idx)) del self.dihedral_types[:] for dihedral in self.dihedrals: if dihedral.type.used: continue dihedral.type.used = True self.dihedral_types.append(dihedral.type) dihedral.type.list = self.dihedral_types # Now do the impropers for imp in self.impropers: # Store the atoms a1, a2, a3, a4 = imp.atom1, imp.atom2, imp.atom3, imp.atom4 at1, at2, at3, at4 = a1.type, a2.type, a3.type, a4.type key = tuple(sorted([at1, at2, at3, at4])) altkey1 = a1.type, a2.type, a3.type, a4.type altkey2 = a4.type, a3.type, a2.type, a1.type # Check for exact harmonic or exact periodic if key in parmset.improper_types: imp.type = parmset.improper_types[key] elif key in parmset.improper_periodic_types: imp.type = parmset.improper_periodic_types[key] elif altkey1 in parmset.improper_periodic_types: imp.type = parmset.improper_periodic_types[altkey1] elif altkey2 in parmset.improper_periodic_types: imp.type = parmset.improper_periodic_types[altkey2] else: # Check for wild-card harmonic for anchor in (at2, at3, at4): key = tuple(sorted([at1, anchor, 'X', 'X'])) if key in parmset.improper_types: imp.type = parmset.improper_types[key] break # Check for wild-card periodic if key not in parmset.improper_types: for anchor in (at2, at3, at4): key = tuple(sorted([at1, anchor, 'X', 'X'])) if key in parmset.improper_periodic_types: imp.type = parmset.improper_periodic_types[key] break # Not found anywhere if key not in parmset.improper_periodic_types: raise ParameterError( 'No improper parameters found for ' '%r' % imp) imp.type.used = False # prepare list of harmonic impropers present in system del self.improper_types[:] for improper in self.impropers: if improper.type.used: continue improper.type.used = True if isinstance(improper.type, ImproperType): self.improper_types.append(improper.type) improper.type.list = self.improper_types elif isinstance(improper.type, DihedralType): self.dihedral_types.append(improper.type) improper.type.list = self.dihedral_types else: assert False, 'Should not be here' # Look through the list of impropers -- if there are any periodic # impropers, move them over to the dihedrals list for i in reversed(range(len(self.impropers))): if isinstance(self.impropers[i].type, DihedralType): imp = self.impropers.pop(i) dih = Dihedral(imp.atom1, imp.atom2, imp.atom3, imp.atom4, improper=True, ignore_end=True, type=imp.type) imp.delete() self.dihedrals.append(dih) # Now do the cmaps. These will not have wild-cards for cmap in self.cmaps: key = (cmap.atom1.type, cmap.atom2.type, cmap.atom3.type, cmap.atom4.type, cmap.atom2.type, cmap.atom3.type, cmap.atom4.type, cmap.atom5.type) try: cmap.type = parmset.cmap_types[key] except KeyError: raise ParameterError('No CMAP parameters found for %r' % cmap) cmap.type.used = False del self.cmap_types[:] for cmap in self.cmaps: if cmap.type.used: continue cmap.type.used = True self.cmap_types.append(cmap.type) cmap.type.list = self.cmap_types
def check_validity(parm, warnings): # local shortcut for _check_exist_nvals def checkme(*args, **kwargs): kwargs['warnings'] = warnings return _check_exist_nvals(parm, *args, **kwargs) # Make sure all of our sections that we expect are present and have the # right number of variables in it nttyp = parm.ptr('ntypes') * (parm.ptr('ntypes') + 1) // 2 # for LJ stuff checkme('ATOM_NAME', parm.ptr('natom'), True, False, None, str) checkme('CHARGE', parm.ptr('natom'), True, False, None, float) checkme('MASS', parm.ptr('natom'), True, False, None, float) checkme('ATOM_TYPE_INDEX', parm.ptr('natom'), True, False, None, int) checkme('NUMBER_EXCLUDED_ATOMS', parm.ptr('natom'), True, False, None, int) checkme('NONBONDED_PARM_INDEX', parm.ptr('ntypes')**2, True, False, None, int) checkme('RESIDUE_LABEL', parm.ptr('nres'), True, False, None, str) checkme('RESIDUE_POINTER', parm.ptr('nres'), True, False, None, int) checkme('BOND_FORCE_CONSTANT', parm.ptr('numbnd'), True, False, None, float) checkme('BOND_EQUIL_VALUE', parm.ptr('numbnd'), True, False, None, float) checkme('ANGLE_FORCE_CONSTANT', parm.ptr('numang'), True, False, None, float) checkme('ANGLE_EQUIL_VALUE', parm.ptr('numang'), True, False, None, float) checkme('DIHEDRAL_FORCE_CONSTANT', parm.ptr('nptra'), True, False, None, float) checkme('DIHEDRAL_PERIODICITY', parm.ptr('nptra'), True, False, None, float) checkme('DIHEDRAL_PHASE', parm.ptr('nptra'), True, False, None, float) checkme('SCEE_SCALE_FACTOR', parm.ptr('nptra'), False, True, 'scee 1.2', float) checkme('SCNB_SCALE_FACTOR', parm.ptr('nptra'), False, True, 'scnb 2.0', float) checkme('SOLTY', parm.ptr('natyp'), True, False, None, float) checkme('LENNARD_JONES_ACOEF', nttyp, True, False, None, float) checkme('LENNARD_JONES_BCOEF', nttyp, True, False, None, float) checkme('BONDS_INC_HYDROGEN', parm.ptr('nbonh') * 3, True, False, None, int) checkme('BONDS_WITHOUT_HYDROGEN', parm.ptr('nbona') * 3, True, False, None, int) checkme('ANGLES_INC_HYDROGEN', parm.ptr('ntheth') * 4, True, False, None, int) checkme('ANGLES_WITHOUT_HYDROGEN', parm.ptr('ntheta') * 4, True, False, None, int) checkme('DIHEDRALS_INC_HYDROGEN', parm.ptr('nphih') * 5, True, False, None, int) checkme('DIHEDRALS_WITHOUT_HYDROGEN', parm.ptr('nphia') * 5, True, False, None, int) checkme('EXCLUDED_ATOMS_LIST', parm.ptr('next'), True, False, None, int) checkme('HBOND_ACOEF', parm.ptr('nphb'), True, False, None, float) checkme('HBOND_BCOEF', parm.ptr('nphb'), True, False, None, float) checkme('HBCUT', parm.ptr('nphb'), True, False, None, float) checkme('AMBER_ATOM_TYPE', parm.ptr('natom'), True, False, None, str) checkme('TREE_CHAIN_CLASSIFICATION', parm.ptr('natom'), True, False, None, str) checkme('JOIN_ARRAY', parm.ptr('natom'), True, False, None, int) checkme('IROTAT', parm.ptr('natom'), True, False, None, int) # PBC tests if parm.ptr('ifbox') > 0: if checkme('SOLVENT_POINTERS', 3, True, True, 'setMolecules', int): checkme('ATOMS_PER_MOLECULE', parm.parm_data['SOLVENT_POINTERS'][1], True, True, 'setMolecules', int) checkme('BOX_DIMENSIONS', 4, True, False, None, float) # Check for contiguous molecules. Back up ATOMS_PER_MOLECULE and # SOLVENT_POINTERS so we can use rediscover_molecules without clobbering # any changes users may have made apm_cpy = parm.parm_data['ATOMS_PER_MOLECULE'][:] sp_cpy = parm.parm_data['SOLVENT_POINTERS'][:] try: parm.rediscover_molecules(fix_broken=False) parm.parm_data['ATOMS_PER_MOLECULE'] = apm_cpy parm.parm_data['SOLVENT_POINTERS'] = sp_cpy except MoleculeError: warnings.warn('ATOMS_PER_MOLECULE section corrupt! Molecules are ' 'not contiguous!') # Cap info if parm.ptr('ifcap') > 0: checkme('CAP_INFO', 1, True, False, None, int) checkme('CAP_INFO2', 4, True, False, None, float) # GB stuff if parm.ptr('ifbox') == 0: checkme('RADII', parm.ptr('natom'), True, True, 'changeRadii', float) checkme('SCREEN', parm.ptr('natom'), True, True, 'changeRadii', float) checkme('RADIUS_SET', 1, True, True, 'changeRadii', str) checkme('ATOMIC_NUMBER', parm.ptr('natom'), False, True, 'addAtomicNumber', int) # Some chamber checks if parm.chamber: # See if we have any CMAP terms has_cmap = 'CHARMM_CMAP_COUNT' in parm.flag_list # Ugh, don't want short-circuiting, so have to do this... try: nvals = parm.parm_data['FORCE_FIELD_TYPE'][0] nvals = int(nvals) except IndexError: warnings.warn('%FLAG FORCE_FIELD_TYPE is empty', BadParmWarning) except ValueError: warnings.warn('%FLAG FORCE_FIELD_TYPE does not have integer first', BadParmWarning) if len(parm.parm_data['FORCE_FIELD_TYPE']) - 1 < nvals: warnings.warn('Not enough lines in %FLAG FORCE_FIELD_TYPE', BadParmWarning) else: for i, val in enumerate(parm.parm_data['FORCE_FIELD_TYPE']): if i % 2 == 0 and not isinstance(val, int): warnings.warn('int type mismatch in FORCE_FIELD_TYPE', BadParmWarning) elif i % 2 == 1 and not isinstance(val, str): warnings.warn('str type mismatch in FORCE_FIELD_TYPE', BadParmWarning) hasallkeys = checkme('CHARMM_UREY_BRADLEY_COUNT', 2, False, False, None, int) hk = checkme('CHARMM_NUM_IMPROPERS', 1, True, False, None, int) hasallkeys = hasallkeys and hk hk = checkme('CHARMM_NUM_IMPR_TYPES', 1, True, False, None, int) hasallkeys = hasallkeys and hk if has_cmap: hk = checkme('CHARMM_CMAP_COUNT', 2, True, False, None, int) hasallkeys = hasallkeys and hk if hasallkeys: # Get the number of terms nub = parm.parm_data['CHARMM_UREY_BRADLEY_COUNT'][0] nubtypes = parm.parm_data['CHARMM_UREY_BRADLEY_COUNT'][1] nimphi = parm.parm_data['CHARMM_NUM_IMPROPERS'][0] nimprtyp = parm.parm_data['CHARMM_NUM_IMPR_TYPES'][0] cmap_term_cnt = parm.parm_data['CHARMM_CMAP_COUNT'][0] cmap_type_cnt = parm.parm_data['CHARMM_CMAP_COUNT'][1] # Look for terms checkme('LENNARD_JONES_14_ACOEF', nttyp, True, False, None, float) checkme('LENNARD_JONES_14_BCOEF', nttyp, True, False, None, float) checkme('CHARMM_UREY_BRADLEY', nub, True, False, None, int) checkme('CHARMM_UREY_BRADLEY_FORCE_CONSTANT', nubtypes, True, False, None, float) checkme('CHARMM_UREY_BRADLEY_EQUIL_CONSTANT', nubtypes, True, False, None, float) checkme('CHARMM_IMPROPERS', nimphi * 5, True, False, None, int) checkme('CHARMM_IMPROPER_FORCE_CONSTANT', nimprtyp, True, False, None, float) checkme('CHARMM_IMPROPER_PHASE', nimprtyp, True, False, None, float) if has_cmap: checkme('CHARMM_CMAP_INDEX', cmap_term_cnt * 6, True, False, None, int) if checkme('CHARMM_CMAP_RESOLUTION', cmap_type_cnt, True, False, True, int): for i in range(cmap_type_cnt): checkme('CHARMM_CMAP_PARAMETER_%s' % (str(i).zfill(2)), parm.parm_data['CHARMM_CMAP_RESOLUTION'][i], True, False, True, float) # Check that ATOMS_PER_MOLECULE == NATOM if 'ATOMS_PER_MOLECULE' in parm.parm_data: if sum(parm.parm_data['ATOMS_PER_MOLECULE']) != parm.ptr('natom'): warnings.warn( 'sum(ATOMS_PER_MOLECULE) != NATOM. Use ' '"setMolecules" to fix this', FixableParmWarning) # Duplicate pmemd's checks if parm.ptr('ifpert') != 0: warnings.warn('IFPERT must be 0! Parm will not work in Amber', AmberIncompatibleWarning) if (parm.ptr('mbona') != parm.ptr('nbona') or parm.ptr('mtheta') != parm.ptr('ntheta') or parm.ptr('mphia') != parm.ptr('nphia')): warnings.warn( 'Constraints can no longer be put in the prmtop ' 'in Amber programs!', AmberIncompatibleWarning) # Check that we didn't change off-diagonal LJ pairs, since this will not # work for all programs necessarily... Check relative error, though, since # Lennard Jones coefficients are very large parm.fill_LJ() ntypes = parm.ptr('ntypes') try: for i in range(ntypes): for j in range(ntypes): idx = parm.parm_data['NONBONDED_PARM_INDEX'][ntypes * i + j] - 1 rij = parm.LJ_radius[i] + parm.LJ_radius[j] wdij = sqrt(parm.LJ_depth[i] * parm.LJ_depth[j]) acoef = parm.parm_data['LENNARD_JONES_ACOEF'][idx] bcoef = parm.parm_data['LENNARD_JONES_BCOEF'][idx] if acoef == 0 or bcoef == 0: if acoef != 0 or bcoef != 0 or (wdij != 0 and rij != 0): warnings.warn( 'Modified off-diagonal LJ parameters ' 'detected', NonUniversalWarning) raise StopIteration elif (abs((acoef - (wdij * rij**12)) / acoef) > TINY or abs( (bcoef - (2 * wdij * rij**6)) / bcoef) > TINY): warnings.warn( 'Modified off-diagonal LJ parameters ' 'detected', NonUniversalWarning) raise StopIteration except StopIteration: pass # Check to see if we forgot to add disulfides. Unless we have coordinates, # though, we can only check that CYX sulfurs are bonded to another Sulfur mask = AmberMask(parm, ':CYX@SG') for i, sel in enumerate(mask.Selection()): if not sel: continue atm = parm.atoms[i] # We expect 2 bonds bondedatms = [a.name for a in atm.bond_partners] if len(bondedatms) != 2 or 'SG' not in bondedatms: warnings.warn( 'Detected CYX residue with a Sulfur atom not bonded ' 'to another CYX Sulfur! Did you forget to make the ' 'disulfide bond?', MissingDisulfide) break if parm.coordinates is not None: # Check if we think any disulfide bonds might be missing. mask = AmberMask(parm, ':CYS,CYM@SG') s_atms = [] for i, sel in enumerate(mask.Selection()): if not sel: continue s_atms.append(parm.atoms[i]) try: for i in range(len(s_atms) - 1): for j in range(i, len(s_atms)): dx = s_atms[i].xx - s_atms[j].xx dy = s_atms[i].xy - s_atms[j].xy dz = s_atms[i].xz - s_atms[j].xz if (dx * dx + dy * dy + dz * dz) < 9.0: warnings.warn( "Detected two cysteine residues whose sulfur " "atoms are within 3 Angstroms. Rename CYS to " "CYX in the PDB file and use the 'bond' " "command in tleap to create the disulfide " "bond", MissingDisulfide) raise StopIteration except StopIteration: pass # Now check if we have any bonds that seem unreasonably large. This # would indicate gaps in the structure. for bnd in parm.bonds: dx = bnd.atom1.xx - bnd.atom2.xx dy = bnd.atom1.xy - bnd.atom2.xy dz = bnd.atom1.xz - bnd.atom2.xz d2 = dx * dx + dy * dy + dz * dz req = bnd.type.req # Warn if any bond starts at > 3 times its equilibrium length if d2 > 9 * req * req: warnings.warn( 'Atoms %d (%s %d [%s]) and %d (%s %d [%s]) are ' 'bonded (equilibrium length %.3f A) but are %.3f A apart. ' 'This often indicates gaps in the original sequence and ' 'should be checked carefully.' % (bnd.atom1.idx + 1, bnd.atom1.residue.name, bnd.atom1.residue.idx + 1, bnd.atom1.name, bnd.atom2.idx + 1, bnd.atom2.residue.name, bnd.atom2.residue.idx + 1, bnd.atom2.name, req, sqrt(d2)), LongBondWarning)
def _parse_residue(fileobj, name): """ Parses the residue information out of the OFF file assuming the file is pointed at the first line of an atoms table section of the OFF file Parameters ---------- fileobj : file-like Assumed to be open for read, this file is parsed until the *next* atom table is read name : str The name of the residue being processed right now """ container = ResidueTemplateContainer(name) nres = 1 templ = ResidueTemplate(name) line = fileobj.readline() while line[0] != '!': nam, typ, typx, resx, flags, seq, elmnt, chg = line.split() nam = _strip_enveloping_quotes(nam) typ = _strip_enveloping_quotes(typ) typx = int(typx) resx = int(resx) flags = int(flags) seq = int(seq) elmnt = int(elmnt) chg = float(chg) atom = Atom(atomic_number=elmnt, type=typ, name=nam, charge=chg) if resx == nres + 1: container.append(templ) nres += 1 templ = ResidueTemplate(name) templ.add_atom(atom) line = fileobj.readline() # Skip blank lines while line and not line.strip(): line = fileobj.readline() container.append(templ) if nres > 1: start_atoms = [] runsum = 0 for res in container: start_atoms.append(runsum) runsum += len(res) # Make sure we get the next section rematch = AmberOFFLibrary._sec2re.match(line) if not rematch: raise RuntimeError('Expected pertinfo table not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) line = fileobj.readline() while line[0] != '!': if not line: raise RuntimeError('Unexpected EOF in Amber OFF library') # Not used, just skip # TODO sanity check line = fileobj.readline() rematch = AmberOFFLibrary._sec3re.match(line) if not rematch: raise RuntimeError('Expected boundbox table not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) # Only 5 lines try: hasbox = float(fileobj.readline().strip()) angle = float(fileobj.readline().strip()) a = float(fileobj.readline().strip()) b = float(fileobj.readline().strip()) c = float(fileobj.readline().strip()) except ValueError: raise RuntimeError('Error processing boundbox table entries') else: if hasbox > 0: if angle < 3.15: # No box is this acute -- must be in radians angle *= RAD_TO_DEG container.box = [a, b, c, angle, angle, angle] # Get the child sequence entry line = fileobj.readline() rematch = AmberOFFLibrary._sec4re.match(line) if not rematch: raise RuntimeError('Expected childsequence table not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) n = int(fileobj.readline().strip()) if nres + 1 != n: warnings.warn( 'Unexpected childsequence (%d); expected %d for ' 'residue %s' % (n, nres + 1, name), AmberWarning) elif not isinstance(templ, ResidueTemplate) and n != len(templ) + 1: raise RuntimeError('child sequence must be 1 greater than the ' 'number of residues in the unit') # Get the CONNECT array to set head and tail line = fileobj.readline() rematch = AmberOFFLibrary._sec5re.match(line) if not rematch: raise RuntimeError('Expected connect array not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) try: head = int(fileobj.readline().strip()) tail = int(fileobj.readline().strip()) except ValueError: raise RuntimeError('Error processing connect table entries') if head > 0 and nres == 1: templ.head = templ[head - 1] elif head > 0 and nres > 1: if head < sum((len(r) for r in container)): raise RuntimeError('HEAD on multi-residue unit not supported') if tail > 0 and nres == 1: templ.tail = templ[tail - 1] elif tail > 0 and nres > 1: if tail < sum((len(r) for r in container)): warnings.warn( 'TAIL on multi-residue unit not supported (%s). ' 'Ignored...' % name, AmberWarning) # Get the connectivity array to set bonds line = fileobj.readline() if len(templ.atoms) > 1: rematch = AmberOFFLibrary._sec6re.match(line) if not rematch: raise RuntimeError('Expected connectivity table not found') elif rematch.groups()[0] != name: raise RuntimeError( 'Found residue %s while processing residue %s' % (rematch.groups()[0], name)) line = fileobj.readline() while line[0] != '!': i, j, flag = line.split() line = fileobj.readline() if nres > 1: # Find which residue we belong in i = int(i) - 1 j = int(j) - 1 for ii, idx in enumerate(start_atoms): if idx > i: ii -= 1 break start_idx = start_atoms[ii] container[ii].add_bond(i - start_idx, j - start_idx) else: templ.add_bond(int(i) - 1, int(j) - 1) # Get the hierarchy table rematch = AmberOFFLibrary._sec7re.match(line) if not rematch: raise RuntimeError('Expected hierarchy table not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) line = fileobj.readline() while line[0] != '!': # Skip this section... not used # TODO turn this into a sanity check line = fileobj.readline() # Get the unit name rematch = AmberOFFLibrary._sec8re.match(line) if not rematch: raise RuntimeError('Expected unit name string not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) fileobj.readline() # Skip this... not used line = fileobj.readline() # Get the atomic positions rematch = AmberOFFLibrary._sec9re.match(line) if not rematch: raise RuntimeError('Expected unit positions table not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) for res in container: for atom in res: x, y, z = fileobj.readline().split() atom.xx, atom.xy, atom.xz = float(x), float(y), float(z) line = fileobj.readline() # Get the residueconnect table rematch = AmberOFFLibrary._sec10re.match(line) if not rematch: raise RuntimeError('Expected unit residueconnect table not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) for i in range(nres): c1, c2, c3, c4, c5, c6 = (int(x) for x in fileobj.readline().split()) if (c1 > 0 and templ.head is not None and templ.head is not templ[c1 - 1]): raise RuntimeError('HEAD atom is not connect0') if (c2 > 0 and templ.tail is not None and templ.tail is not templ[c2 - 1]): raise RuntimeError('TAIL atom is not connect1') for i in (c3, c4, c5, c6): if i == 0: continue templ.connections.append(templ[i - 1]) # Get the residues table line = fileobj.readline() rematch = AmberOFFLibrary._sec11re.match(line) if not rematch: raise RuntimeError('Expected unit residues table not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) for i in range(nres): resname, id, next, start, typ, img = fileobj.readline().split() resname = _strip_enveloping_quotes(resname) id = int(id) start = int(start) next = int(next) typ = _strip_enveloping_quotes(typ) img = int(img) if next - start != len(container[i]): warnings.warn( 'residue table predicted %d, not %d atoms for ' 'residue %s' % (next - start, len(container[i]), name), AmberWarning) if typ == 'p': container[i].type = PROTEIN elif typ == 'n': container[i].type = NUCLEIC elif typ == 'w': container[i].type = SOLVENT elif typ != '?': warnings.warn('Unknown residue type "%s"' % typ, AmberWarning) if nres > 1: container[i].name = resname # Get the residues sequence table line = fileobj.readline() rematch = AmberOFFLibrary._sec12re.match(line) if not rematch: raise RuntimeError('Expected residue sequence number not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) for i in range(nres): #TODO sanity check fileobj.readline() line = fileobj.readline() # Get the solventcap array rematch = AmberOFFLibrary._sec13re.match(line) if not rematch: raise RuntimeError('Expected unit solventcap array not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) # Ignore the solvent cap fileobj.readline() fileobj.readline() fileobj.readline() fileobj.readline() fileobj.readline() # Velocities line = fileobj.readline() rematch = AmberOFFLibrary._sec14re.match(line) if not rematch: raise RuntimeError('Expected unit solventcap array not found') elif rematch.groups()[0] != name: raise RuntimeError('Found residue %s while processing residue %s' % (rematch.groups()[0], name)) for res in container: for atom in res: vx, vy, vz = (float(x) for x in fileobj.readline().split()) atom.vx, atom.vy, atom.vz = vx, vy, vz if nres > 1: return container return templ
def __setitem__(self, key, rhs): for n in range(len(self.data)): self.data[n][key] = rhs[n]
def _parse(self): """ This method parses the data out of the ASCII restart file and creates self._coordinates and self._velocities as np.ndarray(natom*3) arrays This method is called automatically for 'old' restart files and should not be called by external callers """ lines = self._file.readlines() self._file.close() self.title = lines[0].strip() self.natom = int(lines[1].strip().split()[0]) try: self.time = float(lines[1].strip().split()[1]) except IndexError: self.time = 0.0 # Get rid of any trailing newlines while not lines[-1].strip(): lines.pop() # Determine what information we have based on the number of lines # present if len(lines) == int(ceil(self.natom / 2.0) + 2): self.hasbox = self.hasvels = False elif len(lines) == int(ceil(self.natom / 2.0) + 3): self.hasbox = True self.hasvels = False elif len(lines) == int(2 * ceil(self.natom / 2.0) + 2): self.hasbox = False self.hasvels = True elif len(lines) == int(2 * ceil(self.natom / 2.0) + 3): self.hasbox = self.hasvels = True else: raise RuntimeError('Badly formatted restart file. Has %d lines ' 'for %d atoms.' % (len(lines), self.natom)) self._coordinates = np.zeros((self.natom, 3)) if self.hasvels: self._velocities = np.zeros((self.natom, 3)) if self.hasbox: self._cell_lengths = np.zeros(3) self._cell_angles = np.zeros(3) # Now it's time to parse. Coordinates first startline = 2 endline = startline + int(ceil(self.natom / 2.0)) idx = 0 for i in range(startline, endline): line = lines[i] x1 = float(line[0:12]) y1 = float(line[12:24]) z1 = float(line[24:36]) self._coordinates[idx] = [x1, y1, z1] idx += 1 try: x2 = float(line[36:48]) y2 = float(line[48:60]) z2 = float(line[60:72]) except ValueError: pass else: self._coordinates[idx] = [x2, y2, z2] idx += 1 self._coordinates = self._coordinates.reshape((1, self.natom, 3)) startline = endline # Now it's time to parse the velocities if we have them if self.hasvels: endline = startline + int(ceil(self.natom / 2.0)) idx = 0 for i in range(startline, endline): line = lines[i] x1 = float(line[0:12]) * VELSCALE y1 = float(line[12:24]) * VELSCALE z1 = float(line[24:36]) * VELSCALE self._velocities[idx] = [x1, y1, z1] idx += 1 try: x2 = float(line[36:48]) * VELSCALE y2 = float(line[48:60]) * VELSCALE z2 = float(line[60:72]) * VELSCALE except ValueError: pass else: self._velocities[idx] = [x2, y2, z2] idx += 1 startline = endline self._velocities = self._velocities.reshape((1, self.natom, 3)) # Now it's time to parse the box info if we have it if self.hasbox: line = lines[startline] self._cell_lengths[0:3] = [ float(line[0:12]), float(line[12:24]), float(line[24:36]) ] self._cell_angles[0:3] = [ float(line[36:48]), float(line[48:60]), float(line[60:72]) ]
def __init__(self, natom): self.natom = natom list.__init__(self, [0 for i in range(natom)])
def __emptyRow(self): return [None for ii in range(len(self._attributeNameList))]
def _set_nonbonded_tables(self, nbfixes=None): """ Sets the tables of Lennard-Jones nonbonded interaction pairs """ from parmed.tools.actions import addLJType data = self.parm_data ntypes = data['POINTERS'][NTYPES] ntypes2 = ntypes * ntypes # Set up the index lookup tables (not a unique solution) data['NONBONDED_PARM_INDEX'] = [0 for i in range(ntypes2)] holder = [0 for i in range(ntypes2)] idx = 0 for i in range(ntypes): for j in range(i + 1): idx += 1 holder[ntypes * i + j] = holder[ntypes * j + i] = idx idx = 0 for i in range(ntypes): for j in range(ntypes): data['NONBONDED_PARM_INDEX'][idx] = \ holder[ntypes*i+j] idx += 1 nttyp = ntypes * (ntypes + 1) // 2 # Now build the Lennard-Jones arrays data['LENNARD_JONES_14_ACOEF'] = [0 for i in range(nttyp)] data['LENNARD_JONES_14_BCOEF'] = [0 for i in range(nttyp)] data['LENNARD_JONES_ACOEF'] = [0 for i in range(nttyp)] data['LENNARD_JONES_BCOEF'] = [0 for i in range(nttyp)] self.recalculate_LJ() # Now make any NBFIX modifications we had if nbfixes is not None: for i, fix in enumerate(nbfixes): for terms in fix: j, rmin, eps, rmin14, eps14 = terms i, j = min(i, j - 1), max(i, j - 1) eps = abs(eps) eps14 = abs(eps14) idx = data['NONBONDED_PARM_INDEX'][ntypes * i + j] - 1 data['LENNARD_JONES_ACOEF'][idx] = eps * rmin**12 data['LENNARD_JONES_BCOEF'][idx] = 2 * eps * rmin**6 data['LENNARD_JONES_14_ACOEF'][idx] = eps14 * rmin14**12 data['LENNARD_JONES_14_BCOEF'][idx] = 2 * eps14 * rmin14**6 # If we had an explicit set of exceptions, we need to implement all of # those exclusions. The electrostatic component of that exception is # already handled (since a scaling factor *can* represent the full # flexibility of electrostatic exceptions). The vdW component must be # handled as 1-4 A- and B-coefficients. If vdW types are too compressed # for this to be handled correctly, use "addLJType" to expand the types # by 1 (so the tables only get as big as they *need* to get, to make # sure they continue to fit in CUDA shared memory). # # The way we do this is to fill all of the elements with None, then fill # them in as we walk through the 1-4 exceptions. If we hit a case where # the target element is not None and *doesn't* equal the computed A- and # B-coefficients, we have to expand our type list (assume all type # names will have the same L-J parameters, which has been a fair # assumption in my experience). if not self.adjusts: return for i in range(len(data['LENNARD_JONES_14_ACOEF'])): data['LENNARD_JONES_14_ACOEF'][i] = None data['LENNARD_JONES_14_BCOEF'][i] = None ii = 0 replaced_atoms = set() while True: needed_split = False for pair in self.adjusts: a1, a2 = pair.atom1, pair.atom2 i, j = sorted([a1.nb_idx - 1, a2.nb_idx - 1]) idx = data['NONBONDED_PARM_INDEX'][ntypes * i + j] - 1 eps = pair.type.epsilon rmin = pair.type.rmin rmin6 = rmin * rmin * rmin * rmin * rmin * rmin acoef = eps * rmin6 * rmin6 bcoef = 2 * eps * rmin6 if data['LENNARD_JONES_14_ACOEF'][idx] is not None: if abs(data['LENNARD_JONES_14_ACOEF'][idx] - acoef) > SMALL: # Need to split out another type needed_split = True assert (a1 not in replaced_atoms or a2 not in replaced_atoms) # Only add each atom as a new type ONCE if a1 in replaced_atoms: mask = '@%d' % (a2.idx + 1) replaced_atoms.add(a2) else: mask = '@%d' % (a1.idx + 1) replaced_atoms.add(a1) addLJType(self, mask, radius_14=0, epsilon_14=0).execute() ntypes += 1 # None-out all of the added terms j = ntypes - 1 for i in range(j): _ = data['NONBONDED_PARM_INDEX'][ntypes * i + j] - 1 data['LENNARD_JONES_14_ACOEF'][_] = None data['LENNARD_JONES_14_BCOEF'][_] = None # We can stop here, since the next loop through the # explicit exclusions will fill this in else: data['LENNARD_JONES_14_ACOEF'][idx] = acoef data['LENNARD_JONES_14_BCOEF'][idx] = bcoef ii += 1 if not needed_split: break # The following should never happen assert ii <= len(self.atoms)+1, 'Could not resolve all exceptions. ' \ 'Some unexpected problem with the algorithm' # Now go through and change all None's to 0s, as these terms won't be # used for any exceptions, anyway for i, item in enumerate(data['LENNARD_JONES_14_ACOEF']): if item is None: assert data['LENNARD_JONES_14_BCOEF'][i] is None, \ 'A- and B- coefficients must be in lock-step!' data['LENNARD_JONES_14_ACOEF'][i] = 0.0 data['LENNARD_JONES_14_BCOEF'][i] = 0.0
def add_flag(self, flag_name, flag_format, data=None, num_items=-1, comments=None, after=None): """ Adds a new flag with the given flag name and Fortran format string and initializes the array with the values given, or as an array of 0s of length num_items Parameters ---------- flag_name : str Name of the flag to insert. It is converted to all upper case flag_format : str Fortran format string representing how the data in this section should be written and read. Do not enclose in () data : list=None Sequence with data for the new flag. If None, a list of zeros of length ``num_items`` (see below) is given as a holder num_items : int=-1 Number of items in the section. This variable is ignored if a set of data are given in `data` comments : list of str=None List of comments to add to this section after : str=None If provided, the added flag will be added after the one with the name given to `after`. If this flag does not exist, IndexError will be raised Raises ------ AmberError if flag already exists IndexError if the ``after`` flag does not exist """ if flag_name in self.parm_data: raise AmberError('%s already exists' % (flag_name)) if after is not None: after = after.upper() if not after in self.flag_list: raise IndexError('%s not found in topology flag list' % after) # If the 'after' flag is the last one, just append if self.flag_list[-1] == after: self.flag_list.append(flag_name.upper()) else: # Otherwise find the index and add it after idx = self.flag_list.index(after) + 1 self.flag_list.insert(idx, flag_name.upper()) else: self.flag_list.append(flag_name.upper()) self.formats[flag_name.upper()] = FortranFormat(flag_format) if data is not None: self.parm_data[flag_name.upper()] = list(data) else: if num_items < 0: raise AmberError("If you do not supply prmtop data, num_items " "must be non-negative!") self.parm_data[flag_name.upper()] = [0 for i in range(num_items)] if comments is not None: if isinstance(comments, string_types): comments = [comments] else: comments = list(comments) self.parm_comments[flag_name.upper()] = comments else: self.parm_comments[flag_name.upper()] = []
def _atnum_select(self, at1, at2, mask): """ Fills a _mask array between atom numbers at1 and at2 """ for i in range(at1 - 1, at2): mask[i] = 1
def read(self, fname): """ Parses the .dyn file """ f = open(fname, 'r') try: if f.readline().strip() != 'Number of Atoms and Title :': raise TinkerError('%s is not a recognized TINKER .dyn file' % fname) line = f.readline() self.natom, self.title = int(line[0:8].strip()), line[8:].strip() if f.readline().strip() != 'Periodic Box Dimensions :': raise TinkerError('No periodic box dimension line') self.box = [0.0 for i in range(6)] words = f.readline().upper().split() self.box[0] = float(words[0].replace('D', 'E')) self.box[1] = float(words[1].replace('D', 'E')) self.box[2] = float(words[2].replace('D', 'E')) words = f.readline().upper().split() self.box[3] = float(words[0].replace('D', 'E')) self.box[4] = float(words[1].replace('D', 'E')) self.box[5] = float(words[2].replace('D', 'E')) if f.readline().strip() != 'Current Atomic Positions :': raise TinkerError('No atomic positions in .dyn file') self.positions = [[0.0, 0.0, 0.0] for i in range(self.natom)] DynFile._read_section(f, self.positions, self.natom) line = f.readline().strip() if line == 'Current Translational Velocities :': self.rigidbody = True self.translational_velocities = [[0.0, 0.0, 0.0] for i in range(self.natom)] DynFile._read_section(f, self.translational_velocities, self.natom) if f.readline().strip() != 'Current Angular Velocities :': raise TinkerError('Could not find Angular velocity ' 'section in .dyn file') self.angular_velocities = [[0.0, 0.0, 0.0] for i in range(self.natom)] DynFile._read_section(f, self.angular_velocities, self.natom) if f.readline().strip() != 'Current Angular Momenta :': raise TinkerError('Could not find angular momenta section ' 'in .dyn file') self.angular_momenta = [[0.0, 0.0, 0.0] for i in range(self.natom)] DynFile._read_section(f, self.angular_momenta, self.natom) elif line == 'Current Atomic Velocities :': self.rigidbody = False self.velocities = [[0.0, 0.0, 0.0] for i in range(self.natom)] DynFile._read_section(f, self.velocities, self.natom) if f.readline().strip() != 'Current Atomic Accelerations :': raise TinkerError('Could not find accelerations in %s ' % fname) self.accelerations = [[0.0, 0.0, 0.0] for i in range(self.natom)] DynFile._read_section(f, self.accelerations, self.natom) if f.readline().strip() != 'Alternate Atomic Accelerations :': raise TinkerError( 'Could not find old accelerations in %s' % fname) self.old_accelerations = [[0.0, 0.0, 0.0] for i in range(self.natom)] DynFile._read_section(f, self.old_accelerations, self.natom) else: raise TinkerError('No velocities in %s' % fname) finally: f.close()