def prepare_sapt_molecule(sapt_dimer: core.Molecule, sapt_basis: str) -> Tuple[core.Molecule, core.Molecule, core.Molecule]: """ Prepares a dimer molecule for a SAPT computations. Returns the dimer, monomerA, and monomerB. """ # Shifting to C1 so we need to copy the active molecule sapt_dimer = sapt_dimer.clone() if sapt_dimer.schoenflies_symbol() != 'c1': core.print_out(' SAPT does not make use of molecular symmetry, further calculations in C1 point group.\n') sapt_dimer.reset_point_group('c1') sapt_dimer.fix_orientation(True) sapt_dimer.fix_com(True) sapt_dimer.update_geometry() else: sapt_dimer.update_geometry() # make sure since mol from wfn, kwarg, or P::e sapt_dimer.fix_orientation(True) sapt_dimer.fix_com(True) nfrag = sapt_dimer.nfragments() if nfrag == 3: # Midbond case if sapt_basis == 'monomer': raise ValidationError("SAPT basis cannot both be monomer centered and have midbond functions.") midbond = sapt_dimer.extract_subsets(3) ztotal = 0 for n in range(midbond.natom()): ztotal += midbond.Z(n) if ztotal > 0: raise ValidationError("SAPT third monomer must be a midbond function (all ghosts).") ghosts = ([2, 3], [1, 3]) elif nfrag == 2: # Classical dimer case ghosts = (2, 1) else: raise ValidationError('SAPT requires active molecule to have 2 fragments, not %s.' % (nfrag)) if sapt_basis == 'dimer': monomerA = sapt_dimer.extract_subsets(1, ghosts[0]) monomerA.set_name('monomerA') monomerB = sapt_dimer.extract_subsets(2, ghosts[1]) monomerB.set_name('monomerB') elif sapt_basis == 'monomer': monomerA = sapt_dimer.extract_subsets(1) monomerA.set_name('monomerA') monomerB = sapt_dimer.extract_subsets(2) monomerB.set_name('monomerB') else: raise ValidationError("SAPT basis %s not recognized" % sapt_basis) return (sapt_dimer, monomerA, monomerB)
def compute_hessian(self, molecule: core.Molecule, wfn: core.Wavefunction = None) -> core.Matrix: """Compute dispersion Hessian based on engine, dispersion level, and parameters in `self`. Uses finite difference, as no dispersion engine has analytic second derivatives. Parameters ---------- molecule System for which to compute empirical dispersion correction. wfn Location to set QCVariables Returns ------- Matrix (3*nat, 3*nat) dispersion Hessian [Eh/a0/a0]. """ optstash = p4util.OptionsState(['PRINT'], ['PARENT_SYMMETRY']) core.set_global_option('PRINT', 0) core.print_out( "\n\n Analytical Dispersion Hessians are not supported by dftd3 or gcp.\n" ) core.print_out( " Computing the Hessian through finite difference of gradients.\n\n" ) # Setup the molecule molclone = molecule.clone() molclone.reinterpret_coordentry(False) molclone.fix_orientation(True) molclone.fix_com(True) # Record undisplaced symmetry for projection of diplaced point groups core.set_global_option("PARENT_SYMMETRY", molecule.schoenflies_symbol()) findif_meta_dict = driver_findif.hessian_from_gradients_geometries( molclone, -1) for displacement in findif_meta_dict["displacements"].values(): geom_array = np.reshape(displacement["geometry"], (-1, 3)) molclone.set_geometry(core.Matrix.from_array(geom_array)) molclone.update_geometry() displacement["gradient"] = self.compute_gradient( molclone).np.ravel().tolist() H = driver_findif.assemble_hessian_from_gradients(findif_meta_dict, -1) if wfn is not None: wfn.set_variable('DISPERSION CORRECTION HESSIAN', H) optstash.restore() return core.Matrix.from_array(H)