def parsePSF(filename, title=None, ag=None): """Returns an :class:`.AtomGroup` instance storing data parsed from X-PLOR format PSF file *filename*. Atom and bond information is parsed from the file. If *title* is not given, *filename* will be set as the title of the :class:`.AtomGroup` instance. An :class:`.AtomGroup` instance may be provided as *ag* argument. When provided, *ag* must have the same number of atoms in the same order as the file. Data from PSF file will be added to the *ag*. This may overwrite present data if it overlaps with PSF file content. This function now includes the angles, dihedrals, and impropers sections as well as donors, acceptors and crossterms!""" if ag is not None: if not isinstance(ag, AtomGroup): raise TypeError('ag must be an AtomGroup instance') psf = openFile(filename, 'rb') line = psf.readline() while line: line = line.strip() if line.endswith(b'!NATOM'): n_atoms = int(line.split(b'!')[0]) break line = psf.readline() if title is None: title = os.path.splitext(os.path.split(filename)[1])[0] else: title = str(title) if ag is None: ag = AtomGroup(title) else: if n_atoms != ag.numAtoms(): raise ValueError('ag and PSF file must have same number of atoms') serials = zeros(n_atoms, ATOMIC_FIELDS['serial'].dtype) segnames = zeros(n_atoms, ATOMIC_FIELDS['segment'].dtype) resnums = zeros(n_atoms, ATOMIC_FIELDS['resnum'].dtype) resnames = zeros(n_atoms, ATOMIC_FIELDS['resname'].dtype) atomnames = zeros(n_atoms, ATOMIC_FIELDS['name'].dtype) atomtypes = zeros(n_atoms, ATOMIC_FIELDS['type'].dtype) charges = zeros(n_atoms, ATOMIC_FIELDS['charge'].dtype) masses = zeros(n_atoms, ATOMIC_FIELDS['mass'].dtype) n = 0 n_bonds = 0 for line in psf: if line.strip() == b'': continue if b'!NBOND:' in line.upper(): items = line.split() n_bonds = int(items[0]) break if n + 1 > n_atoms: continue if len(line) <= 71: serials[n] = line[:8] segnames[n] = line[9:13].strip() resnums[n] = line[14:19] resnames[n] = line[19:23].strip() atomnames[n] = line[24:28].strip() atomtypes[n] = line[29:35].strip() charges[n] = line[35:44] masses[n] = line[50:60] else: items = line.split() serials[n] = items[0] segnames[n] = items[1] resnums[n] = items[2] resnames[n] = items[3] atomnames[n] = items[4] atomtypes[n] = items[5] charges[n] = items[6] masses[n] = items[7] n += 1 if n < n_atoms: raise IOError( 'number of lines in PSF atoms block is less than the number of ' 'atoms') n_angles = 0 lines = [] for i, line in enumerate(psf): if line.strip() == b'': continue if b'!NTHETA' in line: items = line.split() n_angles = int(items[0]) break lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) b_array = fromstring(lines, count=n_bonds * 2, dtype=int, sep=' ') if len(b_array) != n_bonds * 2: raise IOError('number of bonds expected and parsed do not match') n_dihedrals = 0 lines = [] for i, line in enumerate(psf): if line.strip() == b'': continue if b'!NPHI' in line: items = line.split() n_dihedrals = int(items[0]) break lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) a_array = fromstring(lines, count=n_angles * 3, dtype=int, sep=' ') if len(a_array) != n_angles * 3: raise IOError('number of angles expected and parsed do not match') n_impropers = 0 lines = [] for i, line in enumerate(psf): if line.strip() == b'': continue if b'!NIMPHI' in line: items = line.split() n_impropers = int(items[0]) break lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) d_array = fromstring(lines, count=n_dihedrals * 4, dtype=int, sep=' ') if len(d_array) != n_dihedrals * 4: raise IOError('number of dihedrals expected and parsed do not match') n_donors = 0 lines = [] for i, line in enumerate(psf): if line.strip() == b'': continue if b'!NDON' in line: items = line.split() n_donors = int(items[0]) break lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) i_array = fromstring(lines, count=n_impropers * 4, dtype=int, sep=' ') if len(i_array) != n_impropers * 4: raise IOError('number of impropers expected and parsed do not match') n_acceptors = 0 lines = [] for i, line in enumerate(psf): if line.strip() == b'': continue if b'!NACC' in line: items = line.split() n_acceptors = int(items[0]) break lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) do_array = fromstring(lines, count=n_donors * 2, dtype=int, sep=' ') if len(do_array) != n_donors * 2: raise IOError('number of donors expected and parsed do not match') n_exclusions = 0 lines = [] for i, line in enumerate(psf): if line.strip() == b'': continue if b'!NNB' in line: items = line.split() n_exclusions = int(items[0]) break lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) ac_array = fromstring(lines, count=n_acceptors * 2, dtype=int, sep=' ') if len(ac_array) != n_acceptors * 2: raise IOError('number of acceptors expected and parsed do not match') lines = [] for i, line in enumerate(psf): if line.strip() == b'': continue if b'!' in line: break lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) nbe_array = fromstring(lines, count=n_exclusions * 2, dtype=int, sep=' ') if len(nbe_array) != n_exclusions * 2: raise IOError( 'number of nonbonded exclusions expected and parsed do not match') n_crossterms = 0 for i, line in enumerate(psf): if b'!NCRTERM' in line: items = line.split() n_crossterms = int(items[0]) break lines = [] for i, line in enumerate(psf): lines.append(line.decode(encoding='UTF-8')) lines = ''.join(lines) c_array = fromstring(lines, count=n_crossterms * 4, dtype=int, sep=' ') if len(c_array) != n_crossterms * 4: raise IOError('number of crossterms expected and parsed do not match') psf.close() ag.setSerials(serials) ag.setSegnames(segnames) ag.setResnums(resnums) ag.setResnames(resnames) ag.setNames(atomnames) ag.setTypes(atomtypes) ag.setCharges(charges) ag.setMasses(masses) if n_bonds > 0: b_array = add(b_array, -1, b_array) ag.setBonds(b_array.reshape((n_bonds, 2))) if n_angles > 0: a_array = add(a_array, -1, a_array) ag.setAngles(a_array.reshape((n_angles, 3))) if n_dihedrals > 0: d_array = add(d_array, -1, d_array) ag.setDihedrals(d_array.reshape((n_dihedrals, 4))) if n_impropers > 0: i_array = add(i_array, -1, i_array) ag.setImpropers(i_array.reshape((n_impropers, 4))) if n_donors > 0: do_array = add(do_array, -1, do_array) ag.setDonors(do_array.reshape((n_donors, 2))) if n_acceptors > 0: ac_array = add(ac_array, -1, ac_array) ag.setAcceptors(ac_array.reshape((n_acceptors, 2))) if n_exclusions > 0: nbe_array = add(nbe_array, -1, nbe_array) ag.setNBExclusions(nbe_array.reshape((n_exclusions, 2))) if n_crossterms > 0: c_array = add(c_array, -1, c_array) ag.setCrossterms(c_array.reshape((n_crossterms, 4))) return ag
def parsePSF(filename, title=None, ag=None): """Return an :class:`.AtomGroup` instance storing data parsed from X-PLOR format PSF file *filename*. Atom and bond information is parsed from the file. If *title* is not given, *filename* will be set as the title of the :class:`.AtomGroup` instance. An :class:`.AtomGroup` instance may be provided as *ag* argument. When provided, *ag* must have the same number of atoms in the same order as the file. Data from PSF file will be added to the *ag*. This may overwrite present data if it overlaps with PSF file content. Note that this function does not evaluate angles, dihedrals, and impropers sections.""" if ag is not None: if not isinstance(ag, AtomGroup): raise TypeError('ag must be an AtomGroup instance') psf = openFile(filename, 'rb') line = psf.readline() i_line = 1 while line: line = line.strip() if line.endswith('!NATOM'): n_atoms = int(line.split('!')[0]) break line = psf.readline() i_line += 1 if title is None: title = os.path.splitext(os.path.split(filename)[1])[0] else: title = str(title) if ag is None: ag = AtomGroup(title) else: if n_atoms != ag.numAtoms(): raise ValueError('ag and PSF file must have same number of atoms') serials = zeros(n_atoms, ATOMIC_FIELDS['serial'].dtype) segnames = zeros(n_atoms, ATOMIC_FIELDS['segment'].dtype) resnums = zeros(n_atoms, ATOMIC_FIELDS['resnum'].dtype) resnames = zeros(n_atoms, ATOMIC_FIELDS['resname'].dtype) atomnames = zeros(n_atoms, ATOMIC_FIELDS['name'].dtype) atomtypes = zeros(n_atoms, ATOMIC_FIELDS['type'].dtype) charges = zeros(n_atoms, ATOMIC_FIELDS['charge'].dtype) masses = zeros(n_atoms, ATOMIC_FIELDS['mass'].dtype) lines = psf.readlines(71 * (n_atoms + 5)) if len(lines) < n_atoms: raise IOError('number of lines in PSF is less than the number of ' 'atoms') for i, line in enumerate(lines): if i == n_atoms: break i_line += 1 if len(line) <= 71: serials[i] = line[:8] segnames[i] = line[9:13].strip() resnums[i] = line[14:19] resnames[i] = line[19:23].strip() atomnames[i] = line[24:28].strip() atomtypes[i] = line[29:35].strip() charges[i] = line[35:44] masses[i] = line[50:60] else: items = line.split() serials[i] = items[0] segnames[i] = items[1] resnums[i] = items[2] resnames[i] = items[3] atomnames[i] = items[4] atomtypes[i] = items[5] charges[i] = items[6] masses[i] = items[7] i = n_atoms while 1: line = lines[i].split() if len(line) >= 2 and line[1] == '!NBOND:': n_bonds = int(line[0]) break i += 1 lines = ''.join(lines[i + 1:]) + psf.read(n_bonds / 4 * 71) array = fromstring(lines, count=n_bonds * 2, dtype=int, sep=' ') if len(array) != n_bonds * 2: raise IOError('number of bonds expected and parsed do not match') psf.close() ag.setSerials(serials) ag.setSegnames(segnames) ag.setResnums(resnums) ag.setResnames(resnames) ag.setNames(atomnames) ag.setTypes(atomtypes) ag.setCharges(charges) ag.setMasses(masses) array = add(array, -1, array) ag.setBonds(array.reshape((n_bonds, 2))) return ag
def parsePSF(filename, title=None, ag=None): """Return an :class:`.AtomGroup` instance storing data parsed from X-PLOR format PSF file *filename*. Atom and bond information is parsed from the file. If *title* is not given, *filename* will be set as the title of the :class:`.AtomGroup` instance. An :class:`.AtomGroup` instance may be provided as *ag* argument. When provided, *ag* must have the same number of atoms in the same order as the file. Data from PSF file will be added to the *ag*. This may overwrite present data if it overlaps with PSF file content. Note that this function does not evaluate angles, dihedrals, and impropers sections.""" if ag is not None: if not isinstance(ag, AtomGroup): raise TypeError('ag must be an AtomGroup instance') psf = openFile(filename, 'rb') line = psf.readline() i_line = 1 while line: line = line.strip() if line.endswith('!NATOM'): n_atoms = int(line.split('!')[0]) break line = psf.readline() i_line += 1 if title is None: title = os.path.splitext(os.path.split(filename)[1])[0] else: title = str(title) if ag is None: ag = AtomGroup(title) else: if n_atoms != ag.numAtoms(): raise ValueError('ag and PSF file must have same number of atoms') serials = zeros(n_atoms, ATOMIC_FIELDS['serial'].dtype) segnames = zeros(n_atoms, ATOMIC_FIELDS['segment'].dtype) resnums = zeros(n_atoms, ATOMIC_FIELDS['resnum'].dtype) resnames = zeros(n_atoms, ATOMIC_FIELDS['resname'].dtype) atomnames = zeros(n_atoms, ATOMIC_FIELDS['name'].dtype) atomtypes = zeros(n_atoms, ATOMIC_FIELDS['type'].dtype) charges = zeros(n_atoms, ATOMIC_FIELDS['charge'].dtype) masses = zeros(n_atoms, ATOMIC_FIELDS['mass'].dtype) lines = psf.readlines(71 * (n_atoms + 5)) if len(lines) < n_atoms: raise IOError('number of lines in PSF is less than the number of ' 'atoms') for i, line in enumerate(lines): if i == n_atoms: break i_line += 1 if len(line) <= 71: serials[i] = line[:8] segnames[i] = line[9:13].strip() resnums[i] = line[14:19] resnames[i] = line[19:23].strip() atomnames[i] = line[24:28].strip() atomtypes[i] = line[29:35].strip() charges[i] = line[35:44] masses[i] = line[50:60] else: items = line.split() serials[i] = items[0] segnames[i] = items[1] resnums[i] = items[2] resnames[i] = items[3] atomnames[i] = items[4] atomtypes[i] = items[5] charges[i] = items[6] masses[i] = items[7] i = n_atoms while 1: line = lines[i].split() if len(line) >= 2 and line[1] == '!NBOND:': n_bonds = int(line[0]) break i += 1 lines = ''.join(lines[i+1:]) + psf.read(n_bonds/4 * 71) array = fromstring(lines, count=n_bonds*2, dtype=int, sep=' ') if len(array) != n_bonds*2: raise IOError('number of bonds expected and parsed do not match') psf.close() ag.setSerials(serials) ag.setSegnames(segnames) ag.setResnums(resnums) ag.setResnames(resnames) ag.setNames(atomnames) ag.setTypes(atomtypes) ag.setCharges(charges) ag.setMasses(masses) array = add(array, -1, array) ag.setBonds(array.reshape((n_bonds, 2))) return ag
def parsePSF(filename, title=None, ag=None): """Returns an :class:`.AtomGroup` instance storing data parsed from X-PLOR format PSF file *filename*. Atom and bond information is parsed from the file. If *title* is not given, *filename* will be set as the title of the :class:`.AtomGroup` instance. An :class:`.AtomGroup` instance may be provided as *ag* argument. When provided, *ag* must have the same number of atoms in the same order as the file. Data from PSF file will be added to the *ag*. This may overwrite present data if it overlaps with PSF file content. Note that this function does not evaluate angles, dihedrals, and impropers sections.""" if ag is not None: if not isinstance(ag, AtomGroup): raise TypeError("ag must be an AtomGroup instance") psf = openFile(filename, "rb") line = psf.readline() i_line = 1 while line: line = line.strip() if line.endswith(b"!NATOM"): n_atoms = int(line.split(b"!")[0]) break line = psf.readline() i_line += 1 if title is None: title = os.path.splitext(os.path.split(filename)[1])[0] else: title = str(title) if ag is None: ag = AtomGroup(title) else: if n_atoms != ag.numAtoms(): raise ValueError("ag and PSF file must have same number of atoms") serials = zeros(n_atoms, ATOMIC_FIELDS["serial"].dtype) segnames = zeros(n_atoms, ATOMIC_FIELDS["segment"].dtype) resnums = zeros(n_atoms, ATOMIC_FIELDS["resnum"].dtype) resnames = zeros(n_atoms, ATOMIC_FIELDS["resname"].dtype) atomnames = zeros(n_atoms, ATOMIC_FIELDS["name"].dtype) atomtypes = zeros(n_atoms, ATOMIC_FIELDS["type"].dtype) charges = zeros(n_atoms, ATOMIC_FIELDS["charge"].dtype) masses = zeros(n_atoms, ATOMIC_FIELDS["mass"].dtype) # lines = psf.readlines(71 * (n_atoms + 5)) n = 0 n_bonds = 0 for i, line in enumerate(psf): if line.strip() == b"": continue if b"!NBOND:" in line.upper(): items = line.split() n_bonds = int(items[0]) break if n + 1 > n_atoms: continue if len(line) <= 71: serials[n] = line[:8] segnames[n] = line[9:13].strip() resnums[n] = line[14:19] resnames[n] = line[19:23].strip() atomnames[n] = line[24:28].strip() atomtypes[n] = line[29:35].strip() charges[n] = line[35:44] masses[n] = line[50:60] else: items = line.split() serials[n] = items[0] segnames[n] = items[1] resnums[n] = items[2] resnames[n] = items[3] atomnames[n] = items[4] atomtypes[n] = items[5] charges[n] = items[6] masses[n] = items[7] n += 1 if n < n_atoms: raise IOError("number of lines in PSF is less than the number of " "atoms") # i = n_atoms # while 1: # line = lines[i].split() # if len(line) >= 2 and line[1] == '!NBOND:': # n_bonds = int(line[0]) # break # i += 1 # lines = ''.join(lines[i+1:]) + psf.read(n_bonds/4 * 71) lines = [] for i, line in enumerate(psf): if line.strip() == b"": continue if b"!" in line: break lines.append(line.decode(encoding="UTF-8")) lines = "".join(lines) array = fromstring(lines, count=n_bonds * 2, dtype=int, sep=" ") if len(array) != n_bonds * 2: raise IOError("number of bonds expected and parsed do not match") psf.close() ag.setSerials(serials) ag.setSegnames(segnames) ag.setResnums(resnums) ag.setResnames(resnames) ag.setNames(atomnames) ag.setTypes(atomtypes) ag.setCharges(charges) ag.setMasses(masses) array = add(array, -1, array) ag.setBonds(array.reshape((n_bonds, 2))) return ag