Exemplo n.º 1
0
def _new_atom(parm, carbon, hydrogen, name):
    """
    Creates a new hydrogen atom and add the necessary topology

    Parameters
    ----------
    parm : parmed.Structure
        the topology
    carbon : parmed.Atom
        the carbon atom to which the hydrogen should be bonded
    hydrogen : parmed.Atom
        the other hydrogen, already in the topology
    name : string
        the name of the hydrogen atom

    Returns
    -------
    parmed.Atom
        the newly created atom
    """
    new = parmed.Atom(list=parm.atoms,
                      name=name,
                      type="HAL2",
                      charge=0.0,
                      mass=1.008000)
    new.residue = parm.residues[0]
    parm.residues[0].atoms.insert(hydrogen.idx + 1, new)
    parm.atoms.insert(hydrogen.idx + 1, new)
    # Add topology
    parm.bonds.append(parmed.Bond(carbon, new))
    for a in parm.adjusts:
        if a.atom1 == hydrogen:
            parm.adjusts.append(parmed.NonbondedException(new, a.atom2))
        elif a.atom2 == hydrogen:
            parm.adjusts.append(parmed.NonbondedException(a.atom1, new))
    for angle in hydrogen.angles:
        parm.angles.append(_new_angle(angle, hydrogen, new))
    parm.angles.append(parmed.Angle(hydrogen, carbon, new))
    parm.angles[-1].funct = 5
    for dihedral in hydrogen.dihedrals:
        parm.dihedrals.append(_new_dihedral(dihedral, hydrogen, new))
    return new
Exemplo n.º 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
Exemplo n.º 3
0
def _add_atom(parm, last_idx, template, htemplate):
    """
    Change the terminal hydrogen to a carbon and add three hydrogen atoms,
    also add bonded information

    Parameters
    ----------
    parm : parmed.Structure
        the topology
    last_idx : integer
        the index of the terminal carbon
    template : string
        the name prefix of the carbon atoms
    htemplate: string
        the name postfox of the hydrogen atoms
    """

    # Find all carbons preceeding the terminal carbon and the hydrogens
    # bonded to them
    carbons = []
    hydrogens = []
    for idx in range(last_idx - 2, last_idx + 1):
        carbons.append(_get_atom(parm, "%s%d" % (template, idx)))
        hydrogens.append(
            [_get_atom(parm, "H%d%s" % (idx, hstr)) for hstr in htemplate[:2]])

    # Change the charges of the carbon and hydrogens next to the terminal carbon
    carbons[1].charge = 0.0
    hydrogens[1][0].charge = 0.0
    hydrogens[1][1].charge = 0.0

    # Change the type and charge of the terminal carbon and its hydrogens
    carbons[2].charge = 0.0470
    carbons[2].type = "CTL2"
    for h in hydrogens[2]:
        h.charge = -0.0070
        h.type = "HAL2"

    # Find the final hydrogen and change it to a carbon
    hatom = _get_atom(parm, "H%d%s" % (last_idx, htemplate[2]))
    hatom.type = "CTL3"
    hatom.charge = -0.0810
    hatom.name = "%s%d" % (template, last_idx + 1)
    hatom.mass = 12.0110

    # Create 3 new hydrogen atoms and the topology information
    newatoms = []
    for i, hstr in enumerate(htemplate):
        newatom = parmed.Atom(list=parm.atoms,
                              name="H%d%s" % (last_idx + 1, hstr),
                              type="HAL3",
                              charge=0.0160,
                              mass=1.008000)
        newatom.residue = parm.residues[0]
        parm.residues[0].atoms.insert(hatom.idx + 1 + i, newatom)
        parm.atoms.insert(hatom.idx + 1 + i, newatom)
        parm.bonds.append(parmed.Bond(hatom, newatom))
        parm.adjusts.append(parmed.NonbondedException(carbons[1], newatom))
        parm.adjusts.append(parmed.NonbondedException(hydrogens[2][0],
                                                      newatom))
        parm.adjusts.append(parmed.NonbondedException(hydrogens[2][1],
                                                      newatom))
        parm.angles.append(parmed.Angle(carbons[2], hatom, newatom))
        parm.dihedrals.append(
            parmed.Dihedral(carbons[1], carbons[2], hatom, newatom))
        parm.dihedrals.append(
            parmed.Dihedral(hydrogens[2][0], carbons[2], hatom, newatom))
        parm.dihedrals.append(
            parmed.Dihedral(hydrogens[2][1], carbons[2], hatom, newatom))
        newatoms.append(newatom)

    # Add angles between thre three added hydrogens
    parm.angles.append(parmed.Angle(newatoms[0], hatom, newatoms[1]))
    parm.angles.append(parmed.Angle(newatoms[0], hatom, newatoms[2]))
    parm.angles.append(parmed.Angle(newatoms[1], hatom, newatoms[2]))

    # Correct the angle and dihedral types
    for angle in parm.angles[-12:]:
        angle.funct = 5
    for dihedral in parm.dihedrals[-9:]:
        dihedral.funct = 9