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 write(struct, dest, precision=3, nobox=False): """ Write a Gromacs Topology File from a Structure Parameters ---------- struct : :class:`Structure` The structure to write to a Gromacs GRO file (must have coordinates) dest : str or file-like The name of a file or a file object to write the Gromacs topology to precision : int, optional The number of decimal places to print in the coordinates. Default 3 nobox : bool, optional If the system does not have a periodic box defined, and this option is True, no box will be written. If False, the periodic box will be defined to enclose the solute with 0.5 nm clearance on all sides. If periodic box dimensions *are* defined, this variable has no effect. """ if isinstance(dest, string_types): dest = genopen(dest, 'w') own_handle = True elif not hasattr(dest, 'write'): raise TypeError('dest must be a file name or file-like object') dest.write('GROningen MAchine for Chemical Simulation\n') dest.write('%5d\n' % len(struct.atoms)) has_vels = all(hasattr(a, 'vx') for a in struct.atoms) varwidth = 5 + precision crdfmt = '%%%d.%df' % (varwidth, precision) velfmt = '%%%d.%df' % (varwidth, precision+1) for atom in struct.atoms: resid = (atom.residue.idx + 1) % 100000 atid = (atom.idx + 1) % 100000 dest.write('%5d%-5s%5s%5d' % (resid, atom.residue.name[:5], atom.name[:5], atid)) dest.write((crdfmt % (atom.xx/10))[:varwidth]) dest.write((crdfmt % (atom.xy/10))[:varwidth]) dest.write((crdfmt % (atom.xz/10))[:varwidth]) if has_vels: dest.write((velfmt % (atom.vx/10))[:varwidth]) dest.write((velfmt % (atom.vy/10))[:varwidth]) dest.write((velfmt % (atom.vz/10))[:varwidth]) dest.write('\n') # Box, in the weird format... if struct.box is not None: a, b, c = reduce_box_vectors(*box_lengths_and_angles_to_vectors( *struct.box)) if all([abs(x-90) < TINY for x in struct.box[3:]]): dest.write('%10.5f'*3 % (a[0]/10, b[1]/10, c[2]/10)) else: dest.write('%10.5f'*9 % (a[0]/10, b[1]/10, c[2]/10, a[1]/10, a[2]/10, b[0]/10, b[2]/10, c[0]/10, c[1]/10)) dest.write('\n') elif not nobox and struct.atoms: # Find the extent of the molecule in all dimensions, and buffer it # by 5 A crds = struct.coordinates diff = (crds.max(axis=1) - crds.min(axis=1)) / 10 + 0.5 dest.write('%10.5f'*3 % (diff[0], diff[1], diff[2])) dest.write('\n') if own_handle: dest.close()
def write(struct, dest, precision=3, nobox=False): """ Write a Gromacs Topology File from a Structure Parameters ---------- struct : :class:`Structure` The structure to write to a Gromacs GRO file (must have coordinates) dest : str or file-like The name of a file or a file object to write the Gromacs topology to precision : int, optional The number of decimal places to print in the coordinates. Default 3 nobox : bool, optional If the system does not have a periodic box defined, and this option is True, no box will be written. If False, the periodic box will be defined to enclose the solute with 0.5 nm clearance on all sides. If periodic box dimensions *are* defined, this variable has no effect. """ own_handle = False if isinstance(dest, string_types): dest = genopen(dest, 'w') own_handle = True elif not hasattr(dest, 'write'): raise TypeError('dest must be a file name or file-like object') dest.write('GROningen MAchine for Chemical Simulation\n') dest.write('%5d\n' % len(struct.atoms)) has_vels = all(hasattr(a, 'vx') for a in struct.atoms) varwidth = 5 + precision crdfmt = '%%%d.%df' % (varwidth, precision) velfmt = '%%%d.%df' % (varwidth, precision+1) boxfmt = '%%%d.%df ' % (max(varwidth, 10), max(precision, 5)) for atom in struct.atoms: resid = (atom.residue.idx + 1) % 100000 atid = (atom.idx + 1) % 100000 dest.write('%5d%-5s%5s%5d' % (resid, atom.residue.name[:5], atom.name[:5], atid)) dest.write((crdfmt % (0.1*atom.xx))[:varwidth]) dest.write((crdfmt % (0.1*atom.xy))[:varwidth]) dest.write((crdfmt % (0.1*atom.xz))[:varwidth]) if has_vels: dest.write((velfmt % (0.1*atom.vx))[:varwidth]) dest.write((velfmt % (0.1*atom.vy))[:varwidth]) dest.write((velfmt % (0.1*atom.vz))[:varwidth]) dest.write('\n') # Box, in the weird format... if struct.box is not None: a, b, c = reduce_box_vectors(*box_lengths_and_angles_to_vectors( *struct.box)) if all([abs(x-90) < TINY for x in struct.box[3:]]): dest.write(boxfmt*3 % (0.1*a[0], 0.1*b[1], 0.1*c[2])) else: dest.write(boxfmt*9 % (0.1*a[0], 0.1*b[1], 0.1*c[2], 0.1*a[1], 0.1*a[2], 0.1*b[0], 0.1*b[2], 0.1*c[0], 0.1*c[1])) dest.write('\n') elif not nobox and struct.atoms: # Find the extent of the molecule in all dimensions, and buffer it # by 5 A crds = struct.coordinates diff = 0.1*(crds.max(axis=1) - crds.min(axis=1)) + 0.5 dest.write(boxfmt*3 % (diff[0], diff[1], diff[2])) dest.write('\n') if own_handle: dest.close()
def write(struct, dest, precision=3, nobox=False, combine=False): """ Write a Gromacs Topology File from a Structure Parameters ---------- struct : :class:`Structure` The structure to write to a Gromacs GRO file (must have coordinates) dest : str or file-like The name of a file or a file object to write the Gromacs topology to precision : int, optional The number of decimal places to print in the coordinates. Default 3 nobox : bool, optional If the system does not have a periodic box defined, and this option is True, no box will be written. If False, the periodic box will be defined to enclose the solute with 0.5 nm clearance on all sides. If periodic box dimensions *are* defined, this variable has no effect. combine : 'all', None, or list of iterables, optional Equivalent to the combine argument of the GromacsTopologyFile.write method. If None, system atom order may be changed to meet the need for contiguously bonded groups of atoms to be part of a single moleculetype. All other values leave the atom order unchanged. Default is None. """ def _write_atom_line(atom, atid, resid, has_vels, dest, precision): varwidth = 5 + precision crdfmt = '%%%d.%df' % (varwidth, precision) velfmt = '%%%d.%df' % (varwidth, precision + 1) dest.write('%5d%-5s%5s%5d' % (resid, atom.residue.name[:5], atom.name[:5], atid)) dest.write((crdfmt % (atom.xx / 10))[:varwidth]) dest.write((crdfmt % (atom.xy / 10))[:varwidth]) dest.write((crdfmt % (atom.xz / 10))[:varwidth]) if has_vels: dest.write((velfmt % (atom.vx / 10))[:varwidth]) dest.write((velfmt % (atom.vy / 10))[:varwidth]) dest.write((velfmt % (atom.vz / 10))[:varwidth]) dest.write('\n') own_handle = False if isinstance(dest, string_types): dest = genopen(dest, 'w') own_handle = True elif not hasattr(dest, 'write'): raise TypeError('dest must be a file name or file-like object') dest.write('GROningen MAchine for Chemical Simulation\n') dest.write('%5d\n' % len(struct.atoms)) has_vels = all(hasattr(a, 'vx') for a in struct.atoms) if combine != 'all': resid, atid = 0, 0 # use struct.split to get residue order as per topology file split_struct = struct.split() n_mols = sum(len(mol[1]) for mol in split_struct) unused_atoms = list(struct.atoms) for molid in range(n_mols): # loop through molids so we can get the correct molecule # according to the order they appear molecule = [mol[0] for mol in split_struct if molid in mol[1]][0] new_molecule = set() # track atoms added last_found_atom = None # track when gro and top diverge for residue in molecule.residues: resid += 1 for atom in residue.atoms: # for each atom in split topology get the first # matching occurrence in the original structure for original_atom in unused_atoms: if atom.type == original_atom.type and \ atom.name == original_atom.name and \ atom.residue.name == original_atom.residue.name: if last_found_atom is not None and \ original_atom.idx != last_found_atom.idx + 1: # a rearrangement has occurred! Need to do # extra check that we've found the correct # original_atom if len( new_molecule.intersection( original_atom.bond_partners) ) == 0: # original_atom must be bonded to at # least one atom in the molecule we # are currently writing otherwise find # next candidate continue atid += 1 _write_atom_line(original_atom, atid % 100000, resid % 100000, has_vels, dest, precision) new_molecule.add(original_atom) last_found_atom = original_atom unused_atoms.remove(original_atom) break else: raise Exception("Could not find %s" % atom) else: for atom in struct.atoms: resid = (atom.residue.idx + 1) % 100000 atid = (atom.idx + 1) % 100000 _write_atom_line(atom, atid, resid, has_vels, dest, precision) # Box, in the weird format... if struct.box is not None: a, b, c = reduce_box_vectors(*box_lengths_and_angles_to_vectors( *struct.box)) if all([abs(x - 90) < TINY for x in struct.box[3:]]): dest.write('%10.5f' * 3 % (a[0] / 10, b[1] / 10, c[2] / 10)) else: dest.write('%10.5f' * 9 % (a[0] / 10, b[1] / 10, c[2] / 10, a[1] / 10, a[2] / 10, b[0] / 10, b[2] / 10, c[0] / 10, c[1] / 10)) dest.write('\n') elif not nobox and struct.atoms: # Find the extent of the molecule in all dimensions, and buffer it # by 5 A crds = struct.coordinates diff = (crds.max(axis=1) - crds.min(axis=1)) / 10 + 0.5 dest.write('%10.5f' * 3 % (diff[0], diff[1], diff[2])) dest.write('\n') if own_handle: dest.close()
def write(struct, dest, precision=3, nobox=False, combine=False): """ Write a Gromacs Topology File from a Structure Parameters ---------- struct : :class:`Structure` The structure to write to a Gromacs GRO file (must have coordinates) dest : str or file-like The name of a file or a file object to write the Gromacs topology to precision : int, optional The number of decimal places to print in the coordinates. Default 3 nobox : bool, optional If the system does not have a periodic box defined, and this option is True, no box will be written. If False, the periodic box will be defined to enclose the solute with 0.5 nm clearance on all sides. If periodic box dimensions *are* defined, this variable has no effect. combine : 'all', None, or list of iterables, optional Equivalent to the combine argument of the GromacsTopologyFile.write method. If None, system atom order may be changed to meet the need for contiguously bonded groups of atoms to be part of a single moleculetype. All other values leave the atom order unchanged. Default is None. """ def _write_atom_line(atom, atid, resid, has_vels, dest, precision): varwidth = 5 + precision crdfmt = '%%%d.%df' % (varwidth, precision) velfmt = '%%%d.%df' % (varwidth, precision+1) dest.write('%5d%-5s%5s%5d' % (resid, atom.residue.name[:5], atom.name[:5], atid)) dest.write((crdfmt % (atom.xx/10))[:varwidth]) dest.write((crdfmt % (atom.xy/10))[:varwidth]) dest.write((crdfmt % (atom.xz/10))[:varwidth]) if has_vels: dest.write((velfmt % (atom.vx/10))[:varwidth]) dest.write((velfmt % (atom.vy/10))[:varwidth]) dest.write((velfmt % (atom.vz/10))[:varwidth]) dest.write('\n') own_handle = False if isinstance(dest, string_types): dest = genopen(dest, 'w') own_handle = True elif not hasattr(dest, 'write'): raise TypeError('dest must be a file name or file-like object') dest.write('GROningen MAchine for Chemical Simulation\n') dest.write('%5d\n' % len(struct.atoms)) has_vels = all(hasattr(a, 'vx') for a in struct.atoms) if combine != 'all': resid, atid = 0, 0 # use struct.split to get residue order as per topology file split_struct = struct.split() n_mols = sum(len(mol[1]) for mol in split_struct) unused_atoms = list(struct.atoms) for molid in range(n_mols): # loop through molids so we can get the correct molecule # according to the order they appear molecule = [ mol[0] for mol in split_struct if molid in mol[1]][0] new_molecule = set() # track atoms added last_found_atom = None # track when gro and top diverge for residue in molecule.residues: resid += 1 for atom in residue.atoms: # for each atom in split topology get the first # matching occurrence in the original structure for original_atom in unused_atoms: if atom.type == original_atom.type and \ atom.name == original_atom.name and \ atom.residue.name == original_atom.residue.name: if last_found_atom is not None and \ original_atom.idx != last_found_atom.idx + 1: # a rearrangement has occurred! Need to do # extra check that we've found the correct # original_atom if len(new_molecule.intersection( original_atom.bond_partners)) == 0: # original_atom must be bonded to at # least one atom in the molecule we # are currently writing otherwise find # next candidate continue atid += 1 _write_atom_line( original_atom, atid % 100000, resid % 100000, has_vels, dest, precision) new_molecule.add(original_atom) last_found_atom = original_atom unused_atoms.remove(original_atom) break else: raise RuntimeError("Could not find %s" % atom) else: for atom in struct.atoms: resid = (atom.residue.idx + 1) % 100000 atid = (atom.idx + 1) % 100000 _write_atom_line( atom, atid, resid, has_vels, dest, precision) # Box, in the weird format... if struct.box is not None: a, b, c = reduce_box_vectors(*box_lengths_and_angles_to_vectors( *struct.box)) if all([abs(x-90) < TINY for x in struct.box[3:]]): dest.write('%10.5f'*3 % (a[0]/10, b[1]/10, c[2]/10)) else: dest.write('%10.5f'*9 % (a[0]/10, b[1]/10, c[2]/10, a[1]/10, a[2]/10, b[0]/10, b[2]/10, c[0]/10, c[1]/10)) dest.write('\n') elif not nobox and struct.atoms: # Find the extent of the molecule in all dimensions, and buffer it # by 5 A crds = struct.coordinates diff = (crds.max(axis=1) - crds.min(axis=1)) / 10 + 0.5 dest.write('%10.5f'*3 % (diff[0], diff[1], diff[2])) dest.write('\n') if own_handle: dest.close()