def _parse(self, fname): with closing(io.genopen(fname, 'r')) as crdfile: readingHeader = True while readingHeader: line = crdfile.readline() if not len(line): raise CharmmError('Premature end of file') line = line.strip() words = line.split() if len(line) != 0: if words[0] == 'ENERGIES' or words[0] == '!ENERGIES': readingHeader = False else: self.header.append(line.strip()) else: self.header.append(line.strip()) for row in range(len(self.header)): if len(self.header[row].strip()) != 0: line = self.header[row].strip().split() if line[0][0:5] == 'NATOM' or line[0][0:6] == '!NATOM': try: line = self.header[row + 1].strip().split() self.natom = int(line[0]) self.npriv = int(line[1]) # num. previous steps self.nstep = int(line[2]) # num. steps in file self.nsavc = int(line[3]) # coord save frequency self.nsavv = int(line[4]) # velocities " self.jhstrt = int(line[5]) # Num total steps? break except (ValueError, IndexError): raise CharmmError('Problem parsing CHARMM restart') self.scan(crdfile, '!XOLD') self._get_formatted_crds(crdfile, self.coordsold) self.coordsold = np.array(self.coordsold).reshape( (-1, self.natom, 3)) self.scan(crdfile, '!VX') self._get_formatted_crds(crdfile, self.vels) self.vels = np.array(self.vels).reshape((-1, self.natom, 3)) # Convert velocities to angstroms/ps self.vels *= ONE_TIMESCALE self.scan(crdfile, '!X') self._get_formatted_crds(crdfile, self.coords) self.coords = np.array(self.coords).reshape((-1, self.natom, 3))
def _parse_psf_section(psf): """ This method parses a section of the PSF file Parameters ---------- psf : file Open file that is pointing to the first line of the section that is to be parsed Returns ------- title : str The label of the PSF section we are parsing pointers : (int/tuple of ints) If one pointer is set, pointers is simply the integer that is value of that pointer. Otherwise it is a tuple with every pointer value defined in the first line data : list A list of all data in the parsed section converted to integers """ conv = CharmmPsfFile._convert line = psf.readline() while not line.strip(): if not line: raise _FileEOF('Unexpected EOF in PSF file') else: line = psf.readline() if '!' in line: words = line[:line.index('!')].split() title = line[line.index('!') + 1:].strip().upper() # Strip out description if ':' in title: title = title[:title.index(':')] else: raise CharmmError( 'Could not determine section title') # pragma: no cover if len(words) == 1: pointers = conv(words[0], int, 'pointer') else: pointers = tuple([conv(w, int, 'pointer') for w in words]) line = psf.readline().strip() if not line and title.startswith('NNB'): # This will correctly handle the NNB section (which has a spurious # blank line) as well as any sections that have 0 members. line = psf.readline().strip() data = [] if title == 'NATOM' or title == 'NTITLE': # Store these two sections as strings (ATOM section we will parse # later). The rest of the sections are integer pointers while line: data.append(line) line = psf.readline().strip() else: while line: words = line.split() data.extend([conv(w, int, 'PSF data') for w in words]) line = psf.readline().strip() return title, pointers, data
def _get_formatted_crds(self, crdfile, crds): for row in range(self.natom): line = crdfile.readline() if not line: raise CharmmError('Premature end of file') if len(line) < 3 * CHARMLEN: raise CharmmError("Less than 3 coordinates present in " "coordinate row or coords may be " "truncated.") line = line.replace('D', 'E') # CHARMM uses 'D' for exponentials # CHARMM uses fixed format (len = CHARMLEN = 22) for crds in .rst's crds.append(float(line[0:CHARMLEN])) crds.append(float(line[CHARMLEN:2 * CHARMLEN])) crds.append(float(line[2 * CHARMLEN:3 * CHARMLEN]))
def scan(self, handle, str, r=0): # read lines in file till 'str' is found scanning = True if (r): handle.seek(0) while scanning: line = handle.readline() if not line: raise CharmmError('Premature end of file') if len(line.strip()) != 0: if line.strip().split()[0][0:len(str)] == str: scanning = False
def _convert(string, type, message): """ Converts a string to a specific type, making sure to raise CharmmError with the given message in the event of a failure. Parameters ---------- string : str Input string to process type : type Type of data to convert to (e.g., ``int``) message : str Error message to put in exception if failed """ try: return type(string) except ValueError: raise CharmmError('Could not convert %s [%s]' % (message, string))
def _parse(self, fname): with closing(io.genopen(fname, 'r')) as crdfile: line = crdfile.readline().strip() while len(line) == 0: # Skip whitespace, as a precaution line = crdfile.readline().strip() intitle = True while intitle: self.title.append(line) line = crdfile.readline().strip() if len(line) == 0: intitle = False elif line[0] != '*': intitle = False else: intitle = True while len(line) == 0: # Skip whitespace line = crdfile.readline().strip() try: self.natom = int(line.split()[0]) for row in range(self.natom): line = crdfile.readline().split() self.atomno.append(int(line[0])) self.resno.append(int(line[1])) self.resname.append(line[2]) self.atname.append(line[3]) self.coords.append(float(line[4])) self.coords.append(float(line[5])) self.coords.append(float(line[6])) self.segid.append(line[7]) self.resid.append(int(line[8])) self.weighting.append(float(line[9])) assert 3 * self.natom == len(self.coords), '# atom mismatch' except (ValueError, IndexError): raise CharmmError('Error parsing CHARMM coordinate file') self.coords = np.array(self.coords).reshape((-1, self.natom, 3))
def newfunc(*args, **kwargs): """ Catch the index error """ try: return func(*args, **kwargs) except IndexError as e: raise CharmmError('Array is too short: %s' % e)
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