def write(self, atomdict): """ Write an atom to the file. Args: atom (dict): a dictionary describing the atom. """ from BigDFT.Atoms import Atom at = Atom(atomdict) self._handle.write(at.sym + " ") pos = at.get_position(self.units) self._handle.write(" ".join([str(x) for x in pos])) self._handle.write("\n")
def __init__(self, filename): from BigDFT.Atoms import Atom self.atoms = [] symbol_lookup = [] with open(filename, "r") as ifile: # Read the first line matinfo = next(ifile).split() self.matdim, natoms, ntypes = [int(x) for x in matinfo[:3]] # Units next(ifile) # skip geocode line = next(ifile) # skip shift? line = next(ifile) # get the symbol lookup information for i in range(0, ntypes): line = next(ifile).split() symbol_lookup.append(line[2]) # Get the atom positions for i in range(0, natoms): line = next(ifile).split() adict = {} adict["sym"] = symbol_lookup[int(line[0]) - 1] adict["r"] = [float(x) for x in line[1:4]] # THIS IS BECAUSE THE METADATA FILE CURRENTLY PRINTS THE # WRONG UNITS! adict["units"] = "bohr" adict["indices"] = [] self.atoms.append(Atom(adict)) # Get the indices for i in range(0, self.matdim): line = next(ifile).split() self.atoms[int(line[0]) - 1]["indices"].append(i)
def update_positions_from_dict(self, posinp): """ Update the atomic positions of a system from a posinp dictionary. This method only works if the order of atoms match. Args: posinp (dict): a posinp dictionary. """ from BigDFT.Atoms import Atom units = posinp.get("units", "angstroem") i = 0 for frag in self.values(): for at in frag: at2 = Atom(posinp["positions"][i], units=units) at.set_position(at2.get_position()) i += 1
def compute_system_forces(sys, forcefield="MMFF94", verbose=False): """ Assign the forces of a system using an openbabel forcefield. Args: sys (BigDFT.Systems.System): the system to compute. forcefield (str): the type of forcefield to use. verbose (bool): whether to have openbabel run in verbose mode. Returns: (float): the energy of the system. """ from openbabel.openbabel import OBForceField, OBFF_LOGLVL_LOW from BigDFT.Atoms import Atom, number_to_symbol, AU_to_A # Handle verbository. if verbose: ff.SetLogToStdOut() ff.SetLogLevel(OBFF_LOGLVL_LOW) # Setup the forcefield ff = OBForceField.FindForceField(forcefield) mol = convert_system_to_babel(sys) ff.Setup(mol) # Compute the forces. energy_out = ff.Energy() * _energy_conversion[ff.GetUnit()] gradients = [] for idx in range(1, mol.NumAtoms() + 1): at = mol.GetAtom(idx) grad = ff.GetGradient(at) convgrad = [grad.GetX(), grad.GetY(), grad.GetZ()] convgrad = [ x * _energy_conversion[ff.GetUnit()] / AU_to_A for x in convgrad ] gradients.append(convgrad) # Create the atom list for the compute matching procedure. atom_list = [] for idx in range(1, mol.NumAtoms() + 1): obat = mol.GetAtom(idx) atnum = obat.GetAtomicNum() pos = [obat.GetX(), obat.GetY(), obat.GetZ()] atom_list.append( Atom({ number_to_symbol(atnum): pos, "units": "angstroem" })) lookup = sys.compute_matching(atom_list) # Assign for fragid, frag in sys.items(): for i, at in enumerate(frag): idx = lookup[fragid][i] at.set_force(gradients[idx]) return energy_out
def __next__(self): from BigDFT.Atoms import Atom line = next(self._handle) if self.natoms == len(self.atoms_positions): raise StopIteration split = line.split() sym = split[0] position = [float(x) for x in split[1:4]] this_pos = Atom({'sym': sym, 'r': position, "units": self.units}) self.atoms_positions.append(this_pos) return this_pos
def __init__(self, atomlist=None, xyzfile=None, posinp=None, astruct=None, system=None): from BigDFT.Atoms import Atom self.atoms = [] if system is not None: self._system_to_fragment(system) return # insert atoms. if atomlist: for atom in atomlist: self.append(Atom(atom)) elif xyzfile: with xyzfile: for line in xyzfile: self.append(Atom(line)) elif posinp: units = posinp.get('units', 'angstroem') for atom in posinp['positions']: self.append(Atom(atom, units=units)) elif astruct: units = astruct.get('units', 'angstroem') rshift = astruct.get('Rigid Shift Applied (AU)', [0.0, 0.0, 0.0]) for atom in astruct["positions"]: self.append(Atom(atom, units=units)) self.translate([-1.0 * x for x in rshift]) # Values self.q1 = None self.q2 = None self.frozen = None self.conmat = None
def compute_matching(self, atlist, shift=None, check_matching=True): """ Frequently we are passed a list of atom like objects from which we need to extract data and assign it to a system. However, a system can potentially store those atoms in any order, and may not have the same set of atoms. This helper routine creates a mapping between this list view, to the dictionary view of the system class. Args: atlist (list): a list of atom like objects. shift (list): if the positions in atlist are shifted by some constant vector you can specify that here. check_matching (bool): if set to True, this will raise an error if we can't match all of the atoms in the system. Returns: (dict): a mapping from a system to indices in the atom list. If an atom is not in the list, an index value of -1 is assigned. """ from BigDFT.Atoms import Atom from numpy import array from scipy.spatial import KDTree # Convert everything to pure positions to avoid overhead. poslist = [ array(Atom(x).get_position("bohr", cell=self.cell)) for x in atlist ] if shift is not None: poslist = [x - array(shift) for x in poslist] tree = KDTree(poslist) # Seach for the mapping values mapping = {} for fragid, frag in self.items(): mapping[fragid] = [] for at in frag: atpos = array(at.get_position("bohr", cell=self.cell)) ndist, nearest = tree.query(atpos) if check_matching and ndist > 0.01: raise ValueError("Unable to match atom" + str(dict(at))) mapping[fragid].append(nearest) return mapping
def system_from_dict_positions(posinp, units='angstroem'): """ Build a system from a set of positions from a dictionary whose yaml serialisation is compliant with the BigDFT yaml position format Args: posinp (list): list of the atomic specifications Returns: BigDFT.Systems.System: an instance of the system class. The employed fragment specification is specified in the file. """ from BigDFT.Atoms import Atom from BigDFT.Fragments import Fragment sys = System() for iat, at in enumerate(posinp): frag = GetFragId(at, iat) if frag not in sys: sys[frag] = Fragment() sys[frag].append(Atom(at, units=units)) return sys
def _process_atom(line, include_chain=False): """ This processes a line of a pdb file and extracts information about an atom. Returns: (BigDFT.Atoms.Atom, int, str): return the Atom on this line, its id in the pdb, and the fragment it belongs to. It may include the chain id. """ from BigDFT.Atoms import Atom # Get the basic information about this atom sym = line[76:78].strip().capitalize() if "1-" in sym: sym = sym.replace("1-", "") if "1+" in sym: sym = sym.replace("1+", "") fullname = line[12:16] name = fullname.strip() xpos = float(line[30:38]) ypos = float(line[38:46]) zpos = float(line[46:54]) at = Atom({"sym": sym, "r": [xpos, ypos, zpos], "name": name, "units": "angstroem"}) # Get the atom id for building the lookup table atid = int(line[6:11]) # Information about the residue resname = line[17:20] chain = line[20:22].strip() resid = str(int(line[22:26])) fragid = resname+":"+resid if include_chain: fragid = chain+'-'+fragid return at, atid, fragid
def __setitem__(self, index, value): from BigDFT.Atoms import Atom self.atoms.__setitem__(index, Atom(value))
def insert(self, index, value): from BigDFT.Atoms import Atom self.atoms.insert(index, Atom(value))
def read_mol2(ifile, disable_warnings=False): """ Read a system from a mol2 file. Args: ifile (TextIOBase): the file to read from. disable_warnings (bool): whether to print warnings about possible file issues. Returns: (BigDFT.Systems.System): the system file. """ from BigDFT.Systems import System from BigDFT.Fragments import Fragment from BigDFT.Atoms import Atom from BigDFT.UnitCells import UnitCell from warnings import warn sys = System() # Just go ahead and read the whole file into a string. lines = [x for x in ifile] # First pass, read in the number of atoms. for start, line in enumerate(lines): if ("@<TRIPOS>MOLECULE" in line): break start += 1 split = lines[start+1].split() natoms = int(split[0]) nbonds = int(split[1]) # Second pass read in the atoms. for start, line in enumerate(lines): if ("@<TRIPOS>ATOM" in line): break start += 1 lookup = [] for i in range(0, natoms): split = lines[start + i].split() pos = [float(x) for x in split[2:5]] name = split[5] sym = name.split(".")[0] fragid = split[7] + ":" + split[6] charge = [float(split[8])] # Add fragment if fragid not in sys: sys[fragid] = Fragment() at = Atom({sym: pos, "units": "angstroem", "q0": charge, "name": name}) sys[fragid] += [at] # Lookup table for connectivity lookup.append((fragid, len(sys[fragid]) - 1)) # Third pass reads the connectivity. for start, line in enumerate(lines): if ("@<TRIPOS>BOND" in line): break start += 1 if start < len(lines): sys.conmat = {} for fragid, frag in sys.items(): sys.conmat[fragid] = [] for i in range(0, len(frag)): sys.conmat[fragid].append({}) bowarn = False for i in range(0, nbonds): split = lines[start + i].split() frag1, at1 = lookup[int(split[1])-1] frag2, at2 = lookup[int(split[2])-1] bo = split[3] try: bo = float(split[3]) except ValueError: bowarn = True bo = 1 sys.conmat[frag1][at1][(frag2, at2)] = bo # Since mol2 doesn't include the symmetric bonds. if frag1 != frag2 or at1 != at2: sys.conmat[frag2][at2][(frag1, at1)] = bo # Fourth path reads the unit cell. for start, line in enumerate(lines): if ("@<TRIPOS>CRYSIN" in line): break start += 1 if start < len(lines): split = lines[start].split() a = float(split[0]) b = float(split[1]) c = float(split[2]) alpha = float(split[3]) beta = float(split[4]) gamma = float(split[5]) sys.cell = UnitCell([a, b, c], units="angstroem") if not disable_warnings: if (alpha != 90 or beta != 90 or gamma != 90): warn("Cell angles must be 90 degrees", UserWarning) if not disable_warnings: if sum([len(x) for x in sys.values()]) == 0: warn("Warning: zero atoms found", UserWarning) if bowarn: warn("Unsupported bond type had to be set to 1 (i.e. aromatic)", UserWarning) return sys
def read_polaris_pdb(pdbfile, chain_as_letter=False, slefile=None): """ Read coordinates in the PDB format of POLARIS Args: pdbfile (str): path of the input file chain_as_letter (bool): If True, the fifth column is assumed to contain a letter slefile (str): path of the file ``.sle`` of Polaris from which to extract the system's attributes. Warning: Assumes Free Boundary conditions for the molecule. Only accepts atoms that have one letter in the symbol. Switch representation if there is a single letter in the fifth column Returns: System: A system class """ from BigDFT.Fragments import Fragment from BigDFT.Systems import System, GetFragId from BigDFT.Atoms import Atom sys = System() units = 'angstroem' with open(pdbfile) as ifile: for line in ifile: if 'ATOM' not in line: continue atomline = line.split() if chain_as_letter: iat, name, frag, lett, ifrag, x, y, z, sn = atomline[1:10] chain = lett segname = sn else: iat, name, frag, ifrag, x, y, z, chlett = atomline[1:9] chain = chlett[2] segname = chlett atdict = { str(name[:1]): map(float, [x, y, z]), 'frag': [chain + '-' + frag, int(ifrag)], 'name': name, 'iat': int(iat), 'segname': segname } fragid = GetFragId(atdict, iat) if fragid not in sys: sys[fragid] = Fragment() sys[fragid].append(Atom(atdict, units=units)) if slefile is None: return sys attributes = read_polaris_sle(slefile) from BigDFT import Systems as S, Fragments as F, Atoms as A system = S.System() for name, frag in sys.items(): refrag = F.Fragment() for at in frag: atdict = at.dict() att = attributes[atdict['iat'] - 1] assert att['name'] == atdict['name'] atdict.update(att) refrag.append(A.Atom(atdict)) system[name] = refrag return system