def update_coor(self, mol): crd = [] for i in self.sel: i3 = i * 3 crd.append([ (mol.coor[i3 + j] - mol.boxl[j] * round(mol.coor[i3 + j] / mol.boxl[j], 0)) / qm3.constants.A0 for j in [0, 1, 2] ]) self.vla = [] if (len(self.lnk) > 0): k = len(self.sel) for i, j in self.lnk: c, v = qm3.engines.LA_coordinates(i, j, mol) crd.append([c[j] / qm3.constants.A0 for j in [0, 1, 2]]) self.vla.append((self.sel.index(i), k, v[:])) k += 1 self.QMatm.set_geometry(psi4.core.Matrix.from_list(crd)) self.QMatm.update_geometry() if (len(self.nbn) > 0): self.MMatm = psi4.QMMM() self.MMatm.charges = [] f = open("grid.dat", "wt") for i in self.nbn: i3 = i * 3 t = [ mol.coor[i3 + j] - mol.boxl[j] * round(mol.coor[i3 + j] / mol.boxl[j], 0) for j in [0, 1, 2] ] self.MMatm.charges.append([mol.chrg[i], t[0], t[1], t[2]]) f.write("%20.10lf%20.10lf%20.10lf\n" % (t[0], t[1], t[2])) f.close() self.MMatm.populateExtern() psi4.core.set_global_option_python("EXTERN", self.MMatm.extern)
def set_up_psi4(self, be_quiet=True): """ Sets up a psi4 computation """ # psi4.core.set_output_file('output.dat', True) psi4.core.clean() psi4.core.clean_options() psi4.core.EXTERN = None # Supress print out if be_quiet is True: psi4.core.be_quiet() psi4.set_options(self.qm_param) psi4_geom = '\n' + str(self.charge) + ' ' + str( self.multiplicity) + '\n ' psi4_geom += self.qm_geometry psi4_geom += 'no_reorient \n' psi4_geom += 'no_com \n ' #print(psi4_geom) # make sure this is in angstroms mol = psi4.geometry(psi4_geom) if self.external_charges is not None: Chrgfield = psi4.QMMM() for charge in self.external_charges: Chrgfield.extern.addCharge(charge[0], charge[1], charge[2], charge[3]) psi4.core.set_global_option_python('EXTERN', Chrgfield.extern)
def gen_input(self, path=None): """Generate input file for QM software.""" psi4.set_options({ 'dft_functional': '%s' % self.method, 'basis': '%s' % self.basis }) if self.calc_forces: psi4.set_options({'e_convergence': 1e-8, 'd_convergence': 1e-8}) # if self.read_guess: # psi4.set_options({'guess': 'read'}) if self.addparam is not None: addparam = dict(self.addparam) psi4.set_options(addparam) geom = [] for i in range(self._n_qm_atoms): geom.append("".join([ "%3s" % self._qm_element[i], "%22.14e" % self._qm_position[i, 0], "%22.14e" % self._qm_position[i, 1], "%22.14e" % self._qm_position[i, 2], "\n" ])) geom.append("symmetry c1\n") geom = "".join(geom) molecule = psi4.geometry(geom) molecule.set_molecular_charge(self.charge) molecule.set_multiplicity(self.mult) molecule.fix_com(True) molecule.fix_orientation(True) molecule.update_geometry() mm_charge = psi4.QMMM() for i in range(self._n_mm_atoms): mm_charge.addChargeAngstrom(self._mm_charge[i], self._mm_position[i, 0], self._mm_position[i, 1], self._mm_position[i, 2]) mm_charge.populateExtern() psi4.core.set_global_option_python('EXTERN', mm_charge.extern) if path is None: path = self.basedir with open(os.path.join(path, "grid.dat"), 'w') as f: for i in range(self._n_mm_atoms): f.write("".join([ "%22.14e" % self._mm_position[i, 0], "%22.14e" % self._mm_position[i, 1], "%22.14e" % self._mm_position[i, 2], "\n" ]))
def set_lattice_field(self): """ Set a field of lattice point charges using information received through MDI """ self.lattice_field = psi4.QMMM() unit_conv = self.length_conversion() for ilat in range(self.nlattice): latx = self.clattice[3 * ilat + 0] * unit_conv laty = self.clattice[3 * ilat + 1] * unit_conv latz = self.clattice[3 * ilat + 2] * unit_conv self.lattice_field.extern.addCharge(self.lattice[ilat], latx, laty, latz) psi4.core.set_global_option_python('EXTERN', self.lattice_field.extern) self.set_lattice = True
def createChargefield(fragList, fragsToInclude=[]): charges = [] chrgfield = psi4.QMMM() for i, frag in enumerate(fragList): if i in fragsToInclude: for j in range(fragList[frag][0].natom()): Q = np.array(fragList[frag][1].atomic_point_charges())[j] X, Y, Z = [fragList[frag][0].x(j), fragList[frag][0].y(j), fragList[frag][0].z(j)] chrgfield.addChargeBohr(Q, X, Y, Z) chrgfield.populateExtern() psi4.core.set_global_option_python('EXTERN', chrgfield.extern) return
def generate_geometry(self): """ Create the geometry string to feed into the Psi4 calculation. """ # Use unrestricted Kohn-Sham if molecule is not in the singlet # state. qm_atom_list = self._group_part_dict["qm_atom"] qm_centroid = np.array([ sum([self._positions[atom][k] for atom in qm_atom_list]) / len(qm_atom_list) for k in range(3) ]) if self.qm_spin > 1: psi4.core.set_local_option("SCF", "REFERENCE", "UKS") # Add MM charges in QMregion for analytic embedding. if "analytic" in self._group_part_dict: embedding_list = self._group_part_dict["analytic"] charge_field = psi4.QMMM() for residue in embedding_list: nth_centroid = [ sum([self._positions[atom][k] for atom in residue]) / len(residue) for k in range(3) ] new_centroid = least_mirror_vector( nth_centroid, qm_centroid, self._box, ) displacement = np.array(new_centroid) - np.array(nth_centroid) for atom in residue: position = self._positions[ atom] + displacement + qm_centroid charge_field.extern.addCharge( self._charges[atom], position[0], position[1], position[2], ) psi4.core.set_global_option_python("EXTERN", charge_field.extern) # Construct geometry string. geometrystring = (' \n ' + str(self.qm_charge) + " " + str(self.qm_spin) + " \n" + " noreorient \n " + " nocom \n ") for atom in qm_atom_list: position = self._positions[atom] geometrystring = (geometrystring + " " + str(self._element_symbols[atom]) + " " + str(position[0]) + " " + str(position[1]) + " " + str(position[2]) + " \n") geometrystring = geometrystring + ' symmetry c1 \n ' # now create Psi4 geometry object. self.geometry = psi4.geometry(geometrystring)
def parse(inp): geom_str = '' bq_list = [] options = {} for line in inp: if 'GEOM' in line: print 'found geom' for line2 in inp: if 'END' in line2: break else: geom_str += line2 # GEOM IN ANGSTROM if 'BQ_CHARGES' in line: for line2 in inp: if 'END' in line2: break else: bq = np.fromstring(line2, sep=' ') # BQ POS ALSO ANGSTROM bq_list.append(bq) dat = line.split() if len(dat) > 1: key = dat[0].lower() value = dat[-1].lower() options[key] = value geom_str += "\nsymmetry c1\nno_reorient\nno_com\n" print "GEOM" print geom_str print "CALCTYPE", options['calc_type'] mol = psi4.geometry(geom_str) mol.update_geometry() calc_type = 'energy' if 'calc_type' not in options else options['calc_type'] mult = 1 if 'mult' not in options else int(options['mult']) charge = 0 if 'charge' not in options else int(options['charge']) if mult != 1: mol.set_multiplicity(mult) if charge != 0: mol.set_molecular_charge(charge) options['bq_list'] = bq_list bqfield = psi4.QMMM() # Create external potential in angstrom for bq in bq_list: bqfield.extern.addCharge(bq[0], bq[1], bq[2], bq[3]) return calc_type, mol, bqfield, options
def inp(calc, atoms, bqs, charge, noscf=False, guess=None, save=False): '''Prepare Psi4 module by setting geometry and BQ field''' geom_str = '\n'.join(map(str, atoms)) geom_str += "\nsymmetry c1\nno_reorient\nno_com\n" mol = psi4.geometry(geom_str) mol.update_geometry() nelec = sum(geom.z_map[at.sym] for at in atoms) - charge mult = nelec % 2 mol.set_multiplicity(mult) mol.set_molecular_charge(charge) bqfield = psi4.QMMM() for bq in bqs: bqfield.extern.addCharge(bq[3], bq[0], bq[1], bq[2]) return mol, bqfield, bqs
def set_up_psi4(self, be_quiet=True): """ Sets up a psi4 computation """ # psi4.core.set_output_file('output.dat', True) psi4.core.clean() psi4.core.clean_options() psi4.core.EXTERN = None # Supress print out if be_quiet is True: psi4.core.be_quiet() #print("GEO", self.qm_geometry) print("QM Params", self.qm_param) params = {} for k in self.qm_param.keys(): if "sys_" not in k: params[k] = self.qm_param[k] self.qm_param = params print("QM Params", self.qm_param) psi4.set_options(self.qm_param, True) psi4_geom = '\n' + str(self.charge ) + ' ' + str(self.multiplicity) + '\n ' psi4_geom += self.qm_geometry psi4_geom += 'no_reorient \n' psi4_geom += 'no_com \n ' print(psi4_geom) # make sure this is in angstroms mol = psi4.geometry(psi4_geom) print("PSI4 GEO done") if self.external_charges is not None: Chrgfield = psi4.QMMM() print("external charges", self.external_charges) for charge in self.external_charges: Chrgfield.extern.addCharge(charge[0], charge[1], charge[2], charge[3]) psi4.core.set_global_option_python('EXTERN', Chrgfield.extern) print("Psi4 params set up done")
def createChargefield(fragList, fragsToInclude=[], new=False): charges = [] fragCharges = [] chrgfield = psi4.QMMM() rmsd = [] if new == True: psi4.core.print_out('\n\nComputing ESP of fragment:\n') chargeCloud = pointCloudToPrint() for i, frag in enumerate(fragList): if i in fragsToInclude: if new == True: psi4.core.timer_on('createChargefieldFromESP') psi4.core.print_out(f'Fragment: {i+1}/{len(fragsToInclude)}\n') charges, pointsX, pointsY, pointsZ = calcESP( fragList[frag][0], fragList[frag][1]) if frag in pointsDict.keys(): rmsd += [rmsdFunc(pointsDict[frag][0], charges)] fragCharges += [np.sum(charges)] pointsDict[frag] = [charges, pointsX, pointsY, pointsZ] chargeCloud.append(charges, pointsX, pointsY, pointsZ) psi4.core.timer_off('createChargefieldFromESP') else: charges, pointsX, pointsY, pointsZ = pointsDict[frag] charges = np.add( charges, np.divide( globals()['systemCharge'] - globals()['chargeTotal'], globals()['numChargePoints'])) for j, charge in enumerate(charges): chrgfield.addChargeBohr(charge, pointsX[j], pointsY[j], pointsZ[j]) if new == True: chargeCloud.write() fragCharges = np.add( fragCharges, np.divide(globals()['systemCharge'] - globals()['chargeTotal'], globals()['numChargePoints'])) psi4.core.print_out(f'\n\n') chrgfield.populateExtern() psi4.core.set_global_option_python('EXTERN', chrgfield.extern) return (rmsd)
def test_qmmm_class_error(): with pytest.raises(psi4.UpgradeHelper) as e: psi4.QMMM() assert 'Replace object with a list of charges and locations in Bohr passed as keyword argument' in str(e.value)
def __init__(self, scf_method, **kwargs): """ Initialize an MDIEngine object for communication with MDI Arguments: scf_method: Method used when calculating energies or gradients """ # Method used when the SCF command is received self.scf_method = scf_method # Additional arguments for energy, gradient, or optimization calculations self.kwargs = kwargs # Molecule all MDI operations are performed on input_molecule = kwargs.pop('molecule', psi4.core.get_active_molecule()) self.molecule = input_molecule.clone() psi4.core.set_active_molecule(self.molecule) # Most recent SCF energy self.energy = 0.0 # Variables used when MDI sets a lattice of point charges self.nlattice = 0 # number of lattice point charges self.clattice = [] # list of lattice coordinates self.lattice = [] # list of lattice charges self.lattice_field = psi4.QMMM() # Psi4 chargefield # MPI variables self.mpi_world = None self.world_rank = 0 # Flag for if a lattice of point charges has been set self.set_lattice = False # Get correct intra-code MPI communicator if use_mpi4py: self.mpi_world = MDI_MPI_get_world_comm() self.world_rank = self.mpi_world.Get_rank() # Psi4 does not currently support multiple MPI ranks if self.mpi_world.Get_size() != 1: MPI.COMM_WORLD.Abort() # Accept a communicator to the driver code self.comm = MDI_Accept_Communicator() # Ensure that the molecule is using c1 symmetry self.molecule.reset_point_group('c1') self.molecule.fix_orientation(True) self.molecule.fix_com(True) self.molecule.reinterpret_coordentry(False) self.molecule.update_geometry() # Flag to stop listening for MDI commands self.stop_listening = False # Dictionary of all supported MDI commands self.commands = { "<NATOMS": self.send_natoms, "<COORDS": self.send_coords, "<CHARGES": self.send_charges, "<ELEMENTS": self.send_elements, "<MASSES": self.send_masses, "<ENERGY": self.send_energy, "<FORCES": self.send_forces, ">COORDS": self.recv_coords, ">NLATTICE": self.recv_nlattice, ">CLATTICE": self.recv_clattice, ">LATTICE": self.recv_lattice, ">MASSES": self.recv_masses, "SCF": self.run_scf, "<DIMENSIONS": self.send_dimensions, "<TOTCHARGE": self.send_total_charge, ">TOTCHARGE": self.recv_total_charge, "<ELEC_MULT": self.send_multiplicity, ">ELEC_MULT": self.recv_multiplicity, "EXIT": self.exit } # Register all the supported commands MDI_Register_Node("@DEFAULT") for command in self.commands.keys(): MDI_Register_Command("@DEFAULT", command)
def __init__(self, mol, opts, sele, nbnd=[], link=[]): """ opts = { "reference": "rks", "basis": "6-31g*", "d_convergence": 6, "scf_type": "direct", "guess": "read", "output": False, "charge": 0, "method": "b3lyp", "ncpus": 1, "memory": "1024 MB" } """ self._ce = qm3.constants.H2J self._cg = self._ce / qm3.constants.A0 self.sel = sele[:] self.lnk = link[:] t = [j for i, j in self.lnk] self.nbn = [i for i in nbnd if not i in t] self.smb = mol.guess_symbols(sele) buf = "\n%d 1\n" % (opts.pop("charge")) j = 0 for i in sele: i3 = i * 3 buf += "%-2s%20.10lf%20.10lf%20.10lf\n" % ( self.smb[j], mol.coor[i3] - mol.boxl[0] * round(mol.coor[i3] / mol.boxl[0], 0), mol.coor[i3 + 1] - mol.boxl[1] * round(mol.coor[i3 + 1] / mol.boxl[1], 0), mol.coor[i3 + 2] - mol.boxl[2] * round(mol.coor[i3 + 2] / mol.boxl[2], 0)) j += 1 self.vla = [] if (len(self.lnk) > 0): k = len(self.sel) for i, j in self.lnk: c, v = qm3.engines.LA_coordinates(i, j, mol) buf += "%-2s%20.10lf%20.10lf%20.10lf\n" % ("H", c[0], c[1], c[2]) self.vla.append((self.sel.index(i), k, v[:])) k += 1 # -- psi4 -- if (opts.pop("output")): psi4.core.set_output_file("psi4.out", False) else: psi4.core.be_quiet() psi4.set_memory(opts.pop("memory")) psi4.set_num_threads(opts.pop("ncpus")) buf += "symmetry c1\nno_reorient\nno_com\n" self.QMatm = psi4.geometry(buf) psi4.activate(self.QMatm) if (len(self.nbn) > 0): self.MMatm = psi4.QMMM() self.MMatm.charges = [] for i in self.nbn: i3 = i * 3 self.MMatm.charges.append([ mol.chrg[i], mol.coor[i3] - mol.boxl[0] * round(mol.coor[i3] / mol.boxl[0], 0), mol.coor[i3 + 1] - mol.boxl[1] * round(mol.coor[i3 + 1] / mol.boxl[1], 0), mol.coor[i3 + 2] - mol.boxl[2] * round(mol.coor[i3 + 2] / mol.boxl[2], 0) ]) self.MMatm.populateExtern() psi4.core.set_global_option_python("EXTERN", self.MMatm.extern) self.met = opts.pop("method") psi4.set_options(opts)