def _process_dihedral(struct, force): """ Adds periodic torsions to the structure """ typemap = dict() for ii in range(force.getNumTorsions()): i, j, k, l, per, phase, phi_k = force.getTorsionParameters(ii) ai, aj = struct.atoms[i], struct.atoms[j] ak, al = struct.atoms[k], struct.atoms[l] key = (per, phase._value, phi_k._value) if key in typemap: dihed_type = typemap[key] else: dihed_type = DihedralType(phi_k, per, phase) typemap[key] = dihed_type struct.dihedral_types.append(dihed_type) improper = (ai in ak.bond_partners and aj in ak.bond_partners and al in ak.bond_partners) struct.dihedrals.append( Dihedral(ai, aj, ak, al, improper=improper, type=dihed_type)) struct.dihedral_types.claim()
def _process_rbtorsion(struct, force): """ Adds Ryckaert-Bellemans torsions to the structure """ typemap = dict() for ii in range(force.getNumTorsions()): i, j, k, l, c0, c1, c2, c3, c4, c5 = force.getTorsionParameters(ii) ai, aj = struct.atoms[i], struct.atoms[j] ak, al = struct.atoms[k], struct.atoms[l] # TODO -- Fix this when OpenMM is fixed try: key = (c0._value, c1._value, c2._value, c3._value, c4._value, c5._value) f = 1 # pragma: no cover except AttributeError: # pragma: no cover key = (c0, c1, c2, c3, c4, c5) # pragma: no cover f = u.kilojoules_per_mole # pragma: no cover if key in typemap: dihed_type = typemap[key] else: dihed_type = RBTorsionType(c0 * f, c1 * f, c2 * f, c3 * f, c4 * f, c5 * f) typemap[key] = dihed_type struct.rb_torsion_types.append(dihed_type) struct.rb_torsions.append(Dihedral(ai, aj, ak, al, type=dihed_type)) struct.rb_torsion_types.claim()
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 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 __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 load_parameters(self, parmset): """ Loads parameters from a parameter set that was loaded via CHARMM RTF, PAR, and STR files. Parameters ---------- parmset : :class:`CharmmParameterSet` List of all parameters 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 """ 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])) # 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] 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