def _parse(self, fname): crdfile = open(fname, 'r') readingHeader = True while readingHeader: line = crdfile.readline() if not len(line): raise CharmmFileError('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), e: raise CharmmFileError('Problem parsing CHARMM restart')
def _parse(self, fname): crdfile = open(fname, 'r') readingHeader = True while readingHeader: line = crdfile.readline() if not len(line): raise CharmmFileError('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) as e: raise CharmmFileError('Problem parsing CHARMM restart') self._scan(crdfile, '!XOLD') self._get_formatted_crds(crdfile, self.positionsold) self._scan(crdfile, '!VX') self._get_formatted_crds(crdfile, self.velocities) self._scan(crdfile, '!X') self._get_formatted_crds(crdfile, self.positions) # Convert velocities to angstroms/ps self.velocities = [v * ONE_TIMESCALE for v in self.velocities] # Add units to positions and velocities self.positions = u.Quantity(self.positions, u.angstroms) self.positionsold = u.Quantity(self.positionsold, u.angstroms) self.velocities = u.Quantity(self.velocities, u.angstroms / u.picoseconds)
def _parse(self, fname): with open(fname, 'r') as crdfile: line = crdfile.readline() while len(line.strip()) == 0: # Skip whitespace, as a precaution line = crdfile.readline() intitle = True while intitle: self.title.append(line.strip()) line = crdfile.readline() if len(line.strip()) == 0: intitle = False elif line.strip()[0] != '*': intitle = False else: intitle = True while len(line.strip()) == 0: # Skip whitespace line = crdfile.readline() try: self.natom = int(line.strip().split()[0]) for _ in range(self.natom): line = crdfile.readline().strip().split() self.atomno.append(int(line[0])) self.resno.append(int(line[1])) self.resname.append(line[2]) self.attype.append(line[3]) pos = Vec3(float(line[4]), float(line[5]), float(line[6])) self.positions.append(pos) self.segid.append(line[7]) self.weighting.append(float(line[9])) if self.natom != len(self.positions): raise CharmmFileError("Error parsing CHARMM .crd file: %d " "atoms requires %d positions (not %d)" % (self.natom, self.natom, len(self.positions)) ) except (ValueError, IndexError): raise CharmmFileError('Error parsing CHARMM coordinate file') # Apply units to the positions now. Do it this way to allow for # (possible) numpy functionality in the future. self.positions = u.Quantity(self.positions, u.angstroms)
def _parse(self, fname): crdfile = open(fname, 'r') line = crdfile.readline() while len(line.strip()) == 0: # Skip whitespace, as a precaution line = crdfile.readline() intitle = True while intitle: self.title.append(line.strip()) line = crdfile.readline() if len(line.strip()) == 0: intitle = False elif line.strip()[0] != '*': intitle = False else: intitle = True while len(line.strip()) == 0: # Skip whitespace line = crdfile.readline() try: self.natom = int(line.strip().split()[0]) for row in range(self.natom): line = crdfile.readline().strip().split() self.atomno.append(int(line[0])) self.resno.append(int(line[1])) self.resname.append(line[2]) self.attype.append(line[3]) pos = Vec3(float(line[4]), float(line[5]), float(line[6])) self.positions.append(pos) self.segid.append(line[7]) self.resid.append(int(line[8])) self.weighting.append(float(line[9])) if self.natom != len(self.positions): raise CharmmFileError( "Error parsing CHARMM .crd file: %d " "atoms requires %d positions (not %d)" % (self.natom, self.natom, len(self.positions))) except (ValueError, IndexError), e: raise CharmmFileError('Error parsing CHARMM coordinate file')
def _convert(data, type, msg=''): """ Converts a data type to a desired type, raising CharmmFileError if it fails """ try: return type(data) except ValueError: raise CharmmFileError('Could not convert %s to %s' % (msg, type))
def _get_formatted_crds(self, crdfile, crds): for row in range(self.natom): line = crdfile.readline() if not line: raise CharmmFileError('Premature end of file') if len(line) < 3*CHARMMLEN: raise CharmmFileError("Less than 3 coordinates present in " "coordinate row or positions may be " "truncated.") line = line.replace('D','E') # CHARMM uses 'D' for exponentials # CHARMM uses fixed format (len = CHARMMLEN = 22) for crds in .rst's c = Vec3(float(line[0:CHARMMLEN]), float(line[CHARMMLEN:2*CHARMMLEN]), float(line[2*CHARMMLEN:3*CHARMMLEN])) crds.append(c)
def __init__(self, fname, mode='r'): if mode not in ('r', 'w'): raise ValueError('Cannot open CharmmFile with mode "%s"' % mode) if mode == 'r': self.status = 'OLD' else: self.status = 'NEW' try: self._handle = open(fname, mode) except IOError, e: raise CharmmFileError(str(e))
def __init__(self, fname, mode='r'): if mode not in ('r', 'w'): raise ValueError('Cannot open CharmmFile with mode "%s"' % mode) if mode == 'r': self.status = 'OLD' else: self.status = 'NEW' try: self._handle = open(fname, mode, encoding='utf-8') except IOError as e: raise CharmmFileError(str(e)) self.closed = False self.line_number = 0
def _scan(self, handle, str, r=0): # read lines in file until str is found scanning = True if (r): handle.seek(0) while scanning: line = handle.readline() if not line: raise CharmmFileError('Premature end of file') if len(line.strip()) != 0: if line.strip().split()[0][0:len(str)] == str: scanning = False
def readTopologyFile(self, tfile): """Reads _only_ the atom type definitions from a topology file. This is unnecessary for versions 36 and later of the CHARMM force field. Parameters ---------- tfile : str : Name of the CHARMM TOPology file to read Notes ----- The CHARMM TOPology file is also called a Residue Topology File """ conv = CharmmParameterSet._convert if isinstance(tfile, str): own_handle = True f = CharmmFile(tfile) else: own_handle = False f = tfile for line in f: line = line.strip() if line[:4] != 'MASS': continue words = line.split() try: idx = conv(words[1], int, 'atom type') name = words[2] mass = conv(words[3], float, 'atom mass') except IndexError: raise CharmmFileError('Could not parse MASS section of %s' % tfile) # The parameter file might or might not have an element name try: elem = words[4] atomic_number = get_by_symbol(elem).atomic_number except (IndexError, KeyError): # Figure it out from the mass masselem = Element.getByMass(mass) if masselem is None: atomic_number = 0 # Extra point or something else: atomic_number = masselem.atomic_number atype = AtomType(name=name, number=idx, mass=mass, atomic_number=atomic_number) self.atom_types_str[atype.name] = atype self.atom_types_int[atype.number] = atype self.atom_types_tuple[(atype.name, atype.number)] = atype if own_handle: f.close()
def readParameterFile(self, pfile, permissive=False): """Reads all of the parameters from a parameter file. Versions 36 and later of the CHARMM force field files have an ATOMS section defining all of the atom types. Older versions need to load this information from the RTF/TOP files. Parameters ---------- pfile : str Name of the CHARMM PARameter file to read permissive : bool Accept non-bonbded parameters for undefined atom types (default: False). Notes ----- The atom types must all be loaded by the end of this routine. Either supply a PAR file with atom definitions in them or read in a RTF/TOP file first. Failure to do so will result in a raised RuntimeError. """ conv = CharmmParameterSet._convert if isinstance(pfile, str): own_handle = True f = CharmmFile(pfile) else: own_handle = False f = pfile # What section are we parsing? section = None # The current cmap we are building (these span multiple lines) current_cmap = None current_cmap_data = [] current_cmap_res = 0 nonbonded_types = dict() # Holder parameterset = None read_first_nonbonded = False previous = '' for line in f: line = previous + line.strip() previous = '' if line.endswith('-'): # This will be continued on the next line. previous = line[:-1] continue if line.startswith('!'): # This is a comment. continue if not line: # This is a blank line continue if parameterset is None and line.strip().startswith('*>>'): parameterset = line.strip()[1:78] continue # Set section if this is a section header if line.startswith('ATOMS'): section = 'ATOMS' continue if line.startswith('BONDS'): section = 'BONDS' continue if line.startswith('ANGLES'): section = 'ANGLES' continue if line.startswith('DIHEDRALS'): section = 'DIHEDRALS' continue if line.startswith('IMPROPER'): section = 'IMPROPER' continue if line.startswith('CMAP'): section = 'CMAP' continue if line.startswith('NONBONDED'): read_first_nonbonded = False section = 'NONBONDED' fields = line.upper().split() if 'NBXMOD' in fields: nbxmod = int(fields[fields.index('NBXMOD') + 1]) if nbxmod not in list(range(-5, 6)): raise CharmmFileError( 'Unsupported value for NBXMOD: %d' % nbxmod) self.nbxmod = nbxmod continue if line.startswith('NBFIX'): section = 'NBFIX' continue if line.startswith('THOLE'): section = 'NBTHOLE' continue if line.startswith('HBOND'): section = None continue # It seems like files? sections? can be terminated with 'END' if line.startswith('END'): # should this be case-insensitive? section = None continue # If we have no section, skip if section is None: continue # Now handle each section specifically if section == 'ATOMS': if not line.startswith('MASS'): continue # Should this happen? words = line.split() try: idx = conv(words[1], int, 'atom type') name = words[2] mass = conv(words[3], float, 'atom mass') except IndexError: raise CharmmFileError('Could not parse MASS section.') # The parameter file might or might not have an element name try: elem = words[4] atomic_number = get_by_symbol(elem).atomic_number except (IndexError, KeyError): # Figure it out from the mass masselem = Element.getByMass(mass) if masselem is None: atomic_number = 0 # Extra point or something else: atomic_number = masselem.atomic_number atype = AtomType(name=name, number=idx, mass=mass, atomic_number=atomic_number) self.atom_types_str[atype.name] = atype self.atom_types_int[atype.number] = atype self.atom_types_tuple[(atype.name, atype.number)] = atype continue if section == 'BONDS': words = line.split() try: type1 = words[0] type2 = words[1] k = conv(words[2], float, 'bond force constant') req = conv(words[3], float, 'bond equilibrium dist') except IndexError: raise CharmmFileError('Could not parse bonds.') key = (min(type1, type2), max(type1, type2)) self.bond_types[key] = BondType(k, req) continue if section == 'ANGLES': words = line.split() try: type1 = words[0] type2 = words[1] type3 = words[2] k = conv(words[3], float, 'angle force constant') theteq = conv(words[4], float, 'angle equilibrium value') except IndexError: raise CharmmFileError('Could not parse angles.') key = (min(type1, type3), type2, max(type1, type3)) self.angle_types[key] = AngleType(k, theteq) # See if we have a urey-bradley try: ubk = conv(words[5], float, 'Urey-Bradley force constant') ubeq = conv(words[6], float, 'Urey-Bradley equil. value') ubtype = UreyBradleyType(ubk, ubeq) except IndexError: ubtype = NoUreyBradley self.urey_bradley_types[key] = ubtype continue if section == 'DIHEDRALS': words = line.split() try: type1 = words[0] type2 = words[1] type3 = words[2] type4 = words[3] k = conv(words[4], float, 'dihedral force constant') n = conv(words[5], float, 'dihedral periodicity') phase = conv(words[6], float, 'dihedral phase') except IndexError: raise CharmmFileError('Could not parse dihedrals.') # Torsion can be in either direction. Sort by end groups first, # then sort by middle 2 if type1 < type4: key = (type1, type2, type3, type4) elif type1 > type4: key = (type4, type3, type2, type1) else: # OK, we need to sort by the middle atoms now if type2 < type3: key = (type1, type2, type3, type4) else: key = (type4, type3, type2, type1) # See if this is a second (or more) term of the dihedral group # that's already present. dihedral = DihedralType(k, n, phase) if key in self.dihedral_types: # See if the existing dihedral type list has a term with # the same periodicity -- If so, replace it replaced = False for i, dtype in enumerate(self.dihedral_types[key]): if dtype.per == dihedral.per: # Replace. Warn if they are different if dtype != dihedral: warnings.warn('Replacing dihedral %r with %r' % (dtype, dihedral)) self.dihedral_types[key] replaced = True break if not replaced: self.dihedral_types[key].append(dihedral) else: # key not present self.dihedral_types[key] = [dihedral] continue if section == 'IMPROPER': words = line.split() try: type1 = words[0] type2 = words[1] type3 = words[2] type4 = words[3] k = conv(words[4], float, 'improper force constant') theteq = conv(words[5], float, 'improper equil. value') except IndexError: raise CharmmFileError('Could not parse dihedrals.') # If we have a 7th column, that is the real psi0 (and the 6th # is just a dummy 0) try: tmp = conv(words[6], float, 'improper equil. value') theteq = tmp except IndexError: pass # Do nothing # Improper types seem not to have the central atom defined in # the first place, so just have the key a fully sorted list. We # still depend on the PSF having properly ordered improper atoms key = tuple(sorted([type1, type2, type3, type4])) self.improper_types[key] = ImproperType(k, theteq) continue if section == 'CMAP': # This is the most complicated part, since cmap parameters span # many lines. We won't do much error catching here. words = line.split() try: holder = [float(w) for w in words] current_cmap_data.extend(holder) except ValueError: # We assume this is a definition of a new CMAP, so # terminate the last CMAP if applicable if current_cmap is not None: # We have a map to terminate ty = CmapType(current_cmap_res, current_cmap_data) self.cmap_types[current_cmap] = ty try: type1 = words[0] type2 = words[1] type3 = words[2] type4 = words[3] type5 = words[4] type6 = words[5] type7 = words[6] type8 = words[7] res = conv(words[8], int, 'CMAP resolution') except IndexError: raise CharmmFileError('Could not parse CMAP data.') # order the torsions independently k1 = [type1, type2, type3, type4] k2 = [type4, type3, type2, type1] key1 = min(k1, k2) k1 = [type5, type6, type7, type8] k2 = [type8, type7, type6, type5] key2 = min(k1, k2) current_cmap = tuple(key1 + key2) current_cmap_res = res current_cmap_data = [] continue if section == 'NONBONDED': # Now get the nonbonded values words = line.split() try: atype = words[0] # 1st column is ignored epsilon = conv(words[2], float, 'vdW epsilon term') rmin = conv(words[3], float, 'vdW Rmin/2 term') except IndexError: # If we haven't read our first nonbonded term yet, we may # just be parsing the settings that should be used. So # soldier on if not read_first_nonbonded: continue raise CharmmFileError('Could not parse nonbonded terms.') except CharmmFileError as e: if not read_first_nonbonded: continue raise CharmmFileError(str(e)) else: # OK, we've read our first nonbonded section for sure now read_first_nonbonded = True # See if we have 1-4 parameters try: # 4th column is ignored eps14 = conv(words[5], float, '1-4 vdW epsilon term') rmin14 = conv(words[6], float, '1-4 vdW Rmin/2 term') except IndexError: eps14 = rmin14 = None nonbonded_types[atype] = [epsilon, rmin, eps14, rmin14] continue if section == 'NBFIX': words = line.split() try: at1 = words[0] at2 = words[1] emin = abs(conv(words[2], float, 'NBFIX Emin')) rmin = conv(words[3], float, 'NBFIX Rmin') try: emin14 = abs(conv(words[4], float, 'NBFIX Emin 1-4')) rmin14 = conv(words[5], float, 'NBFIX Rmin 1-4') except IndexError: emin14 = rmin14 = None try: self.atom_types_str[at1].add_nbfix( at2, rmin, emin, rmin14, emin14) self.atom_types_str[at2].add_nbfix( at1, rmin, emin, rmin14, emin14) except KeyError: # Some stream files define NBFIX terms with an atom that # is defined in another toppar file that does not # necessarily have to be loaded. As a result, not every # NBFIX found here will necessarily need to be applied. # If we can't find a particular atom type, don't bother # adding that nbfix and press on pass except IndexError: raise CharmmFileError('Could not parse NBFIX terms.') self.nbfix_types[(min(at1, at2), max(at1, at2))] = (emin, rmin) continue # Here parse the possible nbthole section if section == 'NBTHOLE': words = line.split() try: at1 = words[0] at2 = words[1] nbt = abs(conv(words[2], float, 'NBTHOLE a')) try: self.atom_types_str[at1].add_nbthole(at2, nbt) self.atom_types_str[at2].add_nbthole(at1, nbt) except KeyError: pass except IndexError: raise CharmmFileError('Could not parse NBTHOLE terms.') self.nbthole_types[(min(at1, at2), max(at1, at2))] = (nbt) # If there were any CMAP terms stored in the parameter set, the last one # defined will not have been added to the set. Add it now. if current_cmap is not None: ty = CmapType(current_cmap_res, current_cmap_data) self.cmap_types[current_cmap] = ty # If in permissive mode create an atomtype for every type used in # the nonbonded parameters. This is a work-around for when all that's # available is a CHARMM22 inp file, which has no ATOM/MASS fields if permissive: try: idx = max(self.atom_types_int.keys()) + 1000 except ValueError: idx = 10000 for key in nonbonded_types: if not key in self.atom_types_str: atype = AtomType(name=key, number=idx, mass=float('NaN'), atomic_number=1) self.atom_types_str[key] = atype self.atom_types_int[idx] = atype idx = idx + 1 # Now we're done. Load the nonbonded types into the relevant AtomType # instances. In order for this to work, all keys in nonbonded_types # must be in the self.atom_types_str dict. Raise a RuntimeError if this # is not satisfied try: for key in nonbonded_types: self.atom_types_str[key].set_lj_params(*nonbonded_types[key]) except KeyError: raise RuntimeError('Atom type %s not present in AtomType list' % key) if parameterset is not None: self.parametersets.append(parameterset) if own_handle: f.close()