Пример #1
0
 def __init__(self, fname, seq=None):
     super(XyzFile, self).__init__()
     if isinstance(fname, string_types):
         fxyz = genopen(fname, 'r')
         own_handle_xyz = True
     else:
         fxyz = fname
         own_handle_xyz = False
     if seq is not None:
         seqstruct = load_file(seq)
     # Now parse the file
     try:
         natom = int(fxyz.readline().split()[0])
     except (ValueError, IndexError):
         raise TinkerError('Bad XYZ file format; first line')
     if seq is not None and natom != len(seqstruct.atoms):
         raise ValueError('Sequence file %s # of atoms does not match the # '
                          'of atoms in the XYZ file' % seq)
     words = fxyz.readline().split()
     if len(words) == 6 and not XyzFile._check_atom_record(words):
         self.box = [float(w) for w in words]
         words = fxyz.readline().split()
     atom = Atom(atomic_number=AtomicNum[element_by_name(words[1])],
                 name=words[1], type=words[5])
     atom.xx, atom.xy, atom.xz = [float(w) for w in words[2:5]]
     residue = Residue('SYS')
     residue.number = 1
     residue._idx = 0
     if seq is not None:
         residue = seqstruct.residues[0]
     self.add_atom(atom, residue.name, residue.number, residue.chain,
                   residue.insertion_code, residue.segid)
     bond_ids = [[int(w) for w in words[6:]]]
     for i, line in enumerate(fxyz):
         words = line.split()
         atom = Atom(atomic_number=AtomicNum[element_by_name(words[1])],
                     name=words[1], type=words[5])
         atom.xx, atom.xy, atom.xz = [float(w) for w in words[2:5]]
         if seq is not None:
             residue = seqstruct.atoms[i+1].residue
         self.add_atom(atom, residue.name, residue.number, residue.chain,
                       residue.insertion_code, residue.segid)
         bond_ids.append([int(w) for w in words[6:]])
     # All of the bonds are stored now -- go ahead and make them now
     for atom, bonds in zip(self.atoms, bond_ids):
         i = atom.idx + 1
         for idx in bonds:
             if idx > i:
                 self.bonds.append(Bond(atom, self.atoms[idx-1]))
     if own_handle_xyz:
         fxyz.close()
Пример #2
0
 def parse(cls, filename, structure=False):
     self = cls(filename)
     if structure:
         obj = Structure()
         for _ in range(self.natom):
             # fake
             obj.add_atom(Atom(), resname='XXX', resnum=0)
         obj.box = self.box
         obj.coordinates = self.coordinates[0]
         obj._coordinates = self.coordinates
         return obj
     else:
         return self
Пример #3
0
 def __init__(self, fname, seq=None):
     super(XyzFile, self).__init__()
     if isinstance(fname, string_types):
         fxyz = genopen(fname, 'r')
         own_handle_xyz = True
     else:
         fxyz = fname
         own_handle_xyz = False
     if seq is not None:
         seqstruct = load_file(seq)
     # Now parse the file
     try:
         natom = int(fxyz.readline().split()[0])
     except (ValueError, IndexError):
         raise TinkerError('Bad XYZ file format; first line')
     if seq is not None and natom != len(seqstruct.atoms):
         raise ValueError(
             'Sequence file %s # of atoms does not match the # '
             'of atoms in the XYZ file' % seq)
     words = fxyz.readline().split()
     if len(words) == 6 and not XyzFile._check_atom_record(words):
         self.box = [float(w) for w in words]
         words = fxyz.readline().split()
     atom = Atom(atomic_number=AtomicNum[element_by_name(words[1])],
                 name=words[1],
                 type=words[5])
     atom.xx, atom.xy, atom.xz = [float(w) for w in words[2:5]]
     residue = Residue('SYS')
     residue.number = 1
     residue._idx = 0
     if seq is not None:
         residue = seqstruct.residues[0]
     self.add_atom(atom, residue.name, residue.number, residue.chain,
                   residue.insertion_code, residue.segid)
     bond_ids = [[int(w) for w in words[6:]]]
     for i, line in enumerate(fxyz):
         words = line.split()
         atom = Atom(atomic_number=AtomicNum[element_by_name(words[1])],
                     name=words[1],
                     type=words[5])
         atom.xx, atom.xy, atom.xz = [float(w) for w in words[2:5]]
         if seq is not None:
             residue = seqstruct.atoms[i + 1].residue
         self.add_atom(atom, residue.name, residue.number, residue.chain,
                       residue.insertion_code, residue.segid)
         bond_ids.append([int(w) for w in words[6:]])
     # All of the bonds are stored now -- go ahead and make them now
     for atom, bonds in zip(self.atoms, bond_ids):
         i = atom.idx + 1
         for idx in bonds:
             if idx > i:
                 self.bonds.append(Bond(atom, self.atoms[idx - 1]))
     if own_handle_xyz:
         fxyz.close()
Пример #4
0
    def read(self, line):
        """ Reads a line

        Parameters
        ----------
        line : str
            A line with an atom record from a GRO file

        Returns
        -------
        atom, resname, resnum : Atom, str, int
            The Atom instance, residue name, and residue number containing the
            atom
        """
        resnum = int(line[:5])
        resname = line[5:10].strip()
        atomname = line[10:15].strip()
        elem = element_by_name(atomname)
        atomic_number = AtomicNum[elem]
        mass = Mass[elem]
        atnum = int(line[15:20])
        if atomic_number == 0:
            atom = ExtraPoint(name=atomname, number=atnum)
        else:
            atom = Atom(atomic_number=atomic_number, name=atomname,
                        number=atnum, mass=mass)
        if self._digits is None:
            self._pdeci = line.index('.', 20)
            self._ndeci = line.index('.', self._pdeci+1)
            self._digits = self._ndeci - self._pdeci
        atom.xx, atom.xy, atom.xz = (
                float(line[20+i*self._digits:20+(i+1)*self._digits])*10
                    for i in range(3)
        )
        wbeg = 20 + self._digits * 3
        wend = wbeg + self._digits
        if line[wbeg:wend].strip():
            atom.vx, atom.vy, atom.vz = (
                    float(line[wbeg+i*self._digits:wend+i*self._digits])*10
                    for i in range(3)
            )
        return atom, resname, resnum
Пример #5
0
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
Пример #6
0
 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
Пример #7
0
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
Пример #8
0
 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
Пример #9
0
    def load(pose):
        """
        Load a :class:`Pose` object and return a populated :class:`Structure`
        instance

        Parameters
        ----------
        pose : :class:`Pose`
            PyRosetta :class:`Pose` object to convert
        """
        if not Pose or not AtomID:
            raise ImportError('Could not load the PyRosetta module.')
        if not isinstance(pose, Pose):
            raise TypeError('Object is not a PyRosetta Pose object.')

        struct = Structure()

        atnum = 1
        conf = pose.conformation()
        for resid in range(1, pose.total_residue() + 1):
            res = pose.residue(resid)
            resname = res.name3().strip()
            chain = chr(res.chain() + ord('A') - 1)
            for atno, at in enumerate(res.atoms(), start=1):
                try:
                    atinfo = res.atom_type(atno)
                    atname = res.atom_name(atno).strip()
                    if atinfo.is_virtual():
                        atsym = 'EP'
                    else:
                        atsym = atinfo.element()
                    rmin = atinfo.lj_radius()
                    epsilon = atinfo.lj_wdepth()
                    atomic_number = AtomicNum[atsym]
                    mass = Mass[atsym]
                except KeyError:
                    raise RosettaError('Could not recognize element: %s.' %
                                       atsym)

                params = dict(atomic_number=atomic_number,
                              name=atname,
                              charge=0.0,
                              mass=mass,
                              occupancy=0.0,
                              bfactor=0.0,
                              altloc='',
                              number=atnum,
                              rmin=rmin,
                              epsilon=epsilon)

                if atinfo.is_virtual():
                    atom = ExtraPoint(**params)
                else:
                    atom = Atom(**params)

                atom.xx, atom.xy, atom.xz = tuple(at.xyz())

                struct.add_atom(atom, resname, resid, chain, '')
                atnum += 1
                try:
                    for nbr in conf.bonded_neighbor_all_res(AtomID(
                            atno, resid)):
                        if nbr.rsd() < resid or (nbr.rsd() == resid
                                                 and nbr.atomno() < atno):
                            struct.bonds.append(
                                Bond(struct.atoms[_n_prior(pose, nbr)], atom))
                except:
                    raise RosettaError('Could not add bonds.')

        struct.unchange()
        return struct
Пример #10
0
    def parse(filename, structure=False):
        """ Parses a mol2 file (or mol3) file

        Parameters
        ----------
        filename : str or file-like
            Name of the file to parse or file-like object to parse from
        structure : bool, optional
            If True, the return value is a :class:`Structure` instance. If
            False, it is either a :class:`ResidueTemplate` or
            :class:`ResidueTemplateContainter` instance, depending on whether
            there is one or more than one residue defined in it. Default is
            False

        Returns
        -------
        molecule : :class:`Structure`, :class:`ResidueTemplate`, or
                   :class:`ResidueTemplateContainer`
            The molecule defined by this mol2 file

        Raises
        ------
        Mol2Error
            If the file format is not recognized or non-numeric values are
            present where integers or floating point numbers are expected. Also
            raises Mol2Error if you try to parse a mol2 file that has multiple
            @<MOLECULE> entries with ``structure=True``.
        """
        if isinstance(filename, string_types):
            f = genopen(filename, 'r')
            own_handle = True
        else:
            f = filename
            own_handle = False
        rescont = ResidueTemplateContainer()
        struct = Structure()
        restemp = ResidueTemplate()
        mol_info = []
        multires_structure = False
        try:
            section = None
            last_residue = None
            headtail = 'head'
            molecule_number = 0
            for line in f:
                if line.startswith('#'): continue
                if not line.strip() and section is None: continue
                if line.startswith('@<TRIPOS>'):
                    section = line[9:].strip()
                    if section == 'MOLECULE' and (restemp.atoms or rescont):
                        if structure:
                            raise Mol2Error('Cannot convert MOL2 with multiple '
                                            '@<MOLECULE>s to a Structure')
                        # Set the residue name from the MOL2 title if the
                        # molecule had only 1 residue and it was given a name in
                        # the title
                        if not multires_structure and mol_info[0]:
                            restemp.name = mol_info[0]
                        multires_structure = False
                        rescont.append(restemp)
                        restemp = ResidueTemplate()
                        struct = Structure()
                        last_residue = None
                        molecule_number += 1
                        mol_info = []
                    continue
                if section is None:
                    raise Mol2Error('Bad mol2 file format')
                if section == 'MOLECULE':
                    # Section formatted as follows:
                    #   mol_name
                    #   num_atoms [num_bonds [num_substr [num_feat [num_sets]]]]
                    #   mol_type
                    #   charge_type
                    #   [status_bits]
                    #   [mol_comment]
                    # TODO: Do something with the name.
                    if len(mol_info) == 0:
                        mol_info.append(line.strip())
                    elif len(mol_info) == 1:
                        mol_info.append([int(x) for x in line.split()])
                    elif len(mol_info) == 2:
                        mol_info.append(line.strip())
                    elif len(mol_info) == 3:
                        mol_info.append(line.strip())
                    # Ignore the rest
                    continue
                if section == 'ATOM':
                    # Section formatted as follows:
                    #   atom_id -- serial number of atom
                    #   atom_name -- name of the atom
                    #   x -- X-coordinate of the atom
                    #   y -- Y-coordinate of the atom
                    #   z -- Z-coordinate of the atom
                    #   atom_type -- type of the atom
                    #   subst_id -- Residue serial number
                    #   subst_name -- Residue name
                    #   charge -- partial atomic charge
                    #   status_bit -- ignored
                    words = line.split()
                    id = int(words[0])
                    name = words[1]
                    x = float(words[2])
                    y = float(words[3])
                    z = float(words[4])
                    typ = words[5]
                    try:
                        resid = int(words[6])
                    except IndexError:
                        resid = 0
                    try:
                        resname = words[7]
                    except IndexError:
                        resname = 'UNK'
                    if 'NO_CHARGES' not in mol_info:
                        try:
                            charge = float(words[8])
                        except IndexError:
                            charge = 0
                    else:
                        charge = 0
                    if last_residue is None:
                        last_residue = (resid, resname)
                        restemp.name = resname
                    atom = Atom(name=name, type=typ, number=id, charge=charge)
                    atom.xx, atom.xy, atom.xz = x, y, z
                    struct.add_atom(atom, resname, resid)
                    if last_residue != (resid, resname):
                        rescont.append(restemp)
                        restemp = ResidueTemplate()
                        restemp.name = resname
                        last_residue = (resid, resname)
                        multires_structure = True
                    restemp.add_atom(copy.copy(atom))
                    continue
                if section == 'BOND':
                    # Section formatted as follows:
                    #   bond_id -- serial number of bond (ignored)
                    #   origin_atom_id -- serial number of first atom in bond
                    #   target_atom_id -- serial number of other atom in bond
                    #   bond_type -- string describing bond type (ignored)
                    #   status_bits -- ignored
                    words = line.split()
                    int(words[0]) # Bond serial number... redundant and ignored
                    a1 = int(words[1])
                    a2 = int(words[2])
                    atom1 = struct.atoms.find_original_index(a1)
                    atom2 = struct.atoms.find_original_index(a2)
                    struct.bonds.append(Bond(atom1, atom2))
                    # Now add it to our residue container
                    # See if it's a head/tail connection
                    if atom1.residue is not atom2.residue:
                        if atom1.residue.idx == len(rescont):
                            res1 = restemp
                        elif atom1.residue.idx < len(rescont):
                            res1 = rescont[atom1.residue.idx]
                        else:
                            raise Mol2Error('Bad bonding pattern detected')
                        if atom2.residue.idx == len(rescont):
                            res2 = restemp
                        elif atom1.residue.idx < len(rescont):
                            res2 = rescont[atom2.residue.idx]
                        else:
                            raise Mol2Error('Bad bonding pattern detected')
                        assert res1 is not res2, 'BAD identical residues'
                        idx1 = atom1.idx - atom1.residue[0].idx
                        idx2 = atom2.idx - atom2.residue[0].idx
                        if atom1.residue.idx < atom2.residue.idx:
                            res1.tail = res1[idx1]
                            res2.head = res2[idx2]
                        else:
                            res1.head = res1[idx1]
                            res2.tail = res2[idx2]
                    elif not multires_structure:
                        restemp.add_bond(a1-1, a2-1)
                    else:
                        # Same residue, add the bond
                        offset = atom1.residue[0].idx
                        if atom1.residue.idx == len(rescont):
                            res = restemp
                        else:
                            res = rescont[atom1.residue.idx]
                        res.add_bond(atom1.idx-offset, atom2.idx-offset)
                    continue
                if section == 'CRYSIN':
                    # Section formatted as follows:
                    #   a -- length of first unit cell vector
                    #   b -- length of second unit cell vector
                    #   c -- length of third unit cell vector
                    #   alpha -- angle b/w b and c
                    #   beta -- angle b/w a and c
                    #   gamma -- angle b/w a and b
                    #   space group -- number of space group (ignored)
                    #   space group setting -- ignored
                    words = line.split()
                    box = [float(x) for x in words[:6]]
                    if len(box) != 6:
                        raise ValueError('%d box dimensions found; needed 6' %
                                         len(box))
                    struct.box = copy.copy(box)
                    rescont.box = copy.copy(box)
                    continue
                if section == 'SUBSTRUCTURE':
                    # Section formatted as follows:
                    #   subst_id -- residue number
                    #   subst_name -- residue name
                    #   root_atom -- first atom of residue
                    #   subst_type -- ignored (usually 'RESIDUE')
                    #   dict_type -- type of substructure (ignored)
                    #   chain -- chain ID of residue
                    #   sub_type -- type of the chain
                    #   inter_bonds -- # of inter-substructure bonds
                    #   status -- ignored
                    #   comment -- ignored
                    words = line.split()
                    if not words: continue
                    id = int(words[0])
                    resname = words[1]
                    try:
                        chain = words[5]
                    except IndexError:
                        chain = ''
                    # Set the chain ID
                    for res in struct.residues:
                        if res.number == id and res.name == resname:
                            res.chain = chain
                    continue
                # MOL3 sections
                if section == 'HEADTAIL':
                    atname, residx = line.split()
                    residx = int(residx)
                    if residx in (0, 1) or residx - 1 == len(rescont):
                        res = restemp
                    elif residx - 1 < len(rescont):
                        res = rescont[residx-1]
                    else:
                        raise Mol2Error('Residue out of range in head/tail')
                    for atom in res:
                        if atom.name == atname:
                            if headtail == 'head':
                                res.head = atom
                                headtail = 'tail'
                            else:
                                res.tail = atom
                                headtail = 'head'
                            break
                    else:
                        if headtail == 'head':
                            headtail = 'tail'
                        else:
                            headtail = 'head'
                    continue
                if section == 'RESIDUECONNECT':
                    words = line.split()
                    residx = int(words[0])
                    if residx - 1 == len(rescont):
                        res = restemp
                    elif residx - 1 < len(rescont):
                        res = rescont[residx-1]
                    else:
                        raise Mol2Error('Residue out of range in '
                                        'residueconnect')
                    for a in words[3:]:
                        if a == '0': continue
                        for atom in res:
                            if atom.name == a:
                                atom.connections.append(atom)
                                break
                        else:
                            raise Mol2Error('Residue connection atom %s not '
                                            'found in residue %d' % (a, residx))
            if structure:
                return struct
            elif len(rescont) > 0:
                if not multires_structure and mol_info[0]:
                    restemp.name = mol_info[0]
                rescont.append(restemp)
                return rescont
            else:
                return restemp
        except ValueError as e:
            raise Mol2Error('String conversion trouble: %s' % e)
        finally:
            if own_handle: f.close()
Пример #11
0
    def parse(filename, structure=False):
        """ Parses a mol2 file (or mol3) file

        Parameters
        ----------
        filename : str or file-like
            Name of the file to parse or file-like object to parse from
        structure : bool, optional
            If True, the return value is a :class:`Structure` instance. If
            False, it is either a :class:`ResidueTemplate` or
            :class:`ResidueTemplateContainter` instance, depending on whether
            there is one or more than one residue defined in it. Default is
            False

        Returns
        -------
        molecule : :class:`Structure`, :class:`ResidueTemplate`, or
                   :class:`ResidueTemplateContainer`
            The molecule defined by this mol2 file

        Raises
        ------
        Mol2Error
            If the file format is not recognized or non-numeric values are
            present where integers or floating point numbers are expected. Also
            raises Mol2Error if you try to parse a mol2 file that has multiple
            @<MOLECULE> entries with ``structure=True``.
        """
        if isinstance(filename, string_types):
            f = genopen(filename, 'r')
            own_handle = True
        else:
            f = filename
            own_handle = False
        rescont = ResidueTemplateContainer()
        struct = Structure()
        restemp = ResidueTemplate()
        mol_info = []
        multires_structure = False
        try:
            section = None
            last_residue = None
            headtail = 'head'
            molecule_number = 0
            for line in f:
                if line.startswith('#'): continue
                if not line.strip() and section is None: continue
                if line.startswith('@<TRIPOS>'):
                    section = line[9:].strip()
                    if section == 'MOLECULE' and (restemp.atoms or rescont):
                        if structure:
                            raise Mol2Error('Cannot convert MOL2 with multiple '
                                            '@<MOLECULE>s to a Structure')
                        # Set the residue name from the MOL2 title if the
                        # molecule had only 1 residue and it was given a name in
                        # the title
                        if not multires_structure and mol_info[0]:
                            restemp.name = mol_info[0]
                        multires_structure = False
                        rescont.append(restemp)
                        restemp = ResidueTemplate()
                        struct = Structure()
                        last_residue = None
                        molecule_number += 1
                        mol_info = []
                    continue
                if section is None:
                    raise Mol2Error('Bad mol2 file format')
                if section == 'MOLECULE':
                    # Section formatted as follows:
                    #   mol_name
                    #   num_atoms [num_bonds [num_substr [num_feat [num_sets]]]]
                    #   mol_type
                    #   charge_type
                    #   [status_bits]
                    #   [mol_comment]
                    # TODO: Do something with the name.
                    if len(mol_info) == 0:
                        mol_info.append(line.strip())
                    elif len(mol_info) == 1:
                        mol_info.append([int(x) for x in line.split()])
                    elif len(mol_info) == 2:
                        mol_info.append(line.strip())
                    elif len(mol_info) == 3:
                        mol_info.append(line.strip())
                    # Ignore the rest
                    continue
                if section == 'ATOM':
                    # Section formatted as follows:
                    #   atom_id -- serial number of atom
                    #   atom_name -- name of the atom
                    #   x -- X-coordinate of the atom
                    #   y -- Y-coordinate of the atom
                    #   z -- Z-coordinate of the atom
                    #   atom_type -- type of the atom
                    #   subst_id -- Residue serial number
                    #   subst_name -- Residue name
                    #   charge -- partial atomic charge
                    #   status_bit -- ignored
                    words = line.split()
                    id = int(words[0])
                    name = words[1]
                    x = float(words[2])
                    y = float(words[3])
                    z = float(words[4])
                    typ = words[5]
                    try:
                        resid = int(words[6])
                    except IndexError:
                        resid = 0
                    try:
                        resname = words[7]
                    except IndexError:
                        resname = 'UNK'
                    if 'NO_CHARGES' not in mol_info:
                        try:
                            charge = float(words[8])
                        except IndexError:
                            charge = 0
                    else:
                        charge = 0
                    if last_residue is None:
                        last_residue = (resid, resname)
                        restemp.name = resname
                    atom = Atom(name=name, type=typ, number=id, charge=charge)
                    atom.xx, atom.xy, atom.xz = x, y, z
                    struct.add_atom(atom, resname, resid)
                    if last_residue != (resid, resname):
                        rescont.append(restemp)
                        restemp = ResidueTemplate()
                        restemp.name = resname
                        last_residue = (resid, resname)
                        multires_structure = True
                    try:
                        restemp.add_atom(copy.copy(atom))
                    except ValueError:
                        # Allow mol2 files being parsed as a Structure to have
                        # duplicate atom names
                        if not structure:
                            raise
                    continue
                if section == 'BOND':
                    # Section formatted as follows:
                    #   bond_id -- serial number of bond (ignored)
                    #   origin_atom_id -- serial number of first atom in bond
                    #   target_atom_id -- serial number of other atom in bond
                    #   bond_type -- string describing bond type (ignored)
                    #   status_bits -- ignored
                    words = line.split()
                    int(words[0]) # Bond serial number... redundant and ignored
                    a1 = int(words[1])
                    a2 = int(words[2])
                    atom1 = struct.atoms.find_original_index(a1)
                    atom2 = struct.atoms.find_original_index(a2)
                    struct.bonds.append(Bond(atom1, atom2))
                    # Now add it to our residue container
                    # See if it's a head/tail connection
                    if atom1.residue is not atom2.residue:
                        if atom1.residue.idx == len(rescont):
                            res1 = restemp
                        elif atom1.residue.idx < len(rescont):
                            res1 = rescont[atom1.residue.idx]
                        assert atom.residue.idx <= len(rescont), 'Bad bond!'
                        if atom2.residue.idx == len(rescont):
                            res2 = restemp
                        elif atom2.residue.idx < len(rescont):
                            res2 = rescont[atom2.residue.idx]
                        assert atom.residue.idx <= len(rescont), 'Bad bond!'
                        assert res1 is not res2, 'BAD identical residues'
                        idx1 = atom1.idx - atom1.residue[0].idx
                        idx2 = atom2.idx - atom2.residue[0].idx
                        if atom1.residue.idx < atom2.residue.idx:
                            res1.tail = res1[idx1]
                            res2.head = res2[idx2]
                        else:
                            res1.head = res1[idx1]
                            res2.tail = res2[idx2]
                    elif not multires_structure:
                        if not structure:
                            restemp.add_bond(a1-1, a2-1)
                    else:
                        # Same residue, add the bond
                        offset = atom1.residue[0].idx
                        if atom1.residue.idx == len(rescont):
                            res = restemp
                        else:
                            res = rescont[atom1.residue.idx]
                        res.add_bond(atom1.idx-offset, atom2.idx-offset)
                    continue
                if section == 'CRYSIN':
                    # Section formatted as follows:
                    #   a -- length of first unit cell vector
                    #   b -- length of second unit cell vector
                    #   c -- length of third unit cell vector
                    #   alpha -- angle b/w b and c
                    #   beta -- angle b/w a and c
                    #   gamma -- angle b/w a and b
                    #   space group -- number of space group (ignored)
                    #   space group setting -- ignored
                    words = line.split()
                    box = [float(w) for w in words[:6]]
                    if len(box) != 6:
                        raise ValueError('%d box dimensions found; needed 6' %
                                         len(box))
                    struct.box = copy.copy(box)
                    rescont.box = copy.copy(box)
                    continue
                if section == 'SUBSTRUCTURE':
                    # Section formatted as follows:
                    #   subst_id -- residue number
                    #   subst_name -- residue name
                    #   root_atom -- first atom of residue
                    #   subst_type -- ignored (usually 'RESIDUE')
                    #   dict_type -- type of substructure (ignored)
                    #   chain -- chain ID of residue
                    #   sub_type -- type of the chain
                    #   inter_bonds -- # of inter-substructure bonds
                    #   status -- ignored
                    #   comment -- ignored
                    words = line.split()
                    if not words: continue
                    id = int(words[0])
                    resname = words[1]
                    try:
                        chain = words[5]
                    except IndexError:
                        chain = ''
                    # Set the chain ID
                    for res in struct.residues:
                        if res.number == id and res.name == resname:
                            res.chain = chain
                    continue
                # MOL3 sections
                if section == 'HEADTAIL':
                    atname, residx = line.split()
                    residx = int(residx)
                    if residx in (0, 1) or residx - 1 == len(rescont):
                        res = restemp
                    elif residx - 1 < len(rescont):
                        res = rescont[residx-1]
                    else:
                        raise Mol2Error('Residue out of range in head/tail')
                    for atom in res:
                        if atom.name == atname:
                            if headtail == 'head':
                                res.head = atom
                                headtail = 'tail'
                            else:
                                res.tail = atom
                                headtail = 'head'
                            break
                    else:
                        if headtail == 'head':
                            headtail = 'tail'
                        else:
                            headtail = 'head'
                    continue
                if section == 'RESIDUECONNECT':
                    words = line.split()
                    residx = int(words[0])
                    if residx - 1 == len(rescont):
                        res = restemp
                    elif residx - 1 < len(rescont):
                        res = rescont[residx-1]
                    else:
                        raise Mol2Error('Residue out of range in '
                                        'residueconnect')
                    for a in words[3:]:
                        if a == '0': continue
                        for atom in res:
                            if atom.name == a:
                                res.connections.append(atom)
                                break
                        else:
                            raise Mol2Error('Residue connection atom %s not '
                                            'found in residue %d' % (a, residx))
            if structure:
                return struct
            elif len(rescont) > 0:
                if not multires_structure and mol_info[0]:
                    restemp.name = mol_info[0]
                rescont.append(restemp)
                return rescont
            else:
                return restemp
        except ValueError as e:
            raise Mol2Error('String conversion trouble: %s' % e)
        finally:
            if own_handle: f.close()
Пример #12
0
    def apply_patch(self, patch, precision=4):
        """
        Apply the specified PatchTemplate to the ResidueTemplate.

        This only handles patches that affect a single residue.

        An exception is thrown if patch is incompatible because
        * The patch specifies that an atom is to be deleted that doesn't exist in the residue
        * A bond specified as being added in the patch does not have both atom names present after adding/deleting atoms from the patch
        * The new net charge is not integral to the specified precision
        * The residue is not modified in any way (no atoms or bonds added/changed/deleted)

        Parameters
        ----------

        patch : PatchTemplate
            The patch to apply to this residue

        precision : int, optional
            Each valid patch should be produce a net charge that is integral to
            this many decimal places.
            Default is 4

        Returns
        -------

        residue : ResidueTemplate
            A new ResidueTemplate corresponding to the patched residue is returned.
            The original remains unmodified.

        """
        # Create a copy
        # TODO: Once ResidueTemplate.from_residue() actually copies all info, use that instead?
        residue = _copy.copy(self)
        # Record whether we've actually modified the residue.
        modifications_made = False
        # Delete atoms
        for atom_name in patch.delete_atoms:
            try:
                residue.delete_atom(atom_name)
                modifications_made = True
            except (KeyError, MoleculeError) as e:
                if atom_name.startswith('D') and atom_name[
                        1:] in self and atom_name[1:] in patch.delete_atoms:
                    # This is a Drude particle.  We're also deleting its parent atom, so don't report an error.
                    pass
                else:
                    raise IncompatiblePatchError(
                        'Atom %s could not be deleted from the patched residue: atoms are %s (exception: %s)'
                        % (atom_name, list(residue._map.keys()), str(e)))
        # Add or replace atoms
        for atom in patch.atoms:
            if atom.name in residue:
                # Overwrite type and charge
                residue[atom.name].type = atom.type
                residue[atom.name].charge = atom.charge
            else:
                residue.add_atom(
                    Atom(name=atom.name, type=atom.type, charge=atom.charge))
            modifications_made = True
        # Add bonds
        for (atom1_name, atom2_name, order) in patch.add_bonds:
            try:
                # Remove dangling bonds
                for name in [atom1_name, atom2_name]:
                    if residue.head and (name == residue.head.name):
                        residue.head = None
                    if residue.tail and (name == residue.tail.name):
                        residue.tail = None
                # Add bond
                residue.add_bond(atom1_name, atom2_name, order)
                modifications_made = True
            except (IndexError, MoleculeError) as e:
                raise IncompatiblePatchError(
                    'Bond %s-%s could not be added to patched residue: atoms are %s (exception: %s)'
                    % (atom1_name, atom2_name, list(
                        residue._map.keys()), str(e)))
        # Delete impropers
        for impr in patch.delete_impropers:
            try:
                residue._impr.remove(impr)
                # removal of impropers doesn't do anything as far as OpenMM is concerned, so don't note this as a modification having been made
            except ValueError as e:
                raise IncompatiblePatchError(
                    'Improper %s was not found in residue to be patched.' %
                    impr)
        # Check that the net charge is integral.
        net_charge = residue.net_charge
        is_integral = (round(net_charge, precision) - round(net_charge)) == 0.0
        if not is_integral:
            raise IncompatiblePatchError(
                'Patch is not compatible with residue due to non-integral charge (charge was %f).'
                % net_charge)
        # Ensure residue is connected
        import networkx as nx
        G = residue.to_networkx(False)
        if not nx.is_connected(G):
            components = [c for c in nx.connected_components(G)]
            raise IncompatiblePatchError(
                'Patched residue bond graph is not a connected graph: %s' %
                str(components))
        # Make sure the patch has actually modified the residue
        if not modifications_made:
            raise IncompatiblePatchError('Patch did not modify residue.')

        return residue
Пример #13
0
    def apply_patch(self, patch, precision=4):
        """
        Apply the specified PatchTemplate to the ResidueTemplate.

        This only handles patches that affect a single residue.

        An exception is thrown if patch is incompatible because
        * The patch specifies that an atom is to be deleted that doesn't exist in the residue
        * A bond specified as being added in the patch does not have both atom names present after adding/deleting atoms from the patch
        * The new net charge is not integral to the specified precision

        Parameters
        ----------

        patch : PatchTemplate
            The patch to apply to this residue

        precision : int, optional
            Each valid patch should be produce a net charge that is integral to
            this many decimal places.
            Default is 4

        Returns
        -------

        residue : ResidueTemplate
            A new ResidueTemplate corresponding to the patched residue is returned.
            The original remains unmodified.

        """
        # Create a copy
        # TODO: Once ResidueTemplate.from_residue() actually copies all info, use that instead?
        residue = _copy.copy(self)
        # Delete atoms
        for atom_name in patch.delete_atoms:
            try:
                residue.delete_atom(atom_name)
            except KeyError as e:
                raise IncompatiblePatchError(str(e))
        # Add or replace atoms
        for atom in patch.atoms:
            if atom.name in residue:
                # Overwrite type and charge
                residue[atom.name].type = atom.type
                residue[atom.name].charge = atom.charge
            else:
                residue.add_atom(
                    Atom(name=atom.name, type=atom.type, charge=atom.charge))
        # Add bonds
        for (atom1_name, atom2_name, order) in patch.add_bonds:
            try:
                # Remove dangling bonds
                for name in [atom1_name, atom2_name]:
                    if name == residue.head:
                        residue.head = None
                    if name == residue.tail:
                        residue.tail = None
                # Add bond
                residue.add_bond(atom1_name, atom2_name, order)
            except IndexError as e:
                raise IncompatiblePatchError(
                    'Bond %s-%s could not be added to patched residue: atoms are %s'
                    % (atom1_name, atom2_name, list(residue._map.keys())))
        # Delete impropers
        for impr in patch.delete_impropers:
            try:
                residue._impr.remove(impr)
            except ValueError as e:
                raise IncompatiblePatchError(
                    'Improper %s was not found in residue to be patched.' %
                    impr)
        # Check that the net charge is integral.
        net_charge = residue.net_charge
        is_integral = (round(net_charge, precision) - round(net_charge)) == 0.0
        if not is_integral:
            raise IncompatiblePatchError(
                'Patch is not compatible with residue due to non-integral charge (charge was %f).'
                % net_charge)
        # Ensure residue is connected
        import networkx as nx
        G = residue.to_networkx()
        if not nx.is_connected(G):
            components = [c for c in nx.connected_components(G)]
            raise IncompatiblePatchError(
                'Patched residue bond graph is not a connected graph: %s' %
                str(components))

        return residue
Пример #14
0
    def parse(filename):
        """ Read a PQR file and return a populated `Structure` class

        Parameters
        ----------
        filename : str or file-like
            Name of the PQR file to read, or a file-like object that can iterate
            over the lines of a PQR. Compressed file names can be specified and
            are determined by file-name extension (e.g., file.pqr.gz,
            file.pqr.bz2)

        Returns
        -------
        structure : :class:`Structure`
            The Structure object initialized with all of the information from
            the PDB file.  No bonds or other topological features are added by
            default.
        """
        if isinstance(filename, string_types):
            own_handle = True
            fileobj = genopen(filename, 'r')
        else:
            own_handle = False
            fileobj = filename

        struct = Structure()
        # Add metadata fields
        modelno = 1  # For PDB files with multiple MODELs
        atomno = 0
        coordinates = []
        all_coordinates = []

        # Support hexadecimal numbering like that printed by VMD
        try:
            for line in fileobj:
                words = line.split()
                if words[0] in ('ATOM', 'HETATM'):
                    atomno += 1
                    if len(words) == 10:
                        _, num, nam, res, resn, x, y, z, chg, rad = words
                        chn = ''
                    elif len(words) >= 11:
                        _, num, nam, res, chn, resn, x, y, z, chg, rad = (
                            words[i] for i in range(11))
                        # If the radius is not a float (but rather a letter,
                        # like the element or something), then the chain might
                        # be missing. In this case, shift all tokens "back" one
                        # and empty the chn string
                        try:
                            float(rad)
                        except ValueError:
                            resn, x, y, z, chg, rad = chn, resn, x, y, z, chg
                    else:
                        raise ValueError('Illegal PQR record format: expected '
                                         '10 or 11 tokens on the atom line')
                    x, y, z = float(x), float(y), float(z)
                    chg, rad = float(chg), float(rad)
                    resn, num = int(resn), int(num)
                    elem = element_by_name(nam)  # Yuck
                    atomic_number = AtomicNum[elem]
                    mass = Mass[elem]
                    if nam in ('EP', 'LP'):  # lone pair
                        atom = ExtraPoint(atomic_number=atomic_number,
                                          name=nam,
                                          charge=chg,
                                          mass=mass,
                                          number=num,
                                          solvent_radius=rad)
                    else:
                        atom = Atom(atomic_number=atomic_number,
                                    name=nam,
                                    charge=chg,
                                    mass=mass,
                                    number=num,
                                    solvent_radius=rad)
                    atom.xx, atom.xy, atom.xz = float(x), float(y), float(z)
                    if modelno == 1:
                        struct.add_atom(atom, res, resn, chn)
                    else:
                        try:
                            orig_atom = struct.atoms[atomno - 1]
                        except IndexError:
                            raise PDBError('Extra atom in MODEL %d' % modelno)
                        if (orig_atom.residue.name != res.strip()
                                or orig_atom.name != nam.strip()):
                            raise PDBError(
                                'Atom %d differs in MODEL %d [%s %s '
                                'vs. %s %s]' %
                                (atomno, modelno, orig_atom.residue.name,
                                 orig_atom.name, res, nam))
                    coordinates.extend([atom.xx, atom.xy, atom.xz])
                elif words[0] == 'ENDMDL':
                    # End the current model
                    if len(struct.atoms) == 0:
                        raise PDBError('MODEL ended before any atoms read in')
                    modelno += 1
                    if len(struct.atoms) * 3 != len(coordinates):
                        raise PDBError(
                            'Inconsistent atom numbers in some PDB models')
                    all_coordinates.append(coordinates)
                    atomno = 0
                    coordinates = []
                elif words[0] == 'MODEL':
                    if modelno == 1 and len(struct.atoms) == 0: continue
                    if len(coordinates) > 0:
                        if len(struct.atoms) * 3 != len(coordinates):
                            raise PDBError('Inconsistent atom numbers in '
                                           'some PDB models')
                        warnings.warn('MODEL not explicitly ended', PDBWarning)
                        all_coordinates.append(coordinates)
                        coordinates = []
                    modelno += 1
                    atomno = 0
                elif words[0] == 'CRYST1':
                    a, b, c = (float(w) for w in words[1:4])
                    try:
                        A, B, C = (float(w) for w in words[4:7])
                    except ValueError:
                        A = B = C = 90.0
                    struct.box = [a, b, c, A, B, C]
        finally:
            if own_handle: fileobj.close()

        struct.unchange()
        if coordinates:
            if len(coordinates) != 3 * len(struct.atoms):
                raise PDBError('bad number of atoms in some PQR models')
            all_coordinates.append(coordinates)
        struct._coordinates = np.array(all_coordinates).reshape(
            (-1, len(struct.atoms), 3))
        return struct
Пример #15
0
def load_topology(topology, system=None, xyz=None, box=None):
    """
    Creates a :class:`parmed.structure.Structure` instance from an OpenMM
    Topology, optionally filling in parameters from a System

    Parameters
    ----------
    topology : :class:`simtk.openmm.app.Topology`
        The Topology instance with the list of atoms and bonds for this system
    system : :class:`simtk.openmm.System` or str, optional
        If provided, parameters from this System will be applied to the
        Structure. If a string is given, it will be interpreted as the file name
        of an XML-serialized System, and it will be deserialized into a System
        before used to supply parameters
    xyz : str or array of float
        Name of a file containing coordinate information or an array of
        coordinates. If file has unit cell information, it also uses that
        information unless ``box`` (below) is also specified
    box : array of 6 floats
        Unit cell dimensions

    Returns
    -------
    struct : :class:`Structure <parmed.structure.Structure>`
        The structure from the provided topology

    Raises
    ------
    OpenMMWarning if parameters are found that cannot be interpreted or
    processed by ParmEd

    TypeError if there are any mismatches between the provided topology and
    system (e.g., they have different numbers of atoms)

    IOError if system is a string and it is not an existing file

    Notes
    -----
    Due to its flexibility with CustomForces, it is entirely possible that the
    functional form of the potential will be unknown to ParmEd. This function
    will try to use the energy expression to identify supported potential types
    that are implemented as CustomForce objects. In particular, quadratic
    improper torsions, when recognized, will be extracted.

    Other CustomForces, including the CustomNonbondedForce used to implement
    NBFIX (off-diagonal L-J modifications) and the 12-6-4 potential, will not be
    processed and will result in an unknown functional form
    """
    import simtk.openmm as mm
    struct = Structure()
    atommap = dict()
    for c in topology.chains():
        chain = c.id
        for r in c.residues():
            residue = r.name
            resid = r.index
            for a in r.atoms():
                if a.element is None:
                    atom = ExtraPoint(name=a.name)
                else:
                    atom = Atom(atomic_number=a.element.atomic_number,
                                name=a.name,
                                mass=a.element.mass)
                struct.add_atom(atom, residue, resid, chain)
                atommap[a] = atom
    for a1, a2 in topology.bonds():
        struct.bonds.append(Bond(atommap[a1], atommap[a2]))

    vectors = topology.getPeriodicBoxVectors()
    if vectors is not None:
        leng, ang = box_vectors_to_lengths_and_angles(*vectors)
        leng = leng.value_in_unit(u.angstroms)
        ang = ang.value_in_unit(u.degrees)
        struct.box = [leng[0], leng[1], leng[2], ang[0], ang[1], ang[2]]

    loaded_box = False

    if xyz is not None:
        if isinstance(xyz, string_types):
            xyz = load_file(xyz, skip_bonds=True)
            struct.coordinates = xyz.coordinates
            if struct.box is not None:
                if xyz.box is not None:
                    loaded_box = True
                    struct.box = xyz.box
        else:
            struct.coordinates = xyz

    if box is not None:
        loaded_box = True
        struct.box = box

    if struct.box is not None:
        struct.box = np.asarray(struct.box)

    if system is None:
        return struct

    if isinstance(system, string_types):
        system = load_file(system)

    if not isinstance(system, mm.System):
        raise TypeError('system must be an OpenMM System object or serialized '
                        'XML of an OpenMM System object')

    # We have a system, try to extract parameters from it
    if len(struct.atoms) != system.getNumParticles():
        raise TypeError('Topology and System have different numbers of atoms '
                        '(%d vs. %d)' %
                        (len(struct.atoms), system.getNumParticles()))

    processed_forces = set()
    ignored_forces = (mm.CMMotionRemover, mm.AndersenThermostat,
                      mm.MonteCarloBarostat, mm.MonteCarloAnisotropicBarostat,
                      mm.MonteCarloMembraneBarostat, mm.CustomExternalForce,
                      mm.GBSAOBCForce, mm.CustomGBForce)

    if system.usesPeriodicBoundaryConditions():
        if not loaded_box:
            vectors = system.getDefaultPeriodicBoxVectors()
            leng, ang = box_vectors_to_lengths_and_angles(*vectors)
            leng = leng.value_in_unit(u.angstroms)
            ang = ang.value_in_unit(u.degrees)
            struct.box = np.asarray(
                [leng[0], leng[1], leng[2], ang[0], ang[1], ang[2]])
    else:
        struct.box = None

    for force in system.getForces():
        if isinstance(force, mm.HarmonicBondForce):
            if mm.HarmonicBondForce in processed_forces:
                # Try to process this HarmonicBondForce as a Urey-Bradley term
                _process_urey_bradley(struct, force)
            else:
                _process_bond(struct, force)
        elif isinstance(force, mm.HarmonicAngleForce):
            _process_angle(struct, force)
        elif isinstance(force, mm.PeriodicTorsionForce):
            _process_dihedral(struct, force)
        elif isinstance(force, mm.RBTorsionForce):
            _process_rbtorsion(struct, force)
        elif isinstance(force, mm.CustomTorsionForce):
            if not _process_improper(struct, force):
                struct.unknown_functional = True
                warnings.warn('Unknown functional form of CustomTorsionForce',
                              OpenMMWarning)
        elif isinstance(force, mm.CMAPTorsionForce):
            _process_cmap(struct, force)
        elif isinstance(force, mm.NonbondedForce):
            _process_nonbonded(struct, force)
        elif isinstance(force, ignored_forces):
            continue
        else:
            struct.unknown_functional = True
            warnings.warn('Unsupported Force type %s' % type(force).__name__,
                          OpenMMWarning)
        processed_forces.add(type(force))

    return struct