def _load_improper_info(self): """ Loads the CHARMM Improper types and array """ del self.impropers[:] del self.improper_types[:] for k, eq in zip(self.parm_data['CHARMM_IMPROPER_FORCE_CONSTANT'], self.parm_data['CHARMM_IMPROPER_PHASE']): # Previous versions of ParmEd stored improper phases as degrees, # whereas it should really be stored in radians. So do a simple # heuristic check to see if a conversion is necessary so we support # all versions. eq = eq * RAD_TO_DEG if abs(eq) <= 2 * pi else eq self.improper_types.append(ImproperType(k, eq, self.improper_types)) it = iter(self.parm_data['CHARMM_IMPROPERS']) for i, j, k, l, m in zip(it, it, it, it, it): self.impropers.append( Improper(self.atoms[i - 1], self.atoms[j - 1], self.atoms[k - 1], self.atoms[l - 1], self.improper_types[m - 1])) # Make sure that if we have a comment in the CHARMM impropers, we fix it # to say the units are in radians for i in range(len(self.parm_comments.get('CHARMM_IMPROPER_PHASE', []))): comment = self.parm_comments['CHARMM_IMPROPER_PHASE'][i] if 'degrees' in comment: self.parm_comments['CHARMM_IMPROPER_PHASE'][i] = \ comment.replace('degrees', 'radians')
def _load_improper_info(self): """ Loads the CHARMM Improper types and array """ del self.impropers[:] del self.improper_types[:] for k, eq in zip(self.parm_data['CHARMM_IMPROPER_FORCE_CONSTANT'], self.parm_data['CHARMM_IMPROPER_PHASE']): self.improper_types.append(ImproperType(k, eq, self.improper_types)) it = iter(self.parm_data['CHARMM_IMPROPERS']) for i, j, k, l, m in zip(it, it, it, it, it): self.impropers.append( Improper(self.atoms[i - 1], self.atoms[j - 1], self.atoms[k - 1], self.atoms[l - 1], self.improper_types[m - 1]))
def _process_improper(struct, force): """ Processes a CustomTorsionForce and looks at the energy expression to see if it's a quadratic improper torsion. Then adds the parameters if applicable Returns ------- is_improper : bool Returns True if the energy expression is recognized as a quadratic improper, and False otherwise """ eqn = force.getEnergyFunction().replace(' ', '') # Don't try to be fancy with regexes for fear of making a possible mistake. # ParmEd and OpenMM use only these two eqns for the improper torsions: # k*(theta-theta0)^2 vs. 0.5*k*(theta-theta0)^2 # So only recognize the above 2 forms if eqn not in ('0.5*k*(theta-theta0)^2', 'k*(theta-theta0)^2'): return False if eqn == '0.5*k*(theta-theta0)^2': fac = 0.5 else: fac = 1 typemap = dict() for ii in range(force.getNumTorsions()): # All formulations put k first and equilibrium angle second, in radians i, j, k, l, (psi_k, psi_eq) = force.getTorsionParameters(ii) ai, aj = struct.atoms[i], struct.atoms[j] ak, al = struct.atoms[k], struct.atoms[l] key = (psi_k, psi_eq) if key in typemap: imp_type = typemap[key] else: imp_type = ImproperType( psi_k * fac * u.kilojoule_per_mole / u.radian**2, psi_eq * u.radian) typemap[key] = imp_type struct.improper_types.append(imp_type) struct.impropers.append(Improper(ai, aj, ak, al, type=imp_type)) struct.improper_types.claim() return True
def create_random_structure(parametrized, novalence=False): """ Create a random Structure with random attributes Parameters ---------- parametrized : bool If True, add at least two of all kinds of parameters to the generated random structure. If False, just fill in the atoms and residues and some random valence terms, but no "types" novalence : bool, optional If True, no valence terms will be added. Default is False. This is set to False if parametrized is True """ from parmed.topologyobjects import ( Atom, Bond, AtomType, BondType, AngleType, DihedralType, ImproperType, CmapType, OutOfPlaneBendType, StretchBendType, TorsionTorsionType, AmoebaNonbondedExceptionType, Angle, UreyBradley, Dihedral, Improper, Cmap, TrigonalAngle, OutOfPlaneBend, StretchBend, PiTorsion, TorsionTorsion, AcceptorDonor, Group, ChiralFrame, MultipoleFrame, NonbondedException, RBTorsionType) from parmed import structure from copy import copy if parametrized: novalence = False # Generate random atom and parameter types atom_types = [ AtomType(''.join(random.sample(uppercase, 3)), i, random.random() * 16 + 1, random.randint(1, 8)) for i in range(random.randint(8, 20)) ] bond_types = [ BondType(random.random() * 2, random.random() * 100) for i in range(random.randint(10, 20)) ] angle_types = [ AngleType(random.random() * 50, random.random() * 120) for i in range(random.randint(10, 20)) ] dihed_types = [ DihedralType(random.random() * 10, random.randint(1, 6), random.choice([0, 180])) for i in range(random.randint(10, 20)) ] rb_types = [RBTorsionType(*[random.random() * 10 for i in range(6)])] imp_types = [ ImproperType(random.random() * 100, random.choice([0, 180])) for i in range(random.randint(10, 20)) ] cmap_types = [ CmapType(24, [random.random() * 5 for i in range(24 * 24)]) for i in range(random.randint(5, 10)) ] oop_types = [ OutOfPlaneBendType(random.random() * 100) for i in range(random.randint(10, 20)) ] strbnd_types = [ StretchBendType(random.random() * 10, random.random() * 10, random.random() * 2, random.random() * 2, random.random() * 120) for i in range(random.randint(10, 20)) ] ang1, ang2 = list(range(-180, 180, 36)), list(range(-180, 180, 18)) tortor_types = [ TorsionTorsionType((10, 20), ang1[:], ang2[:], [random.random() * 10 for j in range(200)]) for i in range(random.randint(5, 10)) ] for typ in atom_types: typ.set_lj_params(random.random() * 2, random.random() * 2) struct = structure.Structure() # Add atoms in residues for res in range(random.randint(20, 30)): resname = ''.join(random.sample(uppercase, 3)) resid = res + 1 for i in range(random.randint(10, 25)): name = ''.join(random.sample(uppercase, 4)) if parametrized: typ = random.choice(atom_types) type = str(typ) mass = typ.mass atomic_number = typ.atomic_number else: type = ''.join(random.sample(uppercase, 3)) mass = random.random() * 16 + 1 atomic_number = random.randint(1, 8) charge = random.random() * 2 - 1 solvent_radius = random.random() * 2 screen = random.random() * 2 atom = Atom(atomic_number=atomic_number, type=type, charge=charge, mass=mass, solvent_radius=solvent_radius, screen=screen, name=name) if parametrized: atom.atom_type = typ struct.add_atom(atom, resname, resid) if novalence: return struct # Possibly add parameter type lists if parametrized: struct.bond_types.extend([copy(x) for x in bond_types]) struct.bond_types.claim() struct.angle_types.extend([copy(x) for x in angle_types]) struct.angle_types.claim() struct.dihedral_types.extend([copy(x) for x in dihed_types]) struct.dihedral_types.claim() struct.rb_torsion_types.extend([copy(x) for x in rb_types]) struct.rb_torsion_types.claim() struct.urey_bradley_types.extend([copy(x) for x in bond_types]) struct.urey_bradley_types.claim() struct.improper_types.extend([copy(x) for x in imp_types]) struct.improper_types.claim() struct.cmap_types.extend([copy(x) for x in cmap_types]) struct.cmap_types.claim() struct.trigonal_angle_types.extend([copy(x) for x in angle_types]) struct.trigonal_angle_types.claim() struct.out_of_plane_bend_types.extend([copy(x) for x in oop_types]) struct.out_of_plane_bend_types.claim() struct.pi_torsion_types.extend([copy(x) for x in dihed_types]) struct.pi_torsion_types.claim() struct.stretch_bend_types.extend([copy(x) for x in strbnd_types]) struct.stretch_bend_types.claim() struct.torsion_torsion_types.extend([copy(x) for x in tortor_types]) struct.torsion_torsion_types.claim() struct.adjust_types.extend([ AmoebaNonbondedExceptionType(0.5, 0.5, 0.6, 0.6, 0.7) for i in range(random.randint(10, 20)) ]) struct.adjust_types.claim() # Add valence terms with optional for i in range(random.randint(40, 50)): struct.bonds.append(Bond(*random.sample(struct.atoms, 2))) if parametrized: struct.bonds[-1].type = random.choice(struct.bond_types) for i in range(random.randint(35, 45)): struct.angles.append(Angle(*random.sample(struct.atoms, 3))) if parametrized: struct.angles[-1].type = random.choice(struct.angle_types) for i in range(random.randint(35, 45)): struct.urey_bradleys.append( UreyBradley(*random.sample(struct.atoms, 2))) if parametrized: struct.urey_bradleys[-1].type = random.choice( struct.urey_bradley_types) for i in range(random.randint(30, 40)): struct.dihedrals.append( Dihedral(*random.sample(struct.atoms, 4), improper=random.choice([True, False]))) if parametrized: struct.dihedrals[-1].type = random.choice(struct.dihedral_types) for i in range(random.randint(30, 40)): struct.rb_torsions.append(Dihedral(*random.sample(struct.atoms, 4))) if parametrized: struct.rb_torsions[-1].type = random.choice( struct.rb_torsion_types) for i in range(random.randint(10, 20)): struct.impropers.append(Improper(*random.sample(struct.atoms, 4))) if parametrized: struct.impropers[-1].type = random.choice(struct.improper_types) for i in range(random.randint(25, 35)): struct.cmaps.append(Cmap(*random.sample(struct.atoms, 5))) if parametrized: struct.cmaps[-1].type = random.choice(struct.cmap_types) for i in range(random.randint(30, 40)): struct.trigonal_angles.append( TrigonalAngle(*random.sample(struct.atoms, 4))) if parametrized: struct.trigonal_angles[-1].type = random.choice( struct.trigonal_angle_types) for i in range(random.randint(30, 40)): struct.out_of_plane_bends.append( OutOfPlaneBend(*random.sample(struct.atoms, 4))) if parametrized: struct.out_of_plane_bends[-1].type = random.choice( struct.out_of_plane_bend_types) for i in range(random.randint(30, 40)): struct.stretch_bends.append( StretchBend(*random.sample(struct.atoms, 3))) if parametrized: struct.stretch_bends[-1].type = random.choice( struct.stretch_bend_types) for i in range(random.randint(20, 30)): struct.pi_torsions.append(PiTorsion(*random.sample(struct.atoms, 6))) if parametrized: struct.pi_torsions[-1].type = random.choice( struct.pi_torsion_types) for i in range(random.randint(10, 20)): struct.torsion_torsions.append( TorsionTorsion(*random.sample(struct.atoms, 5))) if parametrized: struct.torsion_torsions[-1].type = random.choice( struct.torsion_torsion_types) # Now use some lesser-used features for i in range(random.randint(5, 10)): struct.acceptors.append(AcceptorDonor(*random.sample(struct.atoms, 2))) struct.donors.append(AcceptorDonor(*random.sample(struct.atoms, 2))) struct.groups.append(Group(random.choice(struct.atoms), 2, 0)) struct.chiral_frames.append( ChiralFrame(*random.sample(struct.atoms, 2), chirality=random.choice([-1, 1]))) struct.multipole_frames.append( MultipoleFrame(random.choice(struct.atoms), 0, 1, 2, 3)) for i in range(random.randint(20, 30)): struct.adjusts.append( NonbondedException(*random.sample(struct.atoms, 2))) if parametrized: struct.adjusts[-1].type = random.choice(struct.adjust_types) struct.prune_empty_terms() struct.unchange() struct.update_dihedral_exclusions() return struct
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