def testComputePBCVectors(self): """ Tests computing periodic box vectors """ deg90 = 90 * degrees vecs = computePeriodicBoxVectors(1, 2, 3, deg90, deg90, deg90) a, b, c = vecs self.assertAlmostEqual(a[0]/nanometers, 1) self.assertAlmostEqual(a[1]/nanometers, 0) self.assertAlmostEqual(a[2]/nanometers, 0) self.assertAlmostEqual(b[0]/nanometers, 0) self.assertAlmostEqual(b[1]/nanometers, 2) self.assertAlmostEqual(b[2]/nanometers, 0) self.assertAlmostEqual(c[0]/nanometers, 0) self.assertAlmostEqual(c[1]/nanometers, 0) self.assertAlmostEqual(c[2]/nanometers, 3) # Make sure round-trip works la, lb, lc, al, be, ga = computeLengthsAndAngles(vecs) self.assertAlmostEqual(la, 1) self.assertAlmostEqual(lb, 2) self.assertAlmostEqual(lc, 3) self.assertAlmostEqual(al, math.pi / 2) self.assertAlmostEqual(be, math.pi / 2) self.assertAlmostEqual(ga, math.pi / 2) # Now test a truncated octahedron. Can't do a simple round-trip though, # due to the reduced form. So test the *second* round-trip, which should # yield the same measurements vecs = computePeriodicBoxVectors(4.24388485, 4.24388485, 4.24388485, 109.4712190*degrees, 109.4712190*degrees, 109.4712190*degrees) la, lb, lc, al, be, ga = computeLengthsAndAngles(vecs) vecs2 = computePeriodicBoxVectors(la, lb, lc, al, be, ga) la2, lb2, lc2, al2, be2, ga2 = computeLengthsAndAngles(vecs2) # Now make sure that the round-trip worked self.assertAlmostEqual(strip_units(la), strip_units(la2)) self.assertAlmostEqual(strip_units(lb), strip_units(lb2)) self.assertAlmostEqual(strip_units(lc), strip_units(lc2)) self.assertAlmostEqual(strip_units(al), strip_units(al2)) self.assertAlmostEqual(strip_units(be), strip_units(be2)) self.assertAlmostEqual(strip_units(ga), strip_units(ga2)) # Check that the vectors are the same a1, a2, a3 = vecs b1, b2, b3 = vecs2 for x, y in zip(a1, b1): self.assertAlmostEqual(strip_units(x), strip_units(y)) for x, y in zip(a2, b2): self.assertAlmostEqual(strip_units(x), strip_units(y)) for x, y in zip(a3, b3): self.assertAlmostEqual(strip_units(x), strip_units(y))
def writeHeader(topology, file=sys.stdout): """Write out the header for a PDB file. Parameters ---------- topology : Topology The Topology defining the molecular system being written file : file=stdout A file to write the file to """ print("REMARK 1 CREATED WITH OPENMM %s, %s" % (Platform.getOpenMMVersion(), str(date.today())), file=file) vectors = topology.getPeriodicBoxVectors() if vectors is not None: a, b, c, alpha, beta, gamma = computeLengthsAndAngles(vectors) RAD_TO_DEG = 180/math.pi print("CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1 1 " % ( a*10, b*10, c*10, alpha*RAD_TO_DEG, beta*RAD_TO_DEG, gamma*RAD_TO_DEG), file=file)
def writeHeader(topology, file=sys.stdout, entry=None, keepIds=False): """Write out the header for a PDBx/mmCIF file. Parameters ---------- topology : Topology The Topology defining the molecular system being written file : file=stdout A file to write the file to entry : str=None The entry ID to assign to the CIF file keepIds : bool=False If True, keep the residue and chain IDs specified in the Topology rather than generating new ones. Warning: It is up to the caller to make sure these are valid IDs that satisfy the requirements of the PDBx/mmCIF format. Otherwise, the output file will be invalid. """ if entry is not None: print('data_%s' % entry, file=file) else: print('data_cell', file=file) print("# Created with OpenMM %s, %s" % (Platform.getOpenMMVersion(), str(date.today())), file=file) print('#', file=file) vectors = topology.getPeriodicBoxVectors() if vectors is not None: a, b, c, alpha, beta, gamma = computeLengthsAndAngles(vectors) RAD_TO_DEG = 180 / math.pi print('_cell.length_a %10.4f' % (a * 10), file=file) print('_cell.length_b %10.4f' % (b * 10), file=file) print('_cell.length_c %10.4f' % (c * 10), file=file) print('_cell.angle_alpha %10.4f' % (alpha * RAD_TO_DEG), file=file) print('_cell.angle_beta %10.4f' % (beta * RAD_TO_DEG), file=file) print('_cell.angle_gamma %10.4f' % (gamma * RAD_TO_DEG), file=file) print('#', file=file) # Identify bonds that should be listed in the file. bonds = [] for atom1, atom2 in topology.bonds(): if atom1.residue.name not in PDBFile._standardResidues or atom2.residue.name not in PDBFile._standardResidues: bonds.append((atom1, atom2)) elif atom1.name == 'SG' and atom2.name == 'SG' and atom1.residue.name == 'CYS' and atom2.residue.name == 'CYS': bonds.append((atom1, atom2)) if len(bonds) > 0: # Write the bond information. print('loop_', file=file) print('_struct_conn.id', file=file) print('_struct_conn.conn_type_id', file=file) print('_struct_conn.ptnr1_label_asym_id', file=file) print('_struct_conn.ptnr1_label_comp_id', file=file) print('_struct_conn.ptnr1_label_seq_id', file=file) print('_struct_conn.ptnr1_label_atom_id', file=file) print('_struct_conn.ptnr2_label_asym_id', file=file) print('_struct_conn.ptnr2_label_comp_id', file=file) print('_struct_conn.ptnr2_label_seq_id', file=file) print('_struct_conn.ptnr2_label_atom_id', file=file) chainIds = {} resIds = {} if keepIds: for chain in topology.chains(): chainIds[chain] = chain.id for res in topology.residues(): resIds[res] = res.id else: for (chainIndex, chain) in enumerate(topology.chains()): chainIds[chain] = chr(ord('A') + chainIndex % 26) for (resIndex, res) in enumerate(chain.residues()): resIds[res] = resIndex + 1 for i, (atom1, atom2) in enumerate(bonds): if atom1.residue.name == 'CYS' and atom2.residue.name == 'CYS': bondType = 'disulf' else: bondType = 'covale' line = "bond%d %s %s %-4s %5s %-4s %s %-4s %5s %-4s" print(line % (i + 1, bondType, chainIds[atom1.residue.chain], atom1.residue.name, resIds[atom1.residue], atom1.name, chainIds[atom2.residue.chain], atom2.residue.name, resIds[atom2.residue], atom2.name), file=file) print('#', file=file) # Write the header for the atom coordinates. print('loop_', file=file) print('_atom_site.group_PDB', file=file) print('_atom_site.id', file=file) print('_atom_site.type_symbol', file=file) print('_atom_site.label_atom_id', file=file) print('_atom_site.label_alt_id', file=file) print('_atom_site.label_comp_id', file=file) print('_atom_site.label_asym_id', file=file) print('_atom_site.label_entity_id', file=file) print('_atom_site.label_seq_id', file=file) print('_atom_site.pdbx_PDB_ins_code', file=file) print('_atom_site.Cartn_x', file=file) print('_atom_site.Cartn_y', file=file) print('_atom_site.Cartn_z', file=file) print('_atom_site.occupancy', file=file) print('_atom_site.B_iso_or_equiv', file=file) print('_atom_site.Cartn_x_esd', file=file) print('_atom_site.Cartn_y_esd', file=file) print('_atom_site.Cartn_z_esd', file=file) print('_atom_site.occupancy_esd', file=file) print('_atom_site.B_iso_or_equiv_esd', file=file) print('_atom_site.pdbx_formal_charge', file=file) print('_atom_site.auth_seq_id', file=file) print('_atom_site.auth_comp_id', file=file) print('_atom_site.auth_asym_id', file=file) print('_atom_site.auth_atom_id', file=file) print('_atom_site.pdbx_PDB_model_num', file=file)
def writeModel(self, positions, unitCellDimensions=None, periodicBoxVectors=None): """Write out a model to the DCD file. The periodic box can be specified either by the unit cell dimensions (for a rectangular box), or the full set of box vectors (for an arbitrary triclinic box). If neither is specified, the box vectors specified in the Topology will be used. Regardless of the value specified, no dimensions will be written if the Topology does not represent a periodic system. Parameters ---------- positions : list The list of atomic positions to write unitCellDimensions : Vec3=None The dimensions of the crystallographic unit cell. periodicBoxVectors : tuple of Vec3=None The vectors defining the periodic box. """ if len(list(self._topology.atoms())) != len(positions): raise ValueError('The number of positions must match the number of atoms') if is_quantity(positions): positions = positions.value_in_unit(nanometers) if any(math.isnan(norm(pos)) for pos in positions): raise ValueError('Particle position is NaN') if any(math.isinf(norm(pos)) for pos in positions): raise ValueError('Particle position is infinite') file = self._file self._modelCount += 1 if self._interval > 1 and self._firstStep+self._modelCount*self._interval > 1<<31: # This will exceed the range of a 32 bit integer. To avoid crashing or producing a corrupt file, # update the header to say the trajectory consisted of a smaller number of larger steps (so the # total trajectory length remains correct). self._firstStep //= self._interval self._dt *= self._interval self._interval = 1 file.seek(0, os.SEEK_SET) file.write(struct.pack('<i4c9if', 84, b'C', b'O', b'R', b'D', 0, self._firstStep, self._interval, 0, 0, 0, 0, 0, 0, self._dt)) # Update the header. file.seek(8, os.SEEK_SET) file.write(struct.pack('<i', self._modelCount)) file.seek(20, os.SEEK_SET) file.write(struct.pack('<i', self._firstStep+self._modelCount*self._interval)) # Write the data. file.seek(0, os.SEEK_END) boxVectors = self._topology.getPeriodicBoxVectors() if boxVectors is not None: if periodicBoxVectors is not None: boxVectors = periodicBoxVectors elif unitCellDimensions is not None: if is_quantity(unitCellDimensions): unitCellDimensions = unitCellDimensions.value_in_unit(nanometers) boxVectors = (Vec3(unitCellDimensions[0], 0, 0), Vec3(0, unitCellDimensions[1], 0), Vec3(0, 0, unitCellDimensions[2]))*nanometers (a_length, b_length, c_length, alpha, beta, gamma) = computeLengthsAndAngles(boxVectors) a_length = a_length * 10. # computeLengthsAndAngles returns unitless nanometers, but need angstroms here. b_length = b_length * 10. # computeLengthsAndAngles returns unitless nanometers, but need angstroms here. c_length = c_length * 10. # computeLengthsAndAngles returns unitless nanometers, but need angstroms here. angle1 = math.sin(math.pi/2-gamma) angle2 = math.sin(math.pi/2-beta) angle3 = math.sin(math.pi/2-alpha) file.write(struct.pack('<i6di', 48, a_length, angle1, b_length, angle2, angle3, c_length, 48)) length = struct.pack('<i', 4*len(positions)) for i in range(3): file.write(length) data = array.array('f', (10*x[i] for x in positions)) data.tofile(file) file.write(length) try: file.flush() except AttributeError: pass