def _add_dodecane_to_topology(self, topology):

        # Carbon element
        carbon_element = Element.getBySymbol('C')
        hydrogen_element = Element.getBySymbol('H')

        chain = topology.addChain("{}-C12".format(topology.getNumChains() + 1))
        residue = topology.addResidue("C12", chain)
        prev_atom = topology.addAtom("C", carbon_element, residue)
        if self.forceField_str == "TraPPE-UA":
            for i in range(11):
                curr_atom = topology.addAtom("C{}".format(i + 1),
                                             carbon_element, residue)
                topology.addBond(prev_atom, curr_atom)
                prev_atom = curr_atom
        else:
            H_counter = 0
            for _ in range(3):
                H = topology.addAtom("H{}".format(H_counter), hydrogen_element,
                                     residue)
                topology.addBond(H, prev_atom)
            for i in range(11):
                curr_atom = topology.addAtom("C{}".format(i + 1),
                                             carbon_element, residue)
                topology.addBond(prev_atom, curr_atom)
                for _ in range(2):
                    H = topology.addAtom("H{}".format(H_counter),
                                         hydrogen_element, residue)
                    topology.addBond(H, curr_atom)
                    prev_atom = curr_atom
            H = topology.addAtom("H{}".format(H_counter), hydrogen_element,
                                 residue)
            topology.addBond(H, prev_atom)

        return chain.id
示例#2
0
def openmm_topology(atoms, bonds):
    '''Make OpenMM topology from ChimeraX atoms and bonds.'''
    a = atoms
    n = len(a)
    r = a.residues
    aname = a.names
    ename = a.element_names
    rname = r.names
    rnum = r.numbers
    cids = r.chain_ids
    from simtk.openmm.app import Topology, Element
    top = Topology()
    cmap = {}
    rmap = {}
    atoms = {}
    for i in range(n):
        cid = cids[i]
        if not cid in cmap:
            cmap[cid] = top.addChain()  # OpenMM chains have no name.
        rid = (rname[i], rnum[i], cid)
        if not rid in rmap:
            rmap[rid] = top.addResidue(rname[i], cmap[cid])
        element = Element.getBySymbol(ename[i])
        atoms[i] = top.addAtom(aname[i], element, rmap[rid])
    a1, a2 = bonds.atoms
    for i1, i2 in zip(a.indices(a1), a.indices(a2)):
        top.addBond(atoms[i1], atoms[i2])
    return top
示例#3
0
def openmm_topology_from_chimerax_model(model):
    '''
  Take an AtomicStructure model from ChimeraX and return an OpenMM
  topology (e.g. for use with the OpenMM Modeller class).
  '''
    a = model.atoms
    b = model.bonds
    n = len(a)
    r = a.residues
    aname = a.names
    ename = a.element_names
    rname = r.names
    rnum = r.numbers
    cids = r.chain_ids
    from simtk.openmm.app import Topology, Element
    from simtk import unit
    top = Topology()
    cmap = {}
    rmap = {}
    atoms = {}
    for i in range(n):
        cid = cids[i]
        if not cid in cmap:
            cmap[cid] = top.addChain()  # OpenMM chains have no name
        rid = (rname[i], rnum[i], cid)
        if not rid in rmap:
            rmap[rid] = top.addResidue(rname[i], cmap[cid])
        element = Element.getBySymbol(ename[i])
        atoms[i] = top.addAtom(aname[i], element, rmap[rid])
    a1, a2 = b.atoms
    for i1, i2 in zip(a.indices(a1), a.indices(a2)):
        if -1 not in [i1, i2]:
            top.addBond(atoms[i1], atoms[i2])
    return top
示例#4
0
    def _apply_init(self, result: ComponentResult) -> None:

        from simtk.openmm.app import Element

        self._cache["elements"] = [
            Element.getBySymbol(ele).atomic_number
            if isinstance(ele, str) else ele for ele in self.allowed_elements
        ]
    def _createTopology(self):
        """Build the topology of the system
        """
        top = Topology()
        positions = []
        velocities = []

        boxVectors = []
        for x, y, z in self._conn.execute('SELECT x, y, z FROM global_cell'):
            boxVectors.append(mm.Vec3(x, y, z))
        unitCellDimensions = [boxVectors[0][0], boxVectors[1][1], boxVectors[2][2]]
        top.setUnitCellDimensions(unitCellDimensions*angstrom)

        atoms = {}
        lastChain = None
        lastResId = None
        c = top.addChain()
        q = """SELECT id, name, anum, resname, resid, chain, x, y, z, vx, vy, vz
        FROM particle ORDER BY id"""
        for (atomId, atomName, atomNumber, resName, resId, chain, x, y, z, vx, vy, vz) in self._conn.execute(q):
            newChain = False
            if chain != lastChain:
                lastChain = chain
                c = top.addChain()
                newChain = True
            if resId != lastResId or newChain:
                lastResId = resId
                if resName in PDBFile._residueNameReplacements:
                    resName = PDBFile._residueNameReplacements[resName]
                r = top.addResidue(resName, c)
                if resName in PDBFile._atomNameReplacements:
                    atomReplacements = PDBFile._atomNameReplacements[resName]
                else:
                    atomReplacements = {}

            if atomNumber == 0 and atomName.startswith('Vrt'):
                elem = None
            else:
                elem = Element.getByAtomicNumber(atomNumber)
            
            if atomName in atomReplacements:
                atomName = atomReplacements[atomName]

            atoms[atomId] = top.addAtom(atomName, elem, r)
            positions.append(mm.Vec3(x, y, z))

            velocities.append(mm.Vec3(vx, vy, vz))
    
        for p0, p1 in self._conn.execute('SELECT p0, p1 FROM bond'):
            top.addBond(atoms[p0], atoms[p1])

        positions = positions*angstrom
        velocities = velocities*angstrom/femtosecond
        return top, positions, velocities
    def _createTopology(self):
        """Build the topology of the system
        """
        top = Topology()
        positions = []
        velocities = []

        boxVectors = []
        for x, y, z in self._conn.execute('SELECT x, y, z FROM global_cell'):
            boxVectors.append(mm.Vec3(x, y, z))
        unitCellDimensions = [boxVectors[0][0], boxVectors[1][1], boxVectors[2][2]]
        top.setUnitCellDimensions(unitCellDimensions*angstrom)

        atoms = {}
        lastChain = None
        lastResId = None
        c = top.addChain()
        q = """SELECT id, name, anum, resname, resid, chain, x, y, z, vx, vy, vz
        FROM particle ORDER BY id"""
        for (atomId, atomName, atomNumber, resName, resId, chain, x, y, z, vx, vy, vz) in self._conn.execute(q):
            newChain = False
            if chain != lastChain:
                lastChain = chain
                c = top.addChain()
                newChain = True
            if resId != lastResId or newChain:
                lastResId = resId
                if resName in PDBFile._residueNameReplacements:
                    resName = PDBFile._residueNameReplacements[resName]
                r = top.addResidue(resName, c)
                if resName in PDBFile._atomNameReplacements:
                    atomReplacements = PDBFile._atomNameReplacements[resName]
                else:
                    atomReplacements = {}

            if atomNumber == 0 and atomName.startswith('Vrt'):
                elem = None
            else:
                elem = Element.getByAtomicNumber(atomNumber)
	    	
            if atomName in atomReplacements:
                atomName = atomReplacements[atomName]

            atoms[atomId] = top.addAtom(atomName, elem, r)
            positions.append(mm.Vec3(x, y, z))

            velocities.append(mm.Vec3(vx, vy, vz))
	
        for p0, p1 in self._conn.execute('SELECT p0, p1 FROM bond'):
            top.addBond(atoms[p0], atoms[p1])

        positions = positions*angstrom
        velocities = velocities*angstrom/femtosecond
        return top, positions, velocities
    def openmm_topology_and_external_forces(self,
                                            sim_construct,
                                            sim_bonds,
                                            fix_shell_backbones=False,
                                            tug_hydrogens=False,
                                            hydrogens_feel_maps=False,
                                            logging=False,
                                            log=None):
        a = sim_construct
        n = len(a)
        r = a.residues
        aname = a.names
        ename = a.element_names
        rname = r.names
        rnum = r.numbers
        cids = r.chain_ids
        from simtk.openmm.app import Topology, Element
        from simtk import unit
        top = self._simulation_topology = Topology()
        cmap = {}
        rmap = {}
        atoms = self._atoms = {}
        for i in range(n):
            cid = cids[i]
            if not cid in cmap:
                cmap[cid] = top.addChain()  # OpenMM chains have no name
            rid = (rname[i], rnum[i], cid)
            if not rid in rmap:
                rmap[rid] = top.addResidue(rname[i], cmap[cid])
            element = Element.getBySymbol(ename[i])
            atoms[i] = top.addAtom(aname[i], element, rmap[rid])

            # Register atoms with forces
            if ename is not 'H' or (ename is 'H' and tug_hydrogens):
                # All CustomExternalForces
                for key, ff in self._custom_external_forces.items():
                    f = ff[0]
                    per_particle_param_vals = ff[3]
                    index_map = ff[4]
                    index_map[i] = f.addParticle(i, per_particle_param_vals)

            if ename is not 'H' or (ename is 'H' and hydrogens_feel_maps):
                # All map forces
                for m in self._maps:
                    self.couple_atom_to_map(i, m)

        a1, a2 = sim_bonds.atoms
        for i1, i2 in zip(a.indices(a1), a.indices(a2)):
            if -1 not in [i1, i2]:
                top.addBond(atoms[i1], atoms[i2])

        from simtk.openmm import Vec3
        pos = a.coords  # in Angstrom (convert to nm for OpenMM)
        return top, pos
示例#8
0
    def _createTopology(self):
        '''Build the topology of the system
        '''
        top = Topology()
        positions = []

        boxVectors = []
        for x, y, z in self._conn.execute('SELECT x, y, z FROM global_cell'):
            boxVectors.append(mm.Vec3(x, y, z)*angstrom)
        unitCellDimensions = [boxVectors[0][0], boxVectors[1][1], boxVectors[2][2]]
        top.setUnitCellDimensions(unitCellDimensions)

        atoms = {}
        lastChain = None
        lastResId = None
        c = top.addChain()
        q = '''SELECT id, name, anum, resname, resid, chain, x, y, z
        FROM particle'''
        for (atomId, atomName, atomNumber, resName, resId, chain, x, y, z) in self._conn.execute(q):
            if chain != lastChain:
                lastChain = chain
                c = top.addChain()
            if resId != lastResId:
                lastResId = resId
                if resName in PDBFile._residueNameReplacements:
                    resName = PDBFile._residueNameReplacements[resName]
                r = top.addResidue(resName, c)
                if resName in PDBFile._atomNameReplacements:
                    atomReplacements = PDBFile._atomNameReplacements[resName]
                else:
                    atomReplacements = {}

            if atomName in atomReplacements:
                atomName = atomReplacements[atomName]

            elem = Element.getByAtomicNumber(atomNumber)
            atoms[atomId] = top.addAtom(atomName, elem, r)
            positions.append(mm.Vec3(x, y, z)*angstrom)

        for p0, p1 in self._conn.execute('SELECT p0, p1 FROM bond'):
            top.addBond(atoms[p0], atoms[p1])

        return top, positions
示例#9
0
    def check_allowed_elements(cls, element: Union[str,
                                                   int]) -> Union[str, int]:
        """
        Check that each item can be cast to a valid element.

        Parameters:
            element: The element that should be checked.

        Raises:
            ValueError: If the element number or symbol passed could not be converted into a valid element.
        """
        from simtk.openmm.app import Element

        if isinstance(element, int):
            return element
        else:
            try:
                _ = Element.getBySymbol(element)
                return element
            except KeyError:
                raise KeyError(
                    f"An element could not be determined from symbol {element}, please enter symbols only."
                )
示例#10
0
class BranchedChainOptions(ChainOptions):

    _SECTION_NAME = "BranchedChain"

    # =========================================================================

    PHOSPHORUS = Element.getBySymbol('P')

    # =========================================================================

    def __init__(self, topology_options):
        super(BranchedChainOptions, self).__init__(topology_options)
        self.backbone = None
        self.branches = []
        self.pdb = None

    def _create_options(self):
        super(BranchedChainOptions, self)._create_options()
        self._OPTIONS['backbone'] = self._parse_backbone

    def _create_sections(self):
        super(BranchedChainOptions, self)._create_sections()
        self._SECTIONS['Branch'] = self._parse_branch

    # =========================================================================

    def _check_for_incomplete_input(self):
        if self.backbone is None:
            self._incomplete_error('backbone')

    # =========================================================================

    def _parse_backbone(self, *args):
        self.backbone = evaluate_output(create_stack(tokenize_expr(args[0])))

    def _parse_branch(self, *args):
        line_deque = args[1]
        branch_options = BranchOptions()
        branch_options.parse(line_deque.popleft())
        self.branches.append((branch_options.sequence, branch_options.indices))

    # =========================================================================

    def add_chain_to_topology(self, topology):
        for _ in range(self.num):
            self._add_chain(topology)

    @staticmethod
    def _find_atom_by_name(name, residue):
        for atom in residue.atoms():
            if atom.name == name:
                return atom
        raise ValueError("No atom with name {} found.".format(name))

    def _add_chain(self, topology):

        # create chain
        chain = topology.addChain()

        # determine residue adding function
        if self.forceField_str == 'TraPPE-UA':
            add_residue_function = self._add_residue_trappeua
        elif self.forceField_str == 'OPLS-AA':
            add_residue_function = self._add_residue_to_chain_oplsaa
        else:
            raise ValueError("Invalid force field.")

        # initialize previous residue atom
        prev_res_atom = None

        # add backbone
        for i, monomer in enumerate(self.backbone):

            # determine whether or not the residue is a terminal one
            left_ter = False
            right_ter = False
            if i == 0:
                left_ter = True
            if i == len(self.backbone) - 1:
                right_ter = True

            # add residue
            prev_res_atom = add_residue_function(topology, chain,
                                                 prev_res_atom, monomer,
                                                 left_ter, right_ter)

        # add branches
        for sequence, indices in self.branches:
            residues = list(chain.residues())
            for index in indices:
                atom = self._find_atom_by_name('C1', residues[index])
                atom.element = self.PHOSPHORUS
                prev_res_atom = atom
                for i, monomer in enumerate(sequence):
                    right_ter = i == len(sequence) - 1
                    prev_res_atom = add_residue_function(topology,
                                                         chain,
                                                         prev_res_atom,
                                                         monomer,
                                                         right_ter=right_ter)

        # create pdb file
        if self.pdb is None:
            positions = self._create_positions_trappeua()
            topology_pdb = Topology()
            topology_pdb._chains.append(chain)
            bc_num = 0
            dirname = os.path.join(os.path.dirname(__file__), "data")
            while os.path.exists(
                    os.path.join(dirname,
                                 "branched_chain_{}.pdb".format(bc_num))):
                bc_num += 1
            self.pdb = os.path.join(dirname,
                                    "branched_chain_{}.pdb".format(bc_num))
            PDBFile.writeFile(topology_pdb, positions, open(self.pdb, 'w'))

    def _create_positions_trappeua(self):

        positions = None

        # load positions for mA12
        mA12_pos = self._mA12_positions_trappeua()

        # initialize list mapping residue number to atom index
        res2atom = []

        # number of atoms in each monomer type
        Natoms_dict = {'A4': 9, 'A12': 17, 'mA12': 18}

        # add backbone positions
        for i, monomer in enumerate(self.backbone):

            # add atom indices to res2atom
            if res2atom:
                res2atom.append(
                    np.arange(Natoms_dict[monomer]) + res2atom[-1][-1] + 1)
            else:
                res2atom.append(np.arange(Natoms_dict[monomer]))

            # create monomer positions
            pos = mA12_pos[:]
            if monomer.startswith('A'):
                pos = np.delete(pos, 2, axis=0)
            if monomer.endswith('4'):
                pos = pos[:-8]

            # add monomer positions to chain positions
            if positions is None:
                positions = pos
            else:
                cos19 = np.cos(np.deg2rad(19.5))
                cos30 = np.cos(np.deg2rad(30.0))
                pos += 1.54 * np.array([cos19 * cos30, 0., 0.]) + np.array([
                    positions[res2atom[i - 1][1]][0], 0., 0.
                ]) - np.array([mA12_pos[0][0], 0., 0.])
                positions = np.vstack((positions, pos))

        # rotate methacrylate monomer positions
        Rz = np.array([[0., 1., 0.], [-1., 0., 0.], [0., 0., 1.]])
        mA12_pos_rot = mA12_pos.dot(Rz)

        # add side chain positions
        for sequence, indices in self.branches:
            for res_index in indices:
                prev_res_pos = positions[res2atom[res_index][1]]
                for monomer in sequence:

                    # create monomer positions
                    pos = mA12_pos_rot[:] + np.array([
                        positions[res2atom[res_index][1]][0] -
                        mA12_pos_rot[1][0], 0., 0.
                    ])
                    if monomer.startswith('A'):
                        pos = np.delete(pos, 2, axis=0)
                    if monomer.endswith('4'):
                        pos = pos[:-8]

                    # add monomer positions
                    cos19 = np.cos(np.deg2rad(19.5))
                    cos30 = np.cos(np.deg2rad(30.0))
                    pos += 1.54 * np.array([0., cos19 * cos30, 0.]) + np.array(
                        [0., prev_res_pos[1], 0.]) - np.array(
                            [0., pos[0][1], 0.])
                    positions = np.vstack((positions, pos))

                    prev_res_pos = pos[1]

        return positions

    @staticmethod
    def _mA12_positions_trappeua():

        # sines and cosines
        sin19 = np.sin(np.deg2rad(19.5))
        cos19 = np.cos(np.deg2rad(19.5))
        sin30 = np.sin(np.deg2rad(30.0))
        cos30 = np.cos(np.deg2rad(30.0))
        sin54 = np.sin(np.deg2rad(54.75))
        cos54 = np.cos(np.deg2rad(54.75))

        # methacrylate positions
        pos = 1.54 * np.array([cos19 * cos30, -cos19 * sin30, sin19])
        pos = np.vstack(
            (pos,
             pos + 1.54 * np.array([cos19 * cos30, cos19 * sin30, -sin19])))
        pos = np.vstack((pos, pos[1] + 1.54 * np.array([0., cos19, sin19])))
        pos = np.vstack((pos, pos[1] + 1.52 * np.array([0., 0., -1.])))
        pos = np.vstack((pos, pos[3] + 1.20 * np.array([0., cos30, -sin30])))
        pos = np.vstack((pos, pos[3] + 1.344 * np.array([0., -cos30, -sin30])))

        # C12 tail positions
        for i in range(12):
            if i == 0:
                pos = np.vstack(
                    (pos, pos[-1] +
                     1.41 * np.array([0.0, (-1)**(i + 1) * cos54, -sin54])))
            else:
                pos = np.vstack(
                    (pos, pos[-1] +
                     1.54 * np.array([0.0, (-1)**(i + 1) * cos54, -sin54])))

        return pos

    def _add_residue_trappeua(self,
                              topology,
                              chain,
                              prev_res_atom,
                              monomer,
                              left_ter=False,
                              right_ter=False):

        # determine monomer type
        if monomer.startswith('mA'):
            monomer_type = 'mA'
            is_methyl = True
        else:
            monomer_type = 'A'
            is_methyl = False

        # determine chain length
        end_chain_length = literal_eval(monomer.replace(monomer_type, ''))

        # add residue to topology
        residue = topology.addResidue(monomer, chain)

        # add first two carbons
        if left_ter and right_ter:
            c_element = self.NITROGEN
            c1_element = self.NITROGEN
        elif left_ter:
            c_element = self.BORON
            c1_element = self.CARBON
        elif right_ter:
            c_element = self.NITROGEN
            c1_element = self.CARBON
        else:
            c_element = self.CARBON
            c1_element = self.CARBON
        c = topology.addAtom('C', c_element, residue)
        c1 = topology.addAtom('C1', c1_element, residue)

        # add bond to previous residue if applicable
        if prev_res_atom is not None:
            topology.addBond(prev_res_atom, c)

        # add bond between first two carbons
        topology.addBond(c, c1)

        # add methyl group if methacryalte
        if is_methyl:
            cm = topology.addAtom('Cm', self.CARBON, residue)
            topology.addBond(c1, cm)

        # add ester group atoms
        c2 = topology.addAtom('C2', self.CARBON, residue)
        topology.addBond(c1, c2)
        oc = topology.addAtom('O', self.OXYGEN, residue)
        topology.addBond(c2, oc)
        oe = topology.addAtom('O1', self.OXYGEN, residue)
        topology.addBond(c2, oe)

        # add carbon chain
        prev_atom = oe
        for i in range(end_chain_length):
            curr_atom = topology.addAtom('C{}'.format(i + 3), self.CARBON,
                                         residue)
            topology.addBond(prev_atom, curr_atom)
            prev_atom = curr_atom

        return c1

    def _add_residue_oplsaa(self,
                            topology,
                            chain,
                            prev_res_atom,
                            monomer,
                            left_ter=False,
                            right_ter=False):
        raise NotImplementedError(
            "OPLS-AA not implemented for branched chain.")
示例#11
0
class ChainOptions(_Options):

    _SECTION_NAME = "Chain"

    # =========================================================================

    HYDROGEN = Element.getBySymbol('H')
    BORON = Element.getBySymbol('B')
    CARBON = Element.getBySymbol('C')
    NITROGEN = Element.getBySymbol('N')
    OXYGEN = Element.getBySymbol('O')

    # =========================================================================

    def __init__(self, topology_options):
        super(ChainOptions, self).__init__()
        self.topology_options = topology_options
        self.forceField_str = topology_options.forceField_str
        self.id = None
        self.num = 1
        self.sequence = None
        self.create_pdb = True
        self.overwrite_pdb = True
        self.instructions = None
        self.sequence_str = None

    def _create_options(self):
        super(ChainOptions, self)._create_options()
        self._OPTIONS['id'] = self._parse_id
        self._OPTIONS['num'] = self._parse_num
        self._OPTIONS['sequence'] = self._parse_sequence
        self._OPTIONS['createPDB'] = self._parse_create_pdb
        self._OPTIONS['overwritePDB'] = self._parse_overwrite_pdb
        self._OPTIONS['instructions'] = self._parse_instructions

    # =========================================================================

    def _check_for_incomplete_input(self):
        if self.sequence is None:
            self._incomplete_error('structure')

    # =====================================================================[====

    def _parse_id(self, *args):
        self.id = args[0]

    def _parse_num(self, *args):
        self.num = literal_eval(args[0])

    def _parse_sequence(self, *args):
        structure_parser = _StructureParser(args[0])
        self.sequence = structure_parser.get_chain_sequence()
        self.sequence_str = str(structure_parser)

    def _parse_create_pdb(self, *args):
        self.create_pdb = literal_eval(args[0])

    def _parse_overwrite_pdb(self, *args):
        self.overwrite_pdb = literal_eval(args[0])

    def _parse_instructions(self, *args):
        self.instructions = [
            instruction.strip() for instruction in args[0].split('/')
        ]

    # =========================================================================

    def add_chain_to_topology(self, topology):

        # Map chain id to sequence
        id_to_sequence = {}

        # Add specified number of chains
        for _ in range(self.num):

            # Initialize topology and positions array for creating chain pdb
            topology_pdb = Topology()
            positions = []

            # Determine chain pdb
            if self.id is not None:
                chain_id = "{}-{}".format(topology.getNumChains() + 1, self.id)
            else:
                chain_id = "{}-{}".format(topology.getNumChains() + 1,
                                          self.sequence_str)

            # Create chain
            chain = topology.addChain(id=chain_id)
            chain_pdb = topology_pdb.addChain(id=chain_id)

            # Add chain id and sequence to dictionary
            id_to_sequence[chain.id] = self.sequence_str

            # Initialize atom from previous residue
            prev_res_atom = None
            prev_res_atom_pdb = None

            # Initialize pos of atom on previous residue
            prev_res_atom_pos = np.array([0.0, 0.0, 0.0])

            # Iterate through sequence to add residues to chain
            for j in range(len(self.sequence)):

                # String representation of monomer
                monomer = self.sequence[j]

                # Determine whether or not the residue is a terminal one
                left_ter = False
                right_ter = False
                if j == 0:
                    left_ter = True
                if j == len(self.sequence) - 1:
                    right_ter = True

                # Add residue to chain
                if self.forceField_str == 'TraPPE-UA':
                    add_residue_function = self._add_residue_to_chain_trappeua
                elif self.forceField_str == 'OPLS-AA':
                    add_residue_function = self._add_residue_to_chain_oplsaa
                else:
                    raise ValueError("Invalid force field.")
                prev_res_atom, prev_res_atom_pdb, prev_res_atom_pos = add_residue_function(
                    topology, topology_pdb, chain, chain_pdb, prev_res_atom,
                    prev_res_atom_pdb, positions, prev_res_atom_pos, monomer,
                    left_ter, right_ter)

            # Create pdb file for chain
            if self.create_pdb:
                self._create_chain_pdb(topology_pdb, positions)

        return id_to_sequence

    def _add_residue_to_chain_oplsaa(self,
                                     topology,
                                     topology_pdb,
                                     chain,
                                     chain_pdb,
                                     prev_res_atom,
                                     prev_res_atom_pdb,
                                     positions,
                                     prev_res_atom_pos,
                                     monomer,
                                     left_ter=False,
                                     right_ter=False):
        # Determine monomer type
        if monomer.startswith('mA'):
            monomer_type = 'mA'
            is_methyl = True
        else:
            monomer_type = 'A'
            is_methyl = False

        # Determine chain length
        end_chain_length = literal_eval(monomer.replace(monomer_type, ''))

        # Determine residue id
        if left_ter:
            residue_id = "LEFTTER"
        elif right_ter:
            residue_id = "RIGHTTER"
        else:
            residue_id = None

        # Add residue to topology
        residue = topology.addResidue(monomer, chain, id=residue_id)
        residue_pdb = topology_pdb.addResidue(monomer,
                                              chain_pdb,
                                              id=residue_id)

        # Function to add atoms to topology
        def _add_atom_to_topology_oplsaa(name, element, counter):
            # Add atom to topology
            name = "{}{}".format(name, counter)
            atom = topology.addAtom(name, element, residue)
            atom_pdb = topology_pdb.addAtom(name, element, residue_pdb)

            # Return atoms and position
            return atom, atom_pdb, counter + 1

        # Import positions from pdb
        residue_positions = md.load(
            os.path.join(self.topology_options.data_directory,
                         "mA12_aa.pdb")).xyz[0] * 10. + prev_res_atom_pos
        residue_positions = list(residue_positions)
        if end_chain_length < 12:
            residue_positions = residue_positions[:-(12 - end_chain_length) *
                                                  3]
        if not is_methyl:
            residue_positions.pop(9)
            residue_positions.pop(8)
            residue_positions.pop(7)
        if not right_ter or (left_ter and right_ter):
            residue_positions.pop(5)
        if (left_ter and right_ter) or (not left_ter):
            residue_positions.pop(2)
        positions += residue_positions

        # Add first carbon and attached hydrogen atoms
        C_counter = 0
        H_counter = 0
        C0, C0_pdb, C_counter = _add_atom_to_topology_oplsaa(
            "C", self.CARBON, C_counter)
        if left_ter and not right_ter:
            num_H = 3
        else:
            num_H = 2
        for _ in range(num_H):
            H, H_pdb, H_counter = _add_atom_to_topology_oplsaa(
                "H", self.HYDROGEN, H_counter)
            self._add_bond_to_topology(H, H_pdb, C0, C0_pdb, topology,
                                       topology_pdb)

        # Add bond to previous residue if applicable
        if prev_res_atom is not None:
            self._add_bond_to_topology(C0, C0_pdb, prev_res_atom,
                                       prev_res_atom_pdb, topology,
                                       topology_pdb)

        # Add second carbon
        C1, C1_pdb, C_counter = _add_atom_to_topology_oplsaa(
            "C", self.CARBON, C_counter)
        self._add_bond_to_topology(C1, C1_pdb, C0, C0_pdb, topology,
                                   topology_pdb)
        if right_ter and not left_ter:
            H, H_pdb, H_counter = _add_atom_to_topology_oplsaa(
                "H", self.HYDROGEN, H_counter)
            self._add_bond_to_topology(H, H_pdb, C1, C1_pdb, topology,
                                       topology_pdb)

        # Add methyl group or hydrogen if not methyl
        if is_methyl:
            Cm, Cm_pdb, C_counter = _add_atom_to_topology_oplsaa(
                "C", self.CARBON, C_counter)
            for _ in range(3):
                H, H_pdb, H_counter = _add_atom_to_topology_oplsaa(
                    "H", self.HYDROGEN, H_counter)
                self._add_bond_to_topology(H, H_pdb, Cm, Cm_pdb, topology,
                                           topology_pdb)
            self._add_bond_to_topology(Cm, Cm_pdb, C1, C1_pdb, topology,
                                       topology_pdb)
        else:
            H, H_pdb, H_counter = _add_atom_to_topology_oplsaa(
                "H", self.HYDROGEN, H_counter)
            self._add_bond_to_topology(H, H_pdb, C1, C1_pdb, topology,
                                       topology_pdb)

        # Add carbonyl carbon
        Cc, Cc_pdb, C_counter = _add_atom_to_topology_oplsaa(
            "C", self.CARBON, C_counter)
        self._add_bond_to_topology(Cc, Cc_pdb, C1, C1_pdb, topology,
                                   topology_pdb)

        # Add carbonyl oxygen
        Oc, Oc_pdb, _, = _add_atom_to_topology_oplsaa("O", self.OXYGEN, 0)
        self._add_bond_to_topology(Oc, Oc_pdb, Cc, Cc_pdb, topology,
                                   topology_pdb)

        # Add ether oxygen
        Oe, Oe_pdb, _ = _add_atom_to_topology_oplsaa("O", self.OXYGEN, 1)
        self._add_bond_to_topology(Oe, Oe_pdb, Cc, Cc_pdb, topology,
                                   topology_pdb)

        # Add alkyl chain
        prev_atom = Oe
        prev_atom_pdb = Oe_pdb
        for _ in range(end_chain_length):
            curr_atom, curr_atom_pdb, C_counter = _add_atom_to_topology_oplsaa(
                "C", self.CARBON, C_counter)
            for _ in range(2):
                H, H_pdb, H_counter = _add_atom_to_topology_oplsaa(
                    "H", self.HYDROGEN, H_counter)
                self._add_bond_to_topology(H, H_pdb, curr_atom, curr_atom_pdb,
                                           topology, topology_pdb)
            self._add_bond_to_topology(curr_atom, curr_atom_pdb, prev_atom,
                                       prev_atom_pdb, topology, topology_pdb)
            prev_atom = curr_atom
            prev_atom_pdb = curr_atom_pdb

        # Add hydrogen to last carbon
        H, H_pdb, H_counter = _add_atom_to_topology_oplsaa(
            "H", self.HYDROGEN, H_counter)
        self._add_bond_to_topology(H, H_pdb, prev_atom, prev_atom_pdb,
                                   topology, topology_pdb)

        return C1, C1_pdb, prev_res_atom_pos + np.array([2.527, 0, 0])

    def _add_residue_to_chain_trappeua(self,
                                       topology,
                                       topology_pdb,
                                       chain,
                                       chain_pdb,
                                       prev_res_atom,
                                       prev_res_atom_pdb,
                                       positions,
                                       prev_res_atom_pos,
                                       monomer,
                                       left_ter=False,
                                       right_ter=False):
        def _deg_to_rad(ang_deg):
            return ang_deg * 2 * np.pi / 360.0

        # Sines and cosines
        sin19 = np.sin(_deg_to_rad(19.5))
        cos19 = np.cos(_deg_to_rad(19.5))
        sin30 = np.sin(_deg_to_rad(30.0))
        cos30 = np.cos(_deg_to_rad(30.0))
        sin54 = np.sin(_deg_to_rad(54.75))
        cos54 = np.cos(_deg_to_rad(54.75))

        # Determine monomer type
        if monomer.startswith('mA'):
            monomer_type = 'mA'
            is_methyl = True
        else:
            monomer_type = 'A'
            is_methyl = False

        # Determine chain length
        end_chain_length = literal_eval(monomer.replace(monomer_type, ''))

        # Determine residue id
        if left_ter:
            residue_id = "LEFTTER"
        elif right_ter:
            residue_id = "RIGHTTER"
        else:
            residue_id = None

        # Add residue to topology
        residue = topology.addResidue(monomer, chain, id=residue_id)
        residue_pdb = topology_pdb.addResidue(monomer,
                                              chain_pdb,
                                              id=residue_id)

        # Add first two carbons
        if left_ter and right_ter:
            carbon_element = self.NITROGEN
            carbon_1_element = self.NITROGEN
        elif left_ter:
            carbon_element = self.BORON
            carbon_1_element = self.CARBON
        elif right_ter:
            carbon_element = self.NITROGEN
            carbon_1_element = self.CARBON
        else:
            carbon_element = self.CARBON
            carbon_1_element = self.CARBON
        carbon_pos = prev_res_atom_pos + 1.54 * np.array(
            [cos19 * cos30, -cos19 * sin30, sin19])
        carbon, carbon_pdb = self._add_atom_to_topology_trappeua(
            'C', carbon_element, residue, residue_pdb, topology, topology_pdb,
            positions, carbon_pos)
        carbon_1_pos = carbon_pos + 1.54 * np.array(
            [cos19 * cos30, cos19 * sin30, -sin19])
        carbon_1, carbon_1_pdb = self._add_atom_to_topology_trappeua(
            'C1', carbon_1_element, residue, residue_pdb, topology,
            topology_pdb, positions, carbon_1_pos)

        # Add bond to previous residue if applicable
        if prev_res_atom is not None:
            self._add_bond_to_topology(carbon, carbon_pdb, prev_res_atom,
                                       prev_res_atom_pdb, topology,
                                       topology_pdb)

        # Add bond between first two carbons
        self._add_bond_to_topology(carbon, carbon_pdb, carbon_1, carbon_1_pdb,
                                   topology, topology_pdb)

        # Add methyl group if methacrylate monomer
        if is_methyl:
            carbon_methyl_pos = carbon_1_pos + 1.54 * np.array([0.0, 1.0, 0.0])
            carbon_methyl, carbon_methyl_pdb = self._add_atom_to_topology_trappeua(
                'Cm', self.CARBON, residue, residue_pdb, topology,
                topology_pdb, positions, carbon_methyl_pos)
            self._add_bond_to_topology(carbon_1, carbon_1_pdb, carbon_methyl,
                                       carbon_methyl_pdb, topology,
                                       topology_pdb)

        # Add ester group atoms
        carbon_2_pos = carbon_1_pos + 1.52 * np.array([0.0, 0.0, -1.0])
        carbon_2, carbon_2_pdb = self._add_atom_to_topology_trappeua(
            'C2', self.CARBON, residue, residue_pdb, topology, topology_pdb,
            positions, carbon_2_pos)
        self._add_bond_to_topology(carbon_1, carbon_1_pdb, carbon_2,
                                   carbon_2_pdb, topology, topology_pdb)
        oxygen_carbonyl_pos = carbon_2_pos + 1.20 * np.array(
            [0.0, cos30, -sin30])
        oxygen_carbonyl, oxygen_carbonyl_pdb = self._add_atom_to_topology_trappeua(
            'O', self.OXYGEN, residue, residue_pdb, topology, topology_pdb,
            positions, oxygen_carbonyl_pos)
        self._add_bond_to_topology(carbon_2, carbon_2_pdb, oxygen_carbonyl,
                                   oxygen_carbonyl_pdb, topology, topology_pdb)
        oxygen_ether_pos = carbon_2_pos + 1.344 * np.array(
            [0.0, -cos30, -sin30])
        oxygen_ether, oxygen_ether_pdb = self._add_atom_to_topology_trappeua(
            'O', self.OXYGEN, residue, residue_pdb, topology, topology_pdb,
            positions, oxygen_ether_pos)
        self._add_bond_to_topology(carbon_2, carbon_2_pdb, oxygen_ether,
                                   oxygen_ether_pdb, topology, topology_pdb)

        # Add carbon chain
        prev_atom_pos = oxygen_ether_pos
        prev_atom = oxygen_ether
        prev_atom_pdb = oxygen_ether_pdb
        for i in range(end_chain_length):
            if i == 0:
                curr_atom_pos = prev_atom_pos + 1.41 * np.array(
                    [0.0, (-1)**(i + 1) * cos54, -sin54])
            else:
                curr_atom_pos = prev_atom_pos + 1.54 * np.array(
                    [0.0, (-1)**(i + 1) * cos54, -sin54])
            curr_atom, curr_atom_pdb = self._add_atom_to_topology_trappeua(
                'C{}'.format(i + 3), self.CARBON, residue, residue_pdb,
                topology, topology_pdb, positions, curr_atom_pos)
            self._add_bond_to_topology(prev_atom, prev_atom_pdb, curr_atom,
                                       curr_atom_pdb, topology, topology_pdb)
            prev_atom_pos = curr_atom_pos
            prev_atom = curr_atom
            prev_atom_pdb = curr_atom_pdb

        return carbon_1, carbon_1_pdb, carbon_1_pos

    @staticmethod
    def _add_atom_to_topology_trappeua(name, element, residue, residue_pdb,
                                       topology, topology_pdb, positions,
                                       atom_pos):

        # Add atom to topology
        atom = topology.addAtom(name, element, residue)
        atom_pdb = topology_pdb.addAtom(name, element, residue_pdb)

        # Add position to array
        positions.append(atom_pos)

        # Return atoms and position
        return atom, atom_pdb

    @staticmethod
    def _add_bond_to_topology(atom1, atom1_pdb, atom2, atom2_pdb, topology,
                              topology_pdb):
        topology.addBond(atom1, atom2)
        topology_pdb.addBond(atom1_pdb, atom2_pdb)

    def _create_chain_pdb(self, topology_pdb, positions):
        dirname = os.path.dirname(__file__)
        filename = "{}.pdb".format(self.sequence_str)
        if self.forceField_str == 'OPLS-AA':
            filename = "{}_aa.pdb".format(self.sequence_str)
        file_path = os.path.join(dirname, "data/{}".format(filename))
        if not os.path.isfile(file_path) or self.overwrite_pdb:
            self.overwrite_pdb = False
            PDBFile.writeFile(topology_pdb, positions, open(file_path, 'w'))
    def _createTopology(self):
        """Build the topology of the system
        """
        top = Topology()
        positions = []
        velocities = []
        boxVectors = []

        #assume cell dimensions are set in the first file
        #the other molecules inherit the same cell
        conn = self._conn[0]
        self.pbc = False
        if self._hasTable('global_cell', self._tables[0]):
            for x, y, z in conn.execute('SELECT x, y, z FROM global_cell'):
                boxVectors.append(mm.Vec3(x, y, z))
            unitCellDimensions = [boxVectors[0][0], boxVectors[1][1], boxVectors[2][2]]
            top.setUnitCellDimensions(unitCellDimensions*angstrom)
            self.pbc = True
            
        #process each file
        nfiles = len(self._conn)
        for (fcounter, conn, tables) in zip(range(0,nfiles),self._conn,self._tables):
            """
            resdb = {}
            chaindb = {}
            """
            atoms = {}
            lastChain = None
            lastResId = None
            c = top.addChain()
            q = """SELECT id, name, anum, resname, resid, chain, x, y, z, vx, vy, vz
                FROM particle ORDER BY id"""
            for (atomId, atomName, atomNumber, resName, resId, chain, x, y, z, vx, vy, vz) in conn.execute(q):
                """
                #more elegant way to assign atoms to residues
                #but it works only if atoms in residues are contiguous
                #due to the fact that openmm does not support non-contiguous residues
                resuid = "%s:%s:%s" % (resName, resId, chain)
                if resuid in resdb.keys():
                    r = resdb[resuid]
                    c = chaindb[chain]
                else:
                    if chain in chaindb.keys():
                        c = chaindb[chain]
                    else:
                        c = top.addChain()
                        chaindb[chain] = c
                    r = top.addResidue(resName, c)
                    resdb[resuid] = r
                """ 

                newChain = False
                if chain != lastChain:
                    lastChain = chain
                    c = top.addChain()
                    newChain = True
                if resId != lastResId or newChain:
                    lastResId = resId
                    if resName in PDBFile._residueNameReplacements:
                        resName = PDBFile._residueNameReplacements[resName]
                    r = top.addResidue(resName, c)

                if resName in PDBFile._atomNameReplacements:
                    atomReplacements = PDBFile._atomNameReplacements[resName]
                else:
                    atomReplacements = {}

                if atomNumber == 0 and atomName.startswith('Vrt'):
                    elem = None
                else:
                    elem = Element.getByAtomicNumber(atomNumber)

                if atomName in atomReplacements:
                    atomName = atomReplacements[atomName]

                atoms[atomId] = top.addAtom(atomName, elem, r)
                positions.append(mm.Vec3(x, y, z))

                velocities.append(mm.Vec3(vx, vy, vz))

            self._natoms[fcounter] = len(atoms)

            for p0, p1 in conn.execute('SELECT p0, p1 FROM bond'):
                top.addBond(atoms[p0], atoms[p1])

        positions = positions*angstrom
        velocities = velocities*angstrom/picosecond
        return top, positions, velocities
    def _add_squalane_to_topology(self, topology):

        # Carbon element
        carbon_element = Element.getBySymbol('C')
        hydrogen_element = Element.getBySymbol('H')

        chain = topology.addChain(
            "{}-squalane".format(topology.getNumChains() + 1))
        residue = topology.addResidue("squalane", chain)
        prev_atom = None
        if self.forceField_str == "TraPPE-UA":
            atom_index = 0
            for _ in range(3):
                C1 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                if prev_atom is not None:
                    topology.addBond(prev_atom, C1)
                C2 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C1, C2)
                C3 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C2, C3)
                C4 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C2, C4)
                C5 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C4, C5)
                prev_atom = C5
            for _ in range(3):
                C1 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(prev_atom, C1)
                C2 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C1, C2)
                C3 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C2, C3)
                C4 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C3, C4)
                C5 = topology.addAtom("C{}".format(atom_index), carbon_element,
                                      residue)
                atom_index += 1
                topology.addBond(C3, C5)
                prev_atom = C5
        else:
            raise NotImplementedError("OPLS-AA not implemented for squalane")

        return chain.id