def report(self, simulation, state): """ Generate a report. Parameters: - simulation (Simulation) The Simulation to generate a report for - state (State) The current state of the simulation """ from parmed.amber.asciicrd import VELSCALE global VELUNIT, FRCUNIT if self.crds: crds = state.getPositions().value_in_unit(u.angstrom) elif self.vels: # crds/vels/frcs are exclusive, elif works vels = state.getVelocities().value_in_unit(VELUNIT) elif self.frcs: frcs = state.getForces().value_in_unit(FRCUNIT) if self._out is None: # This must be the first frame, so set up the trajectory now if self.crds: self.atom = len(crds) elif self.vels: self.atom = len(vels) elif self.frcs: self.atom = len(frcs) self.uses_pbc = simulation.topology.getUnitCellDimensions() is not None self._out = AmberMdcrd(self.fname, self.atom, self.uses_pbc, title="ParmEd-created trajectory using OpenMM", mode='w') # Add the coordinates, velocities, and/or forces as needed if self.crds: flatcrd = [0 for i in range(self.atom*3)] for i in range(self.atom): i3 = i*3 flatcrd[i3], flatcrd[i3+1], flatcrd[i3+2] = crds[i] self._out.add_coordinates(flatcrd) if self.vels: # Divide by the scaling factor (works if vels is a list of Vec3's) # This is necessary since AmberMdcrd does not scale before writing # (since it expects coordinates) vels = [v / VELSCALE for v in vels] flatvel = [0 for i in range(self.atom*3)] for i in range(self.atom): i3 = i*3 flatvel[i3], flatvel[i3+1], flatvel[i3+2] = vels[i] self._out.add_coordinates(flatvel) if self.frcs: flatfrc = [0 for i in range(self.atom*3)] for i in range(self.atom): i3 = i*3 flatfrc[i3], flatfrc[i3+1], flatfrc[i3+2] = frcs[i] self._out.add_coordinates(flatfrc) # Now it's time to add the box lengths if self.uses_pbc: boxvecs = state.getPeriodicBoxVectors() lengths, angles = box_vectors_to_lengths_and_angles(*boxvecs) self._out.add_box(lengths.value_in_unit(u.angstroms))
def report(self, simulation, state): """Generate a report. Parameters ---------- simulation : :class:`app.Simulation` The Simulation to generate a report for state : :class:`mm.State` The current state of the simulation """ global VELUNIT, FRCUNIT if self.crds: crds = state.getPositions().value_in_unit(u.angstrom) if self.vels: vels = state.getVelocities().value_in_unit(VELUNIT) if self.frcs: frcs = state.getForces().value_in_unit(FRCUNIT) if self.protocolWork: protocolWork = simulation.integrator.get_protocol_work(dimensionless=True) if self.alchemicalLambda: alchemicalLambda = simulation.integrator.getGlobalVariableByName('lambda') if self._out is None: # This must be the first frame, so set up the trajectory now if self.crds: atom = len(crds) elif self.vels: atom = len(vels) elif self.frcs: atom = len(frcs) self.uses_pbc = simulation.topology.getUnitCellDimensions() is not None self._out = NetCDF4Traj.open_new( self.fname, atom, self.uses_pbc, self.crds, self.vels, self.frcs, title="ParmEd-created trajectory using OpenMM", protocolWork=self.protocolWork, alchemicalLambda=self.alchemicalLambda, ) if self.uses_pbc: vecs = state.getPeriodicBoxVectors() lengths, angles = box_vectors_to_lengths_and_angles(*vecs) self._out.add_cell_lengths_angles(lengths.value_in_unit(u.angstrom), angles.value_in_unit(u.degree)) # Add the coordinates, velocities, and/or forces as needed if self.crds: self._out.add_coordinates(crds) if self.vels: # The velocities get scaled right before writing self._out.add_velocities(vels) if self.frcs: self._out.add_forces(frcs) if self.protocolWork: self._out.add_protocolWork(protocolWork) if self.alchemicalLambda: self._out.add_alchemicalLambda(alchemicalLambda) # Now it's time to add the time. self._out.add_time(state.getTime().value_in_unit(u.picosecond))
def testBoxLengthsVectors(self): """ Test converting box lengths/angles to vectors and back again """ a, b, c = geo.box_lengths_and_angles_to_vectors(1, 1, 1, 90, 90, 90) self.assertEqualVectors(a, [1.0, 0.0, 0.0] * u.angstroms) self.assertEqualVectors(b, [0.0, 1.0, 0.0] * u.angstroms) self.assertEqualVectors(c, [0.0, 0.0, 1.0] * u.angstroms) ang = 109.475 rad = ang * math.pi / 180 a,b,c = geo.box_lengths_and_angles_to_vectors(50, 50, 50, ang, ang, ang) leng, ang = geo.box_vectors_to_lengths_and_angles(a, b, c) self.assertEqualVectors(leng, (50, 50, 50)) self.assertEqualVectors(ang, (rad, rad, rad))
def report(self, sim, state): """Generate a report. Parameters ---------- sim : :class:`app.Simulation` The Simulation to generate a report for state : :class:`mm.State` The current state of the simulation """ global VELUNIT crds = state.getPositions().value_in_unit(u.angstrom) if self.rst7 is None: self.uses_pbc = sim.topology.getUnitCellDimensions() is not None self.atom = len(crds) # First time written self.rst7 = Rst7(natom=self.atom, title='Restart file written by ParmEd with OpenMM') self.rst7.time = state.getTime().value_in_unit(u.picosecond) flatcrd = [0.0 for i in range(self.atom*3)] for i in range(self.atom): i3 = i*3 flatcrd[i3], flatcrd[i3+1], flatcrd[i3+2] = crds[i] self.rst7.coordinates = flatcrd if self.write_velocities: vels = state.getVelocities().value_in_unit(VELUNIT) flatvel = [0.0 for i in range(self.atom*3)] for i in range(self.atom): i3 = i*3 flatvel[i3], flatvel[i3+1], flatvel[i3+2] = vels[i] self.rst7.vels = flatvel if self.uses_pbc: boxvecs = state.getPeriodicBoxVectors() lengths, angles = box_vectors_to_lengths_and_angles(*boxvecs) lengths = lengths.value_in_unit(u.angstrom) angles = angles.value_in_unit(u.degree) self.rst7.box = [lengths[0], lengths[1], lengths[2], angles[0], angles[1], angles[2]] if self.write_multiple: fname = self.fname + '.%d' % sim.currentStep else: fname = self.fname self.rst7.write(fname, self.netcdf)
def report(self, simulation, state): """Generate a report. Parameters ---------- simulation : :class:`app.Simulation` The Simulation to generate a report for state : :class:`mm.State` The current state of the simulation """ global VELUNIT, FRCUNIT if self.crds: crds = state.getPositions().value_in_unit(u.angstrom) if self.vels: vels = state.getVelocities().value_in_unit(VELUNIT) if self.frcs: frcs = state.getForces().value_in_unit(FRCUNIT) if self._out is None: # This must be the first frame, so set up the trajectory now if self.crds: atom = len(crds) elif self.vels: atom = len(vels) elif self.frcs: atom = len(frcs) self.uses_pbc = simulation.topology.getUnitCellDimensions() is not None self._out = NetCDFTraj.open_new( self.fname, atom, self.uses_pbc, self.crds, self.vels, self.frcs, title="ParmEd-created trajectory using OpenMM" ) if self.uses_pbc: vecs = state.getPeriodicBoxVectors() lengths, angles = box_vectors_to_lengths_and_angles(*vecs) self._out.add_cell_lengths_angles(lengths.value_in_unit(u.angstrom), angles.value_in_unit(u.degree)) # Add the coordinates, velocities, and/or forces as needed if self.crds: self._out.add_coordinates(crds) if self.vels: # The velocities get scaled right before writing self._out.add_velocities(vels) if self.frcs: self._out.add_forces(frcs) # Now it's time to add the time. self._out.add_time(state.getTime().value_in_unit(u.picosecond))
def __init__(self, state): self.coordinates = self._get_data(state, 'getPositions', u.angstrom, (1, -1, 3), asNumpy=True) self.velocities = self._get_data(state, 'getVelocities', u.angstrom/u.picosecond, (1, -1, 3), asNumpy=True) self.forces = self._get_data(state, 'getForces', u.kilocalorie_per_mole/u.angstrom, (1, -1, 3), asNumpy=True) self.energy = self._get_data(state, 'getPotentialEnergy', u.kilocalorie_per_mole) self.time = self._get_data(state, 'getTime', u.picosecond) box = self._get_data(state, 'getPeriodicBoxVectors', u.angstroms, (3, 3), asNumpy=True) if box is not None: leng, ang = box_vectors_to_lengths_and_angles(*box) leng = leng.value_in_unit(u.angstrom) ang = ang.value_in_unit(u.degree) self.box = np.array(list(leng) + list(ang)) else: self.box = None
def parse(filename): """ Parses a Gromacs GRO file Parameters ---------- filename : str or file-like Name of the file or the GRO file object Returns ------- struct : :class:`Structure` The Structure instance instantiated with *just* residues and atoms populated (with coordinates) """ struct = Structure() if isinstance(filename, string_types): fileobj = genopen(filename, 'r') own_handle = True else: fileobj = filename own_handle = False try: # Ignore the title line fileobj.readline() try: natom = int(fileobj.readline().strip()) except ValueError: raise GromacsError('Could not parse %s as GRO file' % filename) digits = None for i, line in enumerate(fileobj): if i == natom: break try: resnum = int(line[:5]) resname = line[5:10].strip() atomname = line[10:15].strip() elem = element_by_name(atomname) atomic_number = AtomicNum[elem] mass = Mass[elem] atnum = int(line[15:20]) if atomic_number == 0: atom = ExtraPoint(name=atomname, number=atnum) else: atom = Atom(atomic_number=atomic_number, name=atomname, number=atnum, mass=mass) if digits is None: pdeci = line.index('.', 20) ndeci = line.index('.', pdeci+1) digits = ndeci - pdeci atom.xx, atom.xy, atom.xz = ( float(line[20+i*digits:20+(i+1)*digits])*10 for i in range(3) ) i = 4 wbeg = (pdeci-4)+(5+ndeci)*(i-1) wend = (pdeci-4)+(5+ndeci)*i if line[wbeg:wend].strip(): atom.vx, atom.vy, atom.vz = ( float(line[(pdeci-3)+(6+ndeci)*i: (pdeci-3)+(6+ndeci)*(i+1)])*10 for i in range(3, 6) ) except (ValueError, IndexError): raise GromacsError('Could not parse the atom record of ' 'GRO file %s' % filename) struct.add_atom(atom, resname, resnum) # Get the box from the last line if it's present if line.strip(): try: box = [float(x) for x in line.split()] except ValueError: raise GromacsError('Could not understand box line of GRO ' 'file %s' % filename) if len(box) == 3: struct.box = [box[0]*10, box[1]*10, box[2]*10, 90.0, 90.0, 90.0] elif len(box) == 9: # Assume we have vectors leng, ang = box_vectors_to_lengths_and_angles( [box[0], box[3], box[4]]*u.nanometers, [box[5], box[1], box[6]]*u.nanometers, [box[7], box[8], box[2]]*u.nanometers) a, b, c = leng.value_in_unit(u.angstroms) alpha, beta, gamma = ang.value_in_unit(u.degrees) struct.box = [a, b, c, alpha, beta, gamma] finally: if own_handle: fileobj.close() return struct
def load_topology(topology, system=None): """ Creates a :class:`parmed.structure.Structure` instance from an OpenMM Topology, optionally filling in parameters from a System Parameters ---------- topology : :class:`simtk.openmm.app.Topology` The Topology instance with the list of atoms and bonds for this system system : :class:`simtk.openmm.System` or str, optional If provided, parameters from this System will be applied to the Structure. If a string is given, it will be interpreted as the file name of an XML-serialized System, and it will be deserialized into a System before used to supply parameters Returns ------- struct : :class:`Structure <parmed.structure.Structure>` The structure from the provided topology Raises ------ OpenMMWarning if parameters are found that cannot be interpreted or processed by ParmEd TypeError if there are any mismatches between the provided topology and system (e.g., they have different numbers of atoms) IOError if system is a string and it is not an existing file Notes ----- Due to its flexibility with CustomForces, it is entirely possible that the functional form of the potential will be unknown to ParmEd. This function will try to use the energy expression to identify supported potential types that are implemented as CustomForce objects. In particular, quadratic improper torsions, when recognized, will be extracted. Other CustomForces, including the CustomNonbondedForce used to implement NBFIX (off-diagonal L-J modifications) and the 12-6-4 potential, will not be processed and will result in an unknown functional form """ struct = Structure() atommap = dict() for c in topology.chains(): chain = c.id for r in c.residues(): residue = r.name resid = r.index for a in r.atoms(): if a.element is None: atom = ExtraPoint(name=a.name) else: atom = Atom(atomic_number=a.element.atomic_number, name=a.name, mass=a.element.mass) struct.add_atom(atom, residue, resid, chain) atommap[a] = atom for a1, a2 in topology.bonds(): struct.bonds.append(Bond(atommap[a1], atommap[a2])) vectors = topology.getPeriodicBoxVectors() if vectors is not None: leng, ang = box_vectors_to_lengths_and_angles(*vectors) leng = leng.value_in_unit(u.angstroms) ang = ang.value_in_unit(u.degrees) struct.box = [leng[0], leng[1], leng[2], ang[0], ang[1], ang[2]] if struct.box is not None: struct.box = create_array(struct.box) if system is None: return struct if isinstance(system, string_types): with open(system, 'r') as f: system = mm.XmlSerializer.deserialize(f.read()) # We have a system, try to extract parameters from it if len(struct.atoms) != system.getNumParticles(): raise TypeError('Topology and System have different numbers of atoms ' '(%d vs. %d)' % (len(struct.atoms), system.getNumParticles())) processed_forces = set() ignored_forces = (mm.CMMotionRemover, mm.AndersenThermostat, mm.MonteCarloBarostat, mm.MonteCarloAnisotropicBarostat, mm.MonteCarloMembraneBarostat, mm.CustomExternalForce, mm.GBSAOBCForce, mm.CustomGBForce) if system.usesPeriodicBoundaryConditions(): vectors = system.getDefaultPeriodicBoxVectors() leng, ang = box_vectors_to_lengths_and_angles(*vectors) leng = leng.value_in_unit(u.angstroms) ang = ang.value_in_unit(u.degrees) struct.box = create_array( [leng[0], leng[1], leng[2], ang[0], ang[1], ang[2]] ) else: struct.box = None for force in system.getForces(): if isinstance(force, mm.HarmonicBondForce): if mm.HarmonicBondForce in processed_forces: # Try to process this HarmonicBondForce as a Urey-Bradley term _process_urey_bradley(struct, force) else: _process_bond(struct, force) elif isinstance(force, mm.HarmonicAngleForce): _process_angle(struct, force) elif isinstance(force, mm.PeriodicTorsionForce): _process_dihedral(struct, force) elif isinstance(force, mm.RBTorsionForce): _process_rbtorsion(struct, force) elif isinstance(force, mm.CustomTorsionForce): if not _process_improper(struct, force): struct.unknown_functional = True warnings.warn('Unknown functional form of CustomTorsionForce', OpenMMWarning) elif isinstance(force, mm.CMAPTorsionForce): _process_cmap(struct, force) elif isinstance(force, mm.NonbondedForce): _process_nonbonded(struct, force) elif isinstance(force, ignored_forces): continue else: struct.unknown_functional = True warnings.warn('Unsupported Force type %s' % type(force).__name__, OpenMMWarning) processed_forces.add(type(force)) return struct
def parse(filename, skip_bonds=False): """ Parses a Gromacs GRO file Parameters ---------- filename : str or file-like Name of the file or the GRO file object skip_bonds : bool, optional If True, skip trying to assign bonds. This can save substantial time when parsing large files with non-standard residue names. However, no bonds are assigned. This is OK if, for instance, the GRO file is being parsed simply for its coordinates. This will also reduce the accuracy of assigned atomic numbers for typical ions. Default is False. Returns ------- struct : :class:`Structure` The Structure instance instantiated with *just* residues and atoms populated (with coordinates) """ struct = Structure() if isinstance(filename, string_types): fileobj = genopen(filename, 'r') own_handle = True else: fileobj = filename own_handle = False try: # Ignore the title line fileobj.readline() try: natom = int(fileobj.readline().strip()) except ValueError: raise GromacsError('Could not parse %s as GRO file' % filename) line_parser = _AtomLineParser() for i, line in enumerate(fileobj): if i == natom: break try: atom, resname, resnum = line_parser.read(line) except (ValueError, IndexError): raise GromacsError('Could not parse the atom record of ' 'GRO file %s' % filename) struct.add_atom(atom, resname, resnum) else: # If no box exists, the break did not hit, so line still # contains the last atom (which cannot be interpreted as a box). # This wipes out line (IFF fileobj reached the line) line = fileobj.readline() if i+1 != natom: raise GromacsError('Truncated GRO file. Found %d of %d ' 'atoms' % (i+1, natom)) # Get the box from the last line if it's present if line.strip(): try: box = [float(x) for x in line.split()] except ValueError: raise GromacsError('Could not understand box line of GRO ' 'file %s' % filename) if len(box) == 3: struct.box = [box[0]*10, box[1]*10, box[2]*10, 90.0, 90.0, 90.0] elif len(box) == 9: # Assume we have vectors leng, ang = box_vectors_to_lengths_and_angles( [box[0], box[3], box[4]]*u.nanometers, [box[5], box[1], box[6]]*u.nanometers, [box[7], box[8], box[2]]*u.nanometers) a, b, c = leng.value_in_unit(u.angstroms) alpha, beta, gamma = ang.value_in_unit(u.degrees) struct.box = [a, b, c, alpha, beta, gamma] finally: if own_handle: fileobj.close() # Assign bonds (and improved element guesses) if not skip_bonds: struct.assign_bonds() return struct
def load_topology(topology, system=None, xyz=None, box=None): """ Creates a :class:`parmed.structure.Structure` instance from an OpenMM Topology, optionally filling in parameters from a System Parameters ---------- topology : :class:`simtk.openmm.app.Topology` The Topology instance with the list of atoms and bonds for this system system : :class:`simtk.openmm.System` or str, optional If provided, parameters from this System will be applied to the Structure. If a string is given, it will be interpreted as the file name of an XML-serialized System, and it will be deserialized into a System before used to supply parameters xyz : str or array of float Name of a file containing coordinate information or an array of coordinates. If file has unit cell information, it also uses that information unless ``box`` (below) is also specified box : array of 6 floats Unit cell dimensions Returns ------- struct : :class:`Structure <parmed.structure.Structure>` The structure from the provided topology Raises ------ OpenMMWarning if parameters are found that cannot be interpreted or processed by ParmEd TypeError if there are any mismatches between the provided topology and system (e.g., they have different numbers of atoms) IOError if system is a string and it is not an existing file Notes ----- Due to its flexibility with CustomForces, it is entirely possible that the functional form of the potential will be unknown to ParmEd. This function will try to use the energy expression to identify supported potential types that are implemented as CustomForce objects. In particular, quadratic improper torsions, when recognized, will be extracted. Other CustomForces, including the CustomNonbondedForce used to implement NBFIX (off-diagonal L-J modifications) and the 12-6-4 potential, will not be processed and will result in an unknown functional form """ import simtk.openmm as mm struct = Structure() atommap = dict() for c in topology.chains(): chain = c.id for r in c.residues(): residue = r.name resid = r.index for a in r.atoms(): if a.element is None: atom = ExtraPoint(name=a.name) else: atom = Atom(atomic_number=a.element.atomic_number, name=a.name, mass=a.element.mass) struct.add_atom(atom, residue, resid, chain) atommap[a] = atom for a1, a2 in topology.bonds(): struct.bonds.append(Bond(atommap[a1], atommap[a2])) vectors = topology.getPeriodicBoxVectors() if vectors is not None: leng, ang = box_vectors_to_lengths_and_angles(*vectors) leng = leng.value_in_unit(u.angstroms) ang = ang.value_in_unit(u.degrees) struct.box = [leng[0], leng[1], leng[2], ang[0], ang[1], ang[2]] loaded_box = False if xyz is not None: if isinstance(xyz, string_types): xyz = load_file(xyz, skip_bonds=True) struct.coordinates = xyz.coordinates if struct.box is not None: if xyz.box is not None: loaded_box = True struct.box = xyz.box else: struct.coordinates = xyz if box is not None: loaded_box = True struct.box = box if struct.box is not None: struct.box = np.asarray(struct.box) if system is None: return struct if isinstance(system, string_types): system = load_file(system) if not isinstance(system, mm.System): raise TypeError('system must be an OpenMM System object or serialized ' 'XML of an OpenMM System object') # We have a system, try to extract parameters from it if len(struct.atoms) != system.getNumParticles(): raise TypeError('Topology and System have different numbers of atoms ' '(%d vs. %d)' % (len(struct.atoms), system.getNumParticles())) processed_forces = set() ignored_forces = (mm.CMMotionRemover, mm.AndersenThermostat, mm.MonteCarloBarostat, mm.MonteCarloAnisotropicBarostat, mm.MonteCarloMembraneBarostat, mm.CustomExternalForce, mm.GBSAOBCForce, mm.CustomGBForce) if system.usesPeriodicBoundaryConditions(): if not loaded_box: vectors = system.getDefaultPeriodicBoxVectors() leng, ang = box_vectors_to_lengths_and_angles(*vectors) leng = leng.value_in_unit(u.angstroms) ang = ang.value_in_unit(u.degrees) struct.box = np.asarray( [leng[0], leng[1], leng[2], ang[0], ang[1], ang[2]]) else: struct.box = None for force in system.getForces(): if isinstance(force, mm.HarmonicBondForce): if mm.HarmonicBondForce in processed_forces: # Try to process this HarmonicBondForce as a Urey-Bradley term _process_urey_bradley(struct, force) else: _process_bond(struct, force) elif isinstance(force, mm.HarmonicAngleForce): _process_angle(struct, force) elif isinstance(force, mm.PeriodicTorsionForce): _process_dihedral(struct, force) elif isinstance(force, mm.RBTorsionForce): _process_rbtorsion(struct, force) elif isinstance(force, mm.CustomTorsionForce): if not _process_improper(struct, force): struct.unknown_functional = True warnings.warn('Unknown functional form of CustomTorsionForce', OpenMMWarning) elif isinstance(force, mm.CMAPTorsionForce): _process_cmap(struct, force) elif isinstance(force, mm.NonbondedForce): _process_nonbonded(struct, force) elif isinstance(force, ignored_forces): continue else: struct.unknown_functional = True warnings.warn('Unsupported Force type %s' % type(force).__name__, OpenMMWarning) processed_forces.add(type(force)) return struct
def parse(filename): """ Parses a Gromacs GRO file Parameters ---------- filename : str or file-like Name of the file or the GRO file object Returns ------- struct : :class:`Structure` The Structure instance instantiated with *just* residues and atoms populated (with coordinates) """ struct = Structure() if isinstance(filename, string_types): fileobj = genopen(filename, 'r') own_handle = True else: fileobj = filename own_handle = False try: # Ignore the title line fileobj.readline() try: natom = int(fileobj.readline().strip()) except ValueError: raise GromacsError('Could not parse %s as GRO file' % filename) digits = None line_parser = _AtomLineParser() for i, line in enumerate(fileobj): if i == natom: break try: atom, resname, resnum = line_parser.read(line) except (ValueError, IndexError): raise GromacsError('Could not parse the atom record of ' 'GRO file %s' % filename) struct.add_atom(atom, resname, resnum) else: # If no box exists, the break did not hit, so line still # contains the last atom (which cannot be interpreted as a box). # This wipes out line (IFF fileobj reached the line) line = fileobj.readline() if i+1 != natom: raise GromacsError('Truncated GRO file. Found %d of %d ' 'atoms' % (i+1, natom)) # Get the box from the last line if it's present if line.strip(): try: box = [float(x) for x in line.split()] except ValueError: raise GromacsError('Could not understand box line of GRO ' 'file %s' % filename) if len(box) == 3: struct.box = [box[0]*10, box[1]*10, box[2]*10, 90.0, 90.0, 90.0] elif len(box) == 9: # Assume we have vectors leng, ang = box_vectors_to_lengths_and_angles( [box[0], box[3], box[4]]*u.nanometers, [box[5], box[1], box[6]]*u.nanometers, [box[7], box[8], box[2]]*u.nanometers) a, b, c = leng.value_in_unit(u.angstroms) alpha, beta, gamma = ang.value_in_unit(u.degrees) struct.box = [a, b, c, alpha, beta, gamma] finally: if own_handle: fileobj.close() return struct