def setUnitCellDimensions(self, dimensions): """Set the dimensions of the crystallographic unit cell. This method is an alternative to setPeriodicBoxVectors() for the case of a rectangular box. It sets the box vectors to be orthogonal to each other and to have the specified lengths.""" if dimensions is None: self._periodicBoxVectors = None else: if is_quantity(dimensions): dimensions = dimensions.value_in_unit(nanometers) self._periodicBoxVectors = (Vec3(dimensions[0], 0, 0), Vec3(0, dimensions[1], 0), Vec3(0, 0, dimensions[2])) * nanometers
def prepare_pdb(self, path_pdb: Union[str, PDBFile], rm_residue=[]): ''' prepare pdb file: add hydrogens, add solvent return: pdb ''' from_fixer = False if isinstance(path_pdb, str): assert os.path.isfile(path_pdb) pdb_tmp = PDBFile(path_pdb) else: from_fixer = True pdb_tmp = path_pdb # feed it into Modeller and add missing atoms # quasi centering positions_arr = self._asarray(pdb_tmp.positions._value) positions_arr = positions_arr - np.mean(positions_arr, axis=0) shift = self._asarray(self.params.boxSize) / 2 positions_arr += shift modeller = Modeller(pdb_tmp.topology, positions_arr) if len(rm_residue) != 0: modeller.delete(toDelete=rm_residue) if not from_fixer: _ = modeller.addHydrogens(self.forcefield) modeller.addSolvent(self.forcefield, boxSize=Vec3(*self.params.boxSize) * nanometer) return modeller
def getUnitCellDimensions(self): """Get the dimensions of the crystallographic unit cell. The return value may be None if this Topology does not represent a periodic structure. """ if self._periodicBoxVectors is None: return None xsize = self._periodicBoxVectors[0][0].value_in_unit(nanometers) ysize = self._periodicBoxVectors[1][1].value_in_unit(nanometers) zsize = self._periodicBoxVectors[2][2].value_in_unit(nanometers) return Vec3(xsize, ysize, zsize) * nanometers
def _parse(self, fname): with open(fname, 'r') as crdfile: line = crdfile.readline() while len(line.strip()) == 0: # Skip whitespace, as a precaution line = crdfile.readline() intitle = True while intitle: self.title.append(line.strip()) line = crdfile.readline() if len(line.strip()) == 0: intitle = False elif line.strip()[0] != '*': intitle = False else: intitle = True while len(line.strip()) == 0: # Skip whitespace line = crdfile.readline() try: self.natom = int(line.strip().split()[0]) for _ in range(self.natom): line = crdfile.readline().strip().split() self.atomno.append(int(line[0])) self.resno.append(int(line[1])) self.resname.append(line[2]) self.attype.append(line[3]) pos = Vec3(float(line[4]), float(line[5]), float(line[6])) self.positions.append(pos) self.segid.append(line[7]) self.weighting.append(float(line[9])) if self.natom != len(self.positions): raise CharmmFileError( "Error parsing CHARMM .crd file: %d " "atoms requires %d positions (not %d)" % (self.natom, self.natom, len(self.positions))) except (ValueError, IndexError): raise CharmmFileError('Error parsing CHARMM coordinate file') # Apply units to the positions now. Do it this way to allow for # (possible) numpy functionality in the future. self.positions = u.Quantity(self.positions, u.angstroms)
def _get_formatted_crds(self, crdfile, crds): for row in range(self.natom): line = crdfile.readline() if not line: raise CharmmFileError('Premature end of file') if len(line) < 3 * CHARMMLEN: raise CharmmFileError("Less than 3 coordinates present in " "coordinate row or positions may be " "truncated.") line = line.replace('D', 'E') # CHARMM uses 'D' for exponentials # CHARMM uses fixed format (len = CHARMMLEN = 22) for crds in .rst's c = Vec3(float(line[0:CHARMMLEN]), float(line[CHARMMLEN:2 * CHARMMLEN]), float(line[2 * CHARMMLEN:3 * CHARMMLEN])) crds.append(c)
def addHydrogensPageCallback(parameters, handler): if 'addhydrogens' in parameters: pH = float(parameters.getfirst('ph')) fixer.addMissingHydrogens(pH) if 'addwater' in parameters: padding, boxSize, boxVectors = None, None, None if parameters.getfirst('boxType') == 'geometry': geompadding = float( parameters.getfirst('geomPadding')) * unit.nanometer geometry = parameters.getfirst('geometryDropdown') base_size = float( parameters.getfirst('maxMolecularAxis')) * unit.nanometer if geometry == 'cube': padding = geompadding elif geometry == 'truncatedOctahedron': vectors = Vec3(1, 0, 0), Vec3(1 / 3, 2 * sqrt(2) / 3, 0), Vec3(-1 / 3, sqrt(2) / 3, sqrt(6) / 3) boxVectors = [(base_size + geompadding) * v for v in vectors] elif geometry == 'rhombicDodecahedron': vectors = Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0.5, 0.5, sqrt(2) / 2) boxVectors = [(base_size + geompadding) * v for v in vectors] else: boxSize = (float(parameters.getfirst('boxx')), float(parameters.getfirst('boxy')), float(parameters.getfirst('boxz'))) * unit.nanometer ionicStrength = float( parameters.getfirst('ionicstrength')) * unit.molar positiveIon = parameters.getfirst('positiveion') + '+' negativeIon = parameters.getfirst('negativeion') + '-' fixer.addSolvent(boxSize, padding, boxVectors, positiveIon, negativeIon, ionicStrength) if 'addmembrane' in parameters: lipidType = parameters.getfirst('lipidType') padding = float( parameters.getfirst('membranePadding')) * unit.nanometer ionicStrength = float( parameters.getfirst('ionicstrength')) * unit.molar positiveIon = parameters.getfirst('positiveion') + '+' negativeIon = parameters.getfirst('negativeion') + '-' fixer.addMembrane(lipidType, 0 * unit.nanometer, padding, positiveIon, negativeIon, ionicStrength) displaySaveFilePage()
def __init__(self, pdb_line, pdbstructure=None, extraParticleIdentifier='EP'): """Create a new pdb.Atom from an ATOM or HETATM line. Example line: ATOM 2209 CB TYR A 299 6.167 22.607 20.046 1.00 8.12 C 00000000011111111112222222222333333333344444444445555555555666666666677777777778 12345678901234567890123456789012345678901234567890123456789012345678901234567890 ATOM line format description from http://deposit.rcsb.org/adit/docs/pdb_atom_format.html: COLUMNS DATA TYPE CONTENTS -------------------------------------------------------------------------------- 1 - 6 Record name "ATOM " 7 - 11 Integer Atom serial number. 13 - 16 Atom Atom name. 17 Character Alternate location indicator. 18 - 20 Residue name Residue name. 22 Character Chain identifier. 23 - 26 Integer Residue sequence number. 27 AChar Code for insertion of residues. 31 - 38 Real(8.3) Orthogonal coordinates for X in Angstroms. 39 - 46 Real(8.3) Orthogonal coordinates for Y in Angstroms. 47 - 54 Real(8.3) Orthogonal coordinates for Z in Angstroms. 55 - 60 Real(6.2) Occupancy (Default = 1.0). 61 - 66 Real(6.2) Temperature factor (Default = 0.0). 73 - 76 LString(4) Segment identifier, left-justified. 77 - 78 LString(2) Element symbol, right-justified. 79 - 80 LString(2) Charge on the atom. """ # We might modify first/final status during _finalize() methods self.is_first_atom_in_chain = False self.is_final_atom_in_chain = False self.is_first_residue_in_chain = False self.is_final_residue_in_chain = False # Start parsing fields from pdb line self.record_name = pdb_line[0:6].strip() if pdbstructure is not None and pdbstructure._atom_numbers_are_hex: self.serial_number = int(pdb_line[6:11], 16) else: try: self.serial_number = int(pdb_line[6:11]) except: try: self.serial_number = int(pdb_line[6:11], 16) pdbstructure._atom_numbers_are_hex = True except: # Just give it the next number in sequence. self.serial_number = pdbstructure._next_atom_number self.name_with_spaces = pdb_line[12:16] alternate_location_indicator = pdb_line[16] self.residue_name_with_spaces = pdb_line[17:20] # In some MD codes, notably ffamber in gromacs, residue name has a fourth character in # column 21 possible_fourth_character = pdb_line[20:21] if possible_fourth_character != " ": # Fourth character should only be there if official 3 are already full if len(self.residue_name_with_spaces.strip()) != 3: raise ValueError('Misaligned residue name: %s' % pdb_line) self.residue_name_with_spaces += possible_fourth_character self.residue_name = self.residue_name_with_spaces.strip() self.chain_id = pdb_line[21] if pdbstructure is not None and pdbstructure._residue_numbers_are_hex: self.residue_number = int(pdb_line[22:26], 16) else: try: self.residue_number = int(pdb_line[22:26]) except: try: self.residue_number = int(pdb_line[22:26], 16) pdbstructure._residue_numbers_are_hex = True except: # When VMD runs out of hex values it starts filling the residue ID field with ****. # Look at the most recent atoms to figure out whether this is a new residue or not. if pdbstructure._current_model is None or pdbstructure._current_model._current_chain is None or pdbstructure._current_model._current_chain._current_residue is None: # This is the first residue in the model. self.residue_number = pdbstructure._next_residue_number else: currentRes = pdbstructure._current_model._current_chain._current_residue if currentRes.name_with_spaces != self.residue_name_with_spaces: # The residue name has changed. self.residue_number = pdbstructure._next_residue_number elif self.name_with_spaces in currentRes.atoms_by_name: # There is already an atom with this name. self.residue_number = pdbstructure._next_residue_number else: self.residue_number = currentRes.number self.insertion_code = pdb_line[26] # coordinates, occupancy, and temperature factor belong in Atom.Location object x = float(pdb_line[30:38]) y = float(pdb_line[38:46]) z = float(pdb_line[46:54]) try: occupancy = float(pdb_line[54:60]) except: occupancy = 1.0 try: temperature_factor = unit.Quantity(float(pdb_line[60:66]), unit.angstroms**2) except: temperature_factor = unit.Quantity(0.0, unit.angstroms**2) self.locations = {} loc = Atom.Location(alternate_location_indicator, unit.Quantity(Vec3(x, y, z), unit.angstroms), occupancy, temperature_factor, self.residue_name_with_spaces) self.locations[alternate_location_indicator] = loc self.default_location_id = alternate_location_indicator # segment id, element_symbol, and formal_charge are not always present self.segment_id = pdb_line[72:76].strip() self.element_symbol = pdb_line[76:78].strip() try: self.formal_charge = int(pdb_line[78:80]) except ValueError: self.formal_charge = None # figure out atom element if self.element_symbol == extraParticleIdentifier: self.element = 'EP' else: try: # Try to find a sensible element symbol from columns 76-77 self.element = element.get_by_symbol(self.element_symbol) except KeyError: self.element = None if pdbstructure is not None: pdbstructure._next_atom_number = self.serial_number + 1 pdbstructure._next_residue_number = self.residue_number + 1
def __neg__(self): return Vec3(-self.x, -self.y, -self.z)
def __deepcopy__(self, memo): return Vec3(self.x, self.y, self.z)
def __div__(self, other): """Divide a Vec3 by a constant.""" return Vec3(self.x / other, self.y / other, self.z / other)
def __rmul__(self, other): """Multiply a Vec3 by a constant.""" if unit.is_unit(other): return unit.Quantity(self, other) return Vec3(other * self.x, other * self.y, other * self.z)
def __rsub__(self, other): """Add two Vec3s.""" return Vec3(other[0] - self.x, other[1] - self.y, other[2] - self.z)
def __sub__(self, other): """Add two Vec3s.""" return Vec3(self.x - other[0], self.y - other[1], self.z - other[2])
def __radd__(self, other): """Add two Vec3s.""" return Vec3(self.x + other[0], self.y + other[1], self.z + other[2])