def _example(): """Example of using OpenBabel interoperability""" from BigDFT.Systems import System from BigDFT.Fragments import Fragment from BigDFT.IO import XYZReader from os.path import join # Read in a system. sys = System() sys["FRA:1"] = Fragment() with XYZReader("CH4") as ifile: for at in ifile: sys["FRA:1"] += Fragment([at]) # We can compute the smiles representation. print(compute_smiles(sys)) # The energy. print(system_energy(sys, forcefield="UFF")) # Extract the forces. compute_system_forces(sys, forcefield="UFF") for frag in sys.values(): for at in frag: print(at["force"]) # Optimize the geometry. sys2 = optimize_system(sys, forcefield="UFF") print(system_energy(sys2, forcefield="UFF"))
def fragment_mask_matrix(self, sys, mat, fragments, log): """ Sometimes we don't want to store an entire matrix, just the parts related to some fragments of interest. This routine will mask out a matrix, keeping entries only related to the list of fragments provided. Args: sys (BigDFT.Systems.System): the system associated with the matrix. mat (scipy.sparse.csr_matrix): the matrix to mask. fragments (list): a list of fragment ids to keep. log (BigDFT.Logfiles.Logfile): the logfile associated with this matrix's calculation. Returns: (scipy.sparse.csr_matrix): the masked matrix. """ from BigDFT.Systems import System from copy import deepcopy from scipy.sparse import dok_matrix subsys = System() for fragid in fragments: subsys[fragid] = deepcopy(sys[fragid]) frag_indices = self.get_frag_indices(subsys, log) frag_indices = sum(frag_indices.values(), []) imat = dok_matrix(mat.shape) for i in frag_indices: imat[i, i] = 1 return imat.dot(mat).dot(imat)
def _example(): """Example of using PSI4 interoperability""" from BigDFT.IO import XYZReader from BigDFT.Systems import System from BigDFT.Fragments import Fragment from os.path import join from copy import deepcopy # Create a system. reader = XYZReader(join("Database", "XYZs", "He.xyz")) fsys = System() fsys["FRA:1"] = Fragment(xyzfile=reader) fsys["FRA:2"] = deepcopy(fsys["FRA:1"]) fsys["FRA:2"].translate([-2, 0, 0]) # Create a calculator. code = PSI4Calculator() log = code.run(sys=fsys, action="energy", basis="jun-cc-pvdz", psi4_options={"scf_type": "direct"}, method="scf", name="test") print(log) # The raw logfile is also available. print(log.log[4]) # Geometry optimization. log = code.run(sys=fsys, action="optimize", basis="jun-cc-pvdz", method="scf", name="test-opt") print(log) # SAPT log = code.run(sys=fsys, action="energy", basis="jun-cc-pvdz", method="sapt0", name="test-sapt") print(log)
def __init__(self, system=None, filename=None): from simtk.openmm.app.pdbfile import PDBFile from simtk.openmm import app from BigDFT.IO import read_pdb, write_pdb from tempfile import NamedTemporaryFile as tmp if filename is not None: pdb = PDBFile(open(filename)) sys = read_pdb(open(filename)) elif system is not None: sys = system ofile = tmp('w+') write_pdb(system=system, ofile=ofile) ofilename = ofile.name pdb = PDBFile(open(ofilename)) ofile.close() System.__init__(self, **sys.dict()) self.pdb = pdb self.modeller = app.Modeller(pdb.topology, pdb.positions)
def compute_fragment_dos(self, frag, log, ks_coeff, eigvals, frag_indices=None, smat=None, assume_pure=False, **kwargs): """ Compute the partial density of states projected onto a given fragment. Args: sys (BigDFT.Fragments.Fragment): the fragment to project on to. log (BigDFT.Logfiles.Logfile): the log of the calculation. ks_coeff (scipy.sparse.csc_matrix): the matrix of eigenvectors. eigvals (list): a list of eigenvalues. frag_indices (list): list of indices associated with this fragment. smat (scipy.sparse.csc_matrix): the overlap matrix. assume_pure (bool): an optimization can be performed if we assume the target is pure. kwargs (dict): any extra options to pass to the DoS constructor. Returns: (BigDFT.DoS.DoS): a density of states object built using the partial density of states. """ from BigDFT.Systems import System from BigDFT.DoS import DoS # Subsystem with just this fragment. subsys = System() subsys["FRAG:0"] = frag # Optional Argments if smat is None: smat = self.get_matrix_s(log) if frag_indices is None: frag_indices = self.get_frag_indices(subsys, log)["FRAG:0"] subsmat = smat[frag_indices, :] subkmat = ks_coeff[frag_indices, :].T vals = [] for n in range(len(eigvals)): work = subkmat[n, :].dot(subsmat) if assume_pure: sumval = work[:, frag_indices].dot(ks_coeff[frag_indices, n]) else: sumval = work[:, :].dot(ks_coeff[:, n]) vals.append(sumval[0, 0]) # Construct the dos object dos = DoS(energies=eigvals, norm=[vals], units='AU', **kwargs) return dos
def __init__(self, name): import os from BigDFT.Fragments import Fragment from BigDFT.Systems import System from BigDFT.IO import XYZReader # import the positions of the molecules from the XYZ directory dirXYZ = os.path.join(os.path.dirname(__file__), 'XYZs') filename = os.path.abspath(os.path.join(dirXYZ, name + '.xyz')) if not os.path.isfile(filename): raise ValueError('Molecule not available') ff = XYZReader(filename) frag = Fragment(xyzfile=ff) system = System({'molecule:0': frag}) self.update(system.get_posinp()) # temporary change of the keys 'values' into 'positions' if 'values' in self: self['positions'] = self.pop('values') if 'positions' in self: for at in self['positions']: if 'r' in at: at.pop('r')
def _create_system(self): """ Create a BigDFT system from the atoms of this calculator. """ from BigDFT.Systems import System from BigDFT.UnitCells import UnitCell from ase.units import Bohr sys = System() sys["FRAG:0"] = ase_to_bigdft(self.atoms) if self.atoms.cell is not None: if any(self.atoms.get_pbc()): if not self.atoms.cell.orthorhombic: raise ValueError("Only orthorhombic cells are supported.") vec = [ float(self.atoms.cell[i, i]) if self.atoms.get_pbc()[i] else float("inf") for i in range(3) ] sys.cell = UnitCell(vec, units="angstroem") return sys
def _example(): """Visualization Example""" from BigDFT.Systems import System from BigDFT.Fragments import Fragment from BigDFT.IO import XYZReader # Read in a system. sys = System() with XYZReader("SiO") as ifile: for i, at in enumerate(ifile): sys["FRA:" + str(i)] = Fragment([at]) # Display the system. viz = InlineVisualizer(400, 300) viz.display_system(sys) # Change the colors colordict = get_distinct_colors(list(sys)) viz.display_system(sys, colordict=colordict)
def create_layered_qmmm_system(self, system, target, pairwise_bo, cutoffs, criteria="bondorder", link_atoms=False): """ Creates a multilayered system suitable for QM/MM calculations. For each layer, a suitable QM region is built around it. Args: system (BigDFT.Systems.System): a System class, already broken up into fragments. pairwise_bo (dict): pairwise bond order values between all fragments in the system. target (str): the name of the fragment to treat as the target of the qm/mm run. cutoffs (list): a list of cutoff value for fragment interactions. The number of layers is equal to the number of cutoffs. criteria (str): how to determine which atoms are included in the QM region. Valid choices are "bondorder" and "distance". link_atoms (bool): whether to generate link atoms. Returns: (list): a list of Systems, one for each QM layer. (System): the MM region. """ from copy import deepcopy from BigDFT.Systems import System # First, we will build the layers using only keys. qm_list = [] qm, mm = self.create_qmmm_system(system, target, pairwise_bo[target], cutoffs[0], criteria, link_atoms=False) qm_list.append(list(qm.keys())) mm_keys = list(mm.keys()) # To avoid duplicates used_list = deepcopy(list(qm.keys())) for k in range(1, len(cutoffs)): qm_list.append([]) for subtarget in qm_list[k - 1]: qm, mm = self.create_qmmm_system(system, subtarget, pairwise_bo[subtarget], cutoffs[k], criteria, link_atoms=False) qm_list[k] += qm.keys() # Remove duplicates qm_list[k] = [x for x in qm_list[k] if x not in used_list] qm_list[k] = list(set(qm_list[k])) used_list = list(set(used_list + qm_list[k])) mm_keys = [x for x in mm_keys if x not in qm_list[k]] # Next, we will convert those keys into systems. qmsys = [] for j in range(0, len(cutoffs)): qmsys.append(System()) for k in qm_list[j]: qmsys[j][k] = deepcopy(system[k]) mmsys = System() for k in mm_keys: mmsys[k] = deepcopy(system[k]) # Add the link atoms. if link_atoms: # One big system to add the link atoms. mergesys = System() for k in range(0, len(cutoffs)): for fragid, frag in qmsys[k].items(): mergesys[fragid] = frag linksub, remove = self.generate_link_atoms(system, mergesys) # Insert the link atoms into the correct layers. for fragid, frag in linksub.items(): for at in frag: if at.is_link: for k in range(0, len(cutoffs)): if fragid in qmsys[k]: qmsys[k][fragid] += [at] break return qmsys, mmsys
def _example(): """ Postprocessing Example """ from BigDFT.Systems import System, FragmentView from BigDFT.Fragments import Fragment from BigDFT.IO import XYZReader from BigDFT.Calculators import SystemCalculator from BigDFT.Inputfiles import Inputfile from scipy.linalg import eigh from copy import deepcopy # Create a system sys = System() sys["FRA:0"] = Fragment() with XYZReader("CH2") as ifile: for at in ifile: sys["FRA:0"].append(at) sys["FRA:1"] = Fragment() with XYZReader("CH3") as ifile: for at in ifile: sys["FRA:1"].append(at) sys["FRA:1"].translate([0, 0, -3]) sys["FRA:2"] = deepcopy(sys["FRA:0"]) sys["FRA:2"].translate([0, 0, 3]) sys["FRA:2"].rotate(y=150, units="degrees") sys["FRA:3"] = deepcopy(sys["FRA:0"]) sys["FRA:3"].translate([3, 0, 1.5]) print(list(sys)) # Run a calculation. `write_support_function_matrices` and `linear` are # key. You also should be sure to set the atom multipoles. inp = Inputfile() inp.set_xc("PBE") inp.set_hgrid(0.4) inp.write_support_function_matrices() inp["import"] = "linear" code = SystemCalculator() code.update_global_options(verbose=False) log = code.run(input=inp, posinp=sys.get_posinp(), run_dir="work") sys.set_atom_multipoles(log) # Create the post processing tool. from BigDFT.PostProcessing import BigDFTool btool = BigDFTool() # Purity purity = btool.run_compute_purity(sys, log) print(purity) # Charges charges = { fragid: sum(at.nel for at in frag) for fragid, frag in sys.items() } # Bond Orders bo = btool.fragment_bond_order(sys, sys.keys(), sys.keys(), log) # Population values. population = btool.fragment_population(sys, log) print(population) # These three things define a fragment view. view = FragmentView(purity, bo, charges) # Auto Fragmentation mapping = btool.auto_fragment(sys, view, 0.10) print(mapping) # This defines a new view. new_view = view.refragment(mapping) print(new_view.purities) # Eigenvalues. H = btool.get_matrix_h(log) S = btool.get_matrix_s(log) w = eigh(H.todense(), b=S.todense(), eigvals_only=True) print(w)
def _example(): """Test the XYZ Module""" from BigDFT.Systems import System from BigDFT.Fragments import Fragment from BigDFT.UnitCells import UnitCell file = "Si4" safe_print("First let's try reading an XYZ file.") atom_list = [] with XYZReader(file) as reader: safe_print(reader.closed) for at in reader: atom_list.append(at) safe_print(reader.closed) safe_print(atom_list) safe_print() safe_print("Now let's try writing an XYZ file.") safe_print() with XYZWriter("test.xyz", len(atom_list), units=reader.units) as writer: safe_print(writer.closed) for at in atom_list: writer.write(at) safe_print(writer.closed) safe_print() with open("test.xyz") as ifile: for line in ifile: safe_print(line, end='') safe_print() safe_print("Print with various boundary conditions") with XYZWriter("test.xyz", len(atom_list), reader.units, cell=UnitCell()) as writer: for at in atom_list: writer.write(at) with XYZReader("test.xyz") as ifile: print(ifile.cell.get_boundary_condition()) with XYZWriter("test.xyz", len(atom_list), reader.units, cell=UnitCell([5, 5, 5])) as writer: for at in atom_list: writer.write(at) with XYZReader("test.xyz") as ifile: print(ifile.cell.get_boundary_condition()) with XYZWriter("test.xyz", len(atom_list), reader.units, cell=UnitCell([5, float("inf"), 5])) as writer: for at in atom_list: writer.write(at) with XYZReader("test.xyz") as ifile: print(ifile.cell.get_boundary_condition()) with XYZWriter("test.xyz", len(atom_list), reader.units, cell=UnitCell([float("inf"), float("inf"), 5])) as writer: for at in atom_list: writer.write(at) with XYZReader("test.xyz") as ifile: print(ifile.cell.get_boundary_condition()) safe_print() safe_print("Now let's demonstrate the pdb and mol2 writer") sys = System() sys["FRAG:0"] = Fragment(atom_list) with open("test.pdb", "w") as ofile: write_pdb(sys, ofile) with open("test.pdb") as ifile: for line in ifile: safe_print(line, end='') safe_print() with open("test.mol2", "w") as ofile: write_mol2(sys, ofile) with open("test.mol2") as ifile: for line in ifile: safe_print(line, end='') safe_print()
def _example(): """Example of using ASE interoperability""" from BigDFT.IO import XYZReader from BigDFT.Inputfiles import Inputfile from BigDFT.Calculators import SystemCalculator from BigDFT.Fragments import Fragment from BigDFT.Systems import System from ase.calculators.lj import LennardJones from BigDFT.UnitCells import UnitCell from ase.units import Hartree from ase.optimize import BFGS from os.path import join from copy import deepcopy # Create a system. sys = System() reader = XYZReader("Ar") sys["FRA:1"] = Fragment(xyzfile=reader) sys["FRA:2"] = deepcopy(sys["FRA:1"]) sys["FRA:2"].translate([-2, 0, 0]) # Skip straight to the potential energy. print(ase_potential_energy(sys, LennardJones())) # Advanced used. asys = bigdft_to_ase(sys) asys.set_calculator(LennardJones()) dyn = BFGS(asys) dyn.run(fmax=0.05) print(asys.get_potential_energy() / Hartree) # Unit cells sys.cell = UnitCell([5, 5, 5], units="bohr") print(ase_potential_energy(sys, LennardJones())) sys.cell = UnitCell([5, float("inf"), 5], units="bohr") print(ase_potential_energy(sys, LennardJones())) sys.cell = UnitCell([float("inf"), float("inf"), 5], units="bohr") print(ase_potential_energy(sys, LennardJones())) # We can also use BigDFT with ase. inp = Inputfile() inp.set_xc("PBE") inp.set_hgrid(0.4) code = SystemCalculator(verbose=False) sys.cell = UnitCell() print( ase_potential_energy( sys, BigASECalculator(inp, code, directory="work", label="ase-free"))) sys.cell = UnitCell([5, 5, 5], units="bohr") print( ase_potential_energy( sys, BigASECalculator(inp, code, directory="work", label="ase-periodic"))) sys.cell = UnitCell([5, float("inf"), 5], units="bohr") print( ase_potential_energy( sys, BigASECalculator(inp, code, directory="work", label="ase-surface"))) sys.cell = UnitCell([float("inf"), float("inf"), 5], units="bohr") print( ase_potential_energy( sys, BigASECalculator(inp, code, directory="work", label="ase-wire")))
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
def pre_processing(self): """ Process local run dictionary to create the input directory and identify the command to be passed Returns: :py:class:`dict`: dictionary containing the command to be passed to :meth:`process_run` """ from psi4 import set_options, energy, optimize, frequency from psi4.core import set_output_file from BigDFT.Systems import System from os.path import join # Check Arguments try: self.sys = self.run_options["sys"] except KeyError: raise ValueError("sys= must be provided as an argument") try: action = self.run_options["action"] except KeyError: raise ValueError("You must specify a valid action=(energy," " optimize, frequency") try: method = self.run_options["method"] except KeyError: raise ValueError("You must specify an ab initio method=") try: basis = self.run_options["basis"] except KeyError: raise ValueError("You must specify a basis=") # Run directory self._ensure_run_directory() # Check skip if self.run_options["skip"]: try: log = PSI4Logfile(self.sys, self._get_logname(True), action, method) return {"command": None} except ValueError: # invalid logfile, so we can't skip. pass except IOError: # no logfile, so we can't skip. pass # Convert system if "sapt" in method: keys = list(self.sys) if len(keys) != 2: raise ValueError("For SAPT method, your system must be" " composed of exactly two fragments.") sysA = System() sysA[keys[0]] = self.sys[keys[0]] sysB = System() sysB[keys[1]] = self.sys[keys[1]] mol = bigdft_to_psi4(sysA=sysA, sysB=sysB) else: mol = bigdft_to_psi4(sysA=self.sys) # Set extra options. if "psi4_options" in self.run_options: set_options(self.run_options["psi4_options"]) set_output_file(self._get_logname(True), False) # Instead of a command, we return a closure. if action == "energy": def cmd(method, mol): energy(method, molecule=mol) elif action == "optimize": def cmd(method, mol): optimize(method, molecule=mol) elif action == "frequency": def cmd(method, mol): frequency(method, molecule=mol) return {"command": cmd(join(method, basis), mol)}
def create_qmmm_system(self, system, target, bond_order, cutoff, criteria="bondorder", link_atoms=False): """ Creates a system suitable for QM/MM calculations. Args: system (System): a System class, already broken up into fragments. target (str): the name of the fragment to treat as the target of the qm/mm run. bond_order (dict): bond order values between the target fragment and all other fragments in the system. cutoff (float): a cutoff value for fragment interactions. criteria (str): how to determine which atoms are included in the QM region. Valid choices are "bondorder" and "distance". link_atoms (bool): whether to generate link atoms. Returns: (System): the QM region. (System): the MM region. """ from BigDFT.Systems import System from BigDFT.Fragments import pairwise_distance from copy import deepcopy qmsys = System() mmsys = deepcopy(system) if criteria == "bondorder": # Order the fragments by their spillage value sort_spill = sorted(bond_order.keys(), key=lambda x: bond_order[x], reverse=True) # Iterate until we reach the cutoff cumsum = sum(bond_order.values()) for key in sort_spill: qmsys[key] = deepcopy(system[key]) del mmsys[key] # check cutoff cumsum -= bond_order[key] if cumsum < cutoff: break elif criteria == "distance": interaction = { x: pairwise_distance(system[target], system[x]) for x in system.keys() } for fragid, frag in system.items(): if interaction[fragid] < cutoff: qmsys[fragid] = deepcopy(frag) del mmsys[fragid] else: raise ValueError("Criteria must be either distance or bondorder") # Double check that the target is in qmsys. # (this can happen if the target is not a pure fragment) if target not in qmsys: qmsys[target] = system[target] if link_atoms: qmsys, remove = self.generate_link_atoms(system, qmsys) return qmsys, mmsys
def generate_link_atoms(self, fullsys, subsys, distcut=6.0): """ This routine adds link atoms to a subsystem based on the bond order of a full system. Link atom positions are automatically adjusted based on the length of some standard bonds. Args: fullsys (BigDFT.Systems.System): the full system that the subsystem is embedded into. subsys (BigDFT.Systems.System): the embedded system which needs link atoms. distcut (float): this cutoff is the largest distance value we expect allow a bond to be. Returns: (BigDFT.Systems.System): the subsystem with link atoms added. (BigDFT.Systems.System): a system which has the atoms that were removed and replaced with link atoms. """ from BigDFT.Systems import System from BigDFT.Fragments import Fragment from copy import deepcopy from numpy.linalg import norm from numpy import array from warnings import warn # Bond lengths for link atoms. bond_lengths = { "C": 2.0598, "N": 1.90862, "O": 1.81414, "F": 1.73855, "P": 2.68341, "S": 2.53223, "Cl": 2.39995, "Br": 2.66451, "I": 3.04246 } if fullsys.conmat is None: raise ValueError("Generating link atoms requires connectivity" " information") linksys = deepcopy(subsys) removesys = System() # Loop over atoms and look for bonds running out of the QM system. for fragid in subsys: linklist = [] for i in range(0, len(fullsys[fragid])): for ft, bv in fullsys.conmat[fragid][i].items(): if ft[0] in subsys.keys(): continue if bv == 1: newat = deepcopy(fullsys[ft[0]][ft[1]]) # Change the symbol to hydrogen newat.sym = "H" newat.is_link = True # If possible we adjust the position for a reasonable # bond length conat = fullsys[fragid][i] if conat.sym in bond_lengths: pos1 = array(conat.get_position("bohr")) pos2 = array(newat.get_position("bohr")) vec = pos2 - pos1 vec *= bond_lengths[conat.sym] / norm(vec) newpos = [x + y for x, y in zip(pos1, vec)] newat.set_position(newpos, units="bohr") else: warn(conat.sym + "bondlength unknown", UserWarning) linklist.append(newat) elif bv > 1: print("Not yet implemented double/triple bonds.", bv) raise NotImplementedError if ft[0] not in removesys: removesys[ft[0]] = Fragment() removesys[ft[0]] += [fullsys[ft[0]][ft[1]]] # Add those atoms to the fragment for link in linklist: linksys[fragid] += [link] return linksys, removesys
def read_pdb(ifile, include_chain=False, disable_warnings=False): """ Read a system from a PDB file. Args: ifile (TextIOBase): the file to read from. disable_warnings (bool): whether to print warnings about possible file issues. include_chain (bool): include the chain id if True Warning: This will read in the connectivity information from the pdb as well. However, a pdb file does not provide any information about the bond order. Thus, the bond order of each bond will be set to one. Returns: (BigDFT.Systems.System): the system file. """ from BigDFT.Fragments import Fragment from BigDFT.Systems import System from warnings import warn from BigDFT.UnitCells import UnitCell # First pass read in the atoms. sys = System() lookup = {} sys.conmat = {} found = False for line in ifile: try: # See if there is an atom on this line if line[:4] == "ATOM" or line[:6] == "HETATM": at, atid, fragid = _process_atom(line, include_chain=include_chain) # We can ignore lone pairs if at.sym == "Lp": continue # Add to the system if fragid not in sys: sys[fragid] = Fragment() sys[fragid] += [at] # Build the lookup table lookup[atid] = (fragid, len(sys[fragid]) - 1) elif line[:6] == "CONECT": found = True split = _split_line(line, len(lookup)) (fragid, atnum) = lookup[int(split[1])] for at2 in split[2:]: fragid2, atnum2 = lookup[int(at2)] if fragid not in sys.conmat: sys.conmat[fragid] = [] for i in range(0, len(sys[fragid])): sys.conmat[fragid].append({}) sys.conmat[fragid][atnum][(fragid2, atnum2)] = 1.0 elif line[:6] == "CRYST1": a = float(line[7:15]) b = float(line[16:24]) c = float(line[25:33]) alpha = float(line[34:40]) beta = float(line[41:47]) gamma = float(line[48:54]) 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) except IndexError: # For shorter lines continue if not found: sys.conmat = None else: # for any connectivity not specified we give default values. for fragid in sys: if fragid not in sys.conmat: sys.conmat[fragid] = [] for i in range(0, len(sys[fragid])): sys.conmat[fragid].append({}) if not disable_warnings: if sum([len(x) for x in sys.values()]) == 0: warn("Warning: zero atoms found", UserWarning) return sys