Beispiel #1
0
    def convert(self, obj):
        """Write selection at current trajectory frame to :class:`~parmed.structure.Structure`.

        Parameters
        -----------
        obj : AtomGroup or Universe or :class:`Timestep`
        """
        try:
            import parmed as pmd
        except ImportError:
            raise ImportError('ParmEd is required for ParmEdConverter but '
                              'is not installed. Try installing it with \n'
                              'pip install parmed')
        try:
            # make sure to use atoms (Issue 46)
            ag_or_ts = obj.atoms
        except AttributeError:
            if isinstance(obj, base.Timestep):
                raise ValueError("Writing Timesteps to ParmEd "
                                 "objects is not supported")
            else:
                raise_from(TypeError("No atoms found in obj argument"), None)

        # Check for topology information
        missing_topology = []
        try:
            names = ag_or_ts.names
        except (AttributeError, NoDataError):
            names = itertools.cycle(('X', ))
            missing_topology.append('names')
        try:
            resnames = ag_or_ts.resnames
        except (AttributeError, NoDataError):
            resnames = itertools.cycle(('UNK', ))
            missing_topology.append('resnames')

        if missing_topology:
            warnings.warn(
                "Supplied AtomGroup was missing the following attributes: "
                "{miss}. These will be written with default values. "
                "Alternatively these can be supplied as keyword arguments."
                "".format(miss=', '.join(missing_topology)))

        try:
            positions = ag_or_ts.positions
        except:
            positions = [None] * ag_or_ts.n_atoms

        try:
            velocities = ag_or_ts.velocities
        except:
            velocities = [None] * ag_or_ts.n_atoms

        atom_kwargs = []
        for atom, name, resname, xyz, vel in zip(ag_or_ts, names, resnames,
                                                 positions, velocities):
            akwargs = {'name': name}
            chain_seg = {'segid': atom.segid}
            for attrname in ('mass', 'charge', 'type', 'altLoc', 'tempfactor',
                             'occupancy', 'gbscreen', 'solventradius',
                             'nbindex', 'rmin', 'epsilon', 'rmin14',
                             'epsilon14', 'id'):
                try:
                    akwargs[MDA2PMD.get(attrname,
                                        attrname)] = getattr(atom, attrname)
                except AttributeError:
                    pass
            try:
                el = atom.element.lower().capitalize()
                akwargs['atomic_number'] = SYMB2Z[el]
            except (KeyError, AttributeError):
                try:
                    tp = atom.type.lower().capitalize()
                    akwargs['atomic_number'] = SYMB2Z[tp]
                except (KeyError, AttributeError):
                    pass
            try:
                chain_seg['chain'] = atom.chainID
            except AttributeError:
                pass
            try:
                chain_seg['inscode'] = atom.icode
            except AttributeError:
                pass
            atom_kwargs.append(
                (akwargs, resname, atom.resid, chain_seg, xyz, vel))

        struct = pmd.Structure()

        for akwarg, resname, resid, kw, xyz, vel in atom_kwargs:
            atom = pmd.Atom(**akwarg)
            if xyz is not None:
                atom.xx, atom.xy, atom.xz = xyz

            if vel is not None:
                atom.vx, atom.vy, atom.vz = vel

            atom.atom_type = pmd.AtomType(
                akwarg['name'],
                None,
                akwarg['mass'],
                atomic_number=akwargs.get('atomic_number'))
            struct.add_atom(atom, resname, resid, **kw)

        try:
            struct.box = ag_or_ts.dimensions
        except AttributeError:
            struct.box = None

        if hasattr(ag_or_ts, 'universe'):
            atomgroup = {
                atom: index
                for index, atom in enumerate(list(ag_or_ts))
            }
            get_atom_indices = functools.partial(get_indices_from_subset,
                                                 atomgroup=atomgroup,
                                                 universe=ag_or_ts.universe)
        else:
            get_atom_indices = lambda x: x

        # bonds
        try:
            params = ag_or_ts.bonds.atomgroup_intersection(ag_or_ts,
                                                           strict=True)
        except AttributeError:
            pass
        else:
            for p in params:
                atoms = [
                    struct.atoms[i] for i in map(get_atom_indices, p.indices)
                ]
                try:
                    for obj in p.type:
                        bond = pmd.Bond(*atoms, type=obj.type, order=obj.order)
                        struct.bonds.append(bond)
                    if isinstance(obj.type, pmd.BondType):
                        struct.bond_types.append(bond.type)
                        bond.type.list = struct.bond_types
                except (TypeError, AttributeError):
                    order = p.order if p.order is not None else 1
                    btype = getattr(p.type, 'type', None)

                    bond = pmd.Bond(*atoms, type=btype, order=order)
                    struct.bonds.append(bond)
                    if isinstance(bond.type, pmd.BondType):
                        struct.bond_types.append(bond.type)
                        bond.type.list = struct.bond_types

        # dihedrals
        try:
            params = ag_or_ts.dihedrals.atomgroup_intersection(ag_or_ts,
                                                               strict=True)
        except AttributeError:
            pass
        else:
            for p in params:
                atoms = [
                    struct.atoms[i] for i in map(get_atom_indices, p.indices)
                ]
                try:
                    for obj in p.type:
                        imp = getattr(obj, 'improper', False)
                        ign = getattr(obj, 'ignore_end', False)
                        dih = pmd.Dihedral(*atoms,
                                           type=obj.type,
                                           ignore_end=ign,
                                           improper=imp)
                        struct.dihedrals.append(dih)
                        if isinstance(dih.type, pmd.DihedralType):
                            struct.dihedral_types.append(dih.type)
                            dih.type.list = struct.dihedral_types
                except (TypeError, AttributeError):
                    btype = getattr(p.type, 'type', None)
                    imp = getattr(p.type, 'improper', False)
                    ign = getattr(p.type, 'ignore_end', False)
                    dih = pmd.Dihedral(*atoms,
                                       type=btype,
                                       improper=imp,
                                       ignore_end=ign)
                    struct.dihedrals.append(dih)
                    if isinstance(dih.type, pmd.DihedralType):
                        struct.dihedral_types.append(dih.type)
                        dih.type.list = struct.dihedral_types

        for param, pmdtype, trackedlist, typelist, clstype in (
            ('ureybradleys', pmd.UreyBradley, struct.urey_bradleys,
             struct.urey_bradley_types, pmd.BondType),
            ('angles', pmd.Angle, struct.angles, struct.angle_types,
             pmd.AngleType), ('impropers', pmd.Improper, struct.impropers,
                              struct.improper_types, pmd.ImproperType),
            ('cmaps', pmd.Cmap, struct.cmaps, struct.cmap_types,
             pmd.CmapType)):
            try:
                params = getattr(ag_or_ts, param)
                values = params.atomgroup_intersection(ag_or_ts, strict=True)
            except AttributeError:
                pass
            else:
                for v in values:
                    atoms = [
                        struct.atoms[i]
                        for i in map(get_atom_indices, v.indices)
                    ]

                    try:
                        for parmed_obj in v.type:
                            p = pmdtype(*atoms, type=parmed_obj.type)
                            trackedlist.append(p)
                            if isinstance(p.type, clstype):
                                typelist.append(p.type)
                                p.type.list = typelist
                    except (TypeError, AttributeError):
                        vtype = getattr(v.type, 'type', None)

                        p = pmdtype(*atoms, type=vtype)
                        trackedlist.append(p)
                        if isinstance(p.type, clstype):
                            typelist.append(p.type)
                            p.type.list = typelist
        return struct
Beispiel #2
0
def to_parmed(off_system: "System") -> pmd.Structure:
    """Convert an OpenFF System to a ParmEd Structure"""
    structure = pmd.Structure()
    _convert_box(off_system.box, structure)

    if "Electrostatics" in off_system.handlers.keys():
        has_electrostatics = True
        electrostatics_handler = off_system.handlers["Electrostatics"]
    else:
        has_electrostatics = False

    for topology_molecule in off_system.topology.topology_molecules:  # type: ignore[union-attr]
        for atom in topology_molecule.atoms:
            atomic_number = atom.atomic_number
            element = pmd.periodic_table.Element[atomic_number]
            mass = pmd.periodic_table.Mass[element]
            structure.add_atom(
                pmd.Atom(
                    atomic_number=atomic_number,
                    mass=mass,
                ),
                resname="FOO",
                resnum=0,
            )

    if "Bonds" in off_system.handlers.keys():
        bond_handler = off_system.handlers["Bonds"]
        bond_type_map: Dict = dict()
        for pot_key, pot in bond_handler.potentials.items():
            k = pot.parameters["k"].to(kcal_mol_a2).magnitude / 2
            length = pot.parameters["length"].to(unit.angstrom).magnitude
            bond_type = pmd.BondType(k=k, req=length)
            bond_type_map[pot_key] = bond_type
            structure.bond_types.append(bond_type)

        for top_key, pot_key in bond_handler.slot_map.items():
            idx_1, idx_2 = top_key.atom_indices
            bond_type = bond_type_map[pot_key]
            bond = pmd.Bond(
                atom1=structure.atoms[idx_1],
                atom2=structure.atoms[idx_2],
                type=bond_type,
            )
            structure.bonds.append(bond)

    structure.bond_types.claim()

    if "Angles" in off_system.handlers.keys():
        angle_handler = off_system.handlers["Angles"]
        angle_type_map: Dict = dict()
        for pot_key, pot in angle_handler.potentials.items():
            k = pot.parameters["k"].to(kcal_mol_rad2).magnitude / 2
            theta = pot.parameters["angle"].to(unit.degree).magnitude
            # TODO: Look up if AngleType already exists in struct
            angle_type = pmd.AngleType(k=k, theteq=theta)
            angle_type_map[pot_key] = angle_type
            structure.angle_types.append(angle_type)

        for top_key, pot_key in angle_handler.slot_map.items():
            idx_1, idx_2, idx_3 = top_key.atom_indices
            angle_type = angle_type_map[pot_key]
            structure.angles.append(
                pmd.Angle(
                    atom1=structure.atoms[idx_1],
                    atom2=structure.atoms[idx_2],
                    atom3=structure.atoms[idx_3],
                    type=angle_type,
                ))
            structure.angle_types.append(angle_type)

    structure.angle_types.claim()

    # ParmEd treats 1-4 scaling factors at the level of each DihedralType,
    # whereas SMIRNOFF captures them at the level of the non-bonded handler,
    # so they need to be stored here for processing dihedrals
    vdw_14 = off_system.handlers["vdW"].scale_14  # type: ignore[attr-defined]
    if has_electrostatics:
        coul_14 = off_system.handlers[
            "Electrostatics"].scale_14  # type: ignore[attr-defined]
    else:
        coul_14 = 1.0
    vdw_handler = off_system.handlers["vdW"]
    if "ProperTorsions" in off_system.handlers.keys():
        proper_torsion_handler = off_system.handlers["ProperTorsions"]
        proper_type_map: Dict = dict()
        for pot_key, pot in proper_torsion_handler.potentials.items():
            k = pot.parameters["k"].to(kcal_mol).magnitude
            periodicity = pot.parameters["periodicity"]
            phase = pot.parameters["phase"].magnitude
            proper_type = pmd.DihedralType(
                phi_k=k,
                per=periodicity,
                phase=phase,
                scnb=1 / vdw_14,
                scee=1 / coul_14,
            )
            proper_type_map[pot_key] = proper_type
            structure.dihedral_types.append(proper_type)

        for top_key, pot_key in proper_torsion_handler.slot_map.items():
            idx_1, idx_2, idx_3, idx_4 = top_key.atom_indices
            dihedral_type = proper_type_map[pot_key]
            structure.dihedrals.append(
                pmd.Dihedral(
                    atom1=structure.atoms[idx_1],
                    atom2=structure.atoms[idx_2],
                    atom3=structure.atoms[idx_3],
                    atom4=structure.atoms[idx_4],
                    type=dihedral_type,
                ))
            structure.dihedral_types.append(dihedral_type)

            key1 = TopologyKey(atom_indices=(idx_1, ))
            key4 = TopologyKey(atom_indices=(idx_4, ))
            vdw1 = vdw_handler.potentials[vdw_handler.slot_map[key1]]
            vdw4 = vdw_handler.potentials[vdw_handler.slot_map[key4]]
            sig1, eps1 = _lj_params_from_potential(vdw1)
            sig4, eps4 = _lj_params_from_potential(vdw4)
            sig = (sig1 + sig4) * 0.5
            eps = (eps1 * eps4)**0.5
            nbtype = pmd.NonbondedExceptionType(rmin=sig * 2**(1 / 6),
                                                epsilon=eps * vdw_14,
                                                chgscale=coul_14)
            structure.adjusts.append(
                pmd.NonbondedException(structure.atoms[idx_1],
                                       structure.atoms[idx_4],
                                       type=nbtype))
            structure.adjust_types.append(nbtype)

    structure.dihedral_types.claim()
    structure.adjust_types.claim()

    #    if False:  # "ImroperTorsions" in off_system.term_collection.terms:
    #        improper_term = off_system.term_collection.terms["ImproperTorsions"]
    #        for improper, smirks in improper_term.smirks_map.items():
    #            idx_1, idx_2, idx_3, idx_4 = improper
    #            pot = improper_term.potentials[improper_term.smirks_map[improper]]
    #            # TODO: Better way of storing periodic data in generally, probably need to improve Potential
    #            n = re.search(r"\d", "".join(pot.parameters.keys())).group()
    #            k = pot.parameters["k" + n].m  # kcal/mol
    #            periodicity = pot.parameters["periodicity" + n].m  # dimless
    #            phase = pot.parameters["phase" + n].m  # degree
    #
    #            dihedral_type = pmd.DihedralType(per=periodicity, phi_k=k, phase=phase)
    #            structure.dihedrals.append(
    #                pmd.Dihedral(
    #                    atom1=structure.atoms[idx_1],
    #                    atom2=structure.atoms[idx_2],
    #                    atom3=structure.atoms[idx_3],
    #                    atom4=structure.atoms[idx_4],
    #                    type=dihedral_type,
    #                )
    #            )

    vdw_handler = off_system.handlers["vdW"]
    for pmd_idx, pmd_atom in enumerate(structure.atoms):
        top_key = TopologyKey(atom_indices=(pmd_idx, ))
        smirks = vdw_handler.slot_map[top_key]
        potential = vdw_handler.potentials[smirks]
        element = pmd.periodic_table.Element[pmd_atom.element]
        sigma, epsilon = _lj_params_from_potential(potential)

        atom_type = pmd.AtomType(
            name=element + str(pmd_idx + 1),
            number=pmd_idx,
            atomic_number=pmd_atom.atomic_number,
            mass=pmd.periodic_table.Mass[element],
        )

        atom_type.set_lj_params(eps=epsilon, rmin=sigma * 2**(1 / 6) / 2)
        pmd_atom.atom_type = atom_type
        pmd_atom.type = atom_type.name
        pmd_atom.name = pmd_atom.type

    for pmd_idx, pmd_atom in enumerate(structure.atoms):
        if has_electrostatics:
            top_key = TopologyKey(atom_indices=(pmd_idx, ))
            partial_charge = electrostatics_handler.charges[
                top_key]  # type: ignore[attr-defined]
            unitless_ = partial_charge.to(unit.elementary_charge).magnitude
            pmd_atom.charge = float(unitless_)
            pmd_atom.atom_type.charge = float(unitless_)
        else:
            pmd_atom.charge = 0

    # Assign dummy residue names, GROMACS will not accept empty strings
    for res in structure.residues:
        res.name = "FOO"

    structure.positions = off_system.positions.to(
        unit.angstrom).magnitude  # type: ignore[attr-defined]
    for idx, pos in enumerate(structure.positions):
        structure.atoms[idx].xx = pos._value[0]
        structure.atoms[idx].xy = pos._value[1]
        structure.atoms[idx].xz = pos._value[2]

    return structure
Beispiel #3
0
    def patch(self, top_string, crd_string):
        INTOP = "in.top"
        INRST = "in.rst"
        OUTTOP = "out.top"
        OUTRST = "out.rst"

        with util.in_temp_dir():
            with open(INTOP, "wt") as outfile:
                outfile.write(top_string)
            with open(INRST, "wt") as outfile:
                outfile.write(crd_string)

            base = pmd.load_file(INTOP)
            crd = pmd.load_file(INRST)
            base.coordinates = crd.coordinates

            # create a new structure to add our dummy atoms to
            parm = pmd.Structure()

            # add in atom type for our dummy particles
            atype = pmd.AtomType("SDUM", 0, mass=12.0, charge=0.0)
            atype.set_lj_params(0.0, 0.0)

            for i in range(self.n_tensors):
                a1 = pmd.Atom(
                    name="S1",
                    atomic_number=atype.atomic_number,
                    type=str(atype),
                    charge=atype.charge,
                    mass=atype.mass,
                    solvent_radius=1.0,
                    screen=0.5,
                )
                a1.atom_type = atype

                a2 = pmd.Atom(
                    name="S2",
                    atomic_number=atype.atomic_number,
                    type=str(atype),
                    charge=atype.charge,
                    mass=atype.mass,
                    solvent_radius=1.0,
                    screen=0.5,
                )
                a2.atom_type = atype

                parm.add_atom(a1, resname="SDM", resnum=i)
                parm.add_atom(a2, resname="SDM", resnum=i)

            # we add noise here because we'll get NaN if the particles ever
            # end up exactly on top of each other
            parm.positions = np.zeros((2 * self.n_tensors, 3))

            # combine the old system with the new dummy atoms
            comb = base + parm
            last_index = comb.residues[-1].idx
            self.resids = list(range(last_index - self.n_tensors + 2, last_index + 2))

            comb.write_parm(OUTTOP)
            comb.write_rst7(OUTRST)
            with open(OUTTOP, "rt") as infile:
                top_string = infile.read()
            with open(OUTRST, "rt") as infile:
                crd_string = infile.read()
        return top_string, crd_string