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
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
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
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 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
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
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." )
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.")
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