Пример #1
0
 def test_flosic_os(self):
     # DFT
     mf = dft.UKS(mol)
     mf.verbose = 4  # Amount of output. 4: full output.
     mf.max_cycle = 300  # Number of SCF iterations.
     mf.conv_tol = 1e-6  # Accuracy of the SCF cycle.
     mf.grids.level = 7  # Level of the numerical grid. 3 is the standard value.
     mf.xc = 'LDA,PW'  # Exchange-correlation functional in the form: (exchange,correlation)
     mf.kernel()
     # FLO-SIC OS
     results = flosic(mol, mf, fod1, fod2)
     e_calc = results['etot_sic']
     e_ref = -40.69057092300857
     self.assertAlmostEqual(e_calc, e_ref, 5)
Пример #2
0
            charge=charge)

# Set up the DFT calculation.

dft_object = dft.UKS(mol)
dft_object.verbose = verbose
dft_object.max_cycle = max_cycle
dft_object.conv_tol = conv_tol
dft_object.grids.level = grids_level
dft_object.xc = xc

# Perform the DFT calculation.

dft_energy = dft_object.kernel()

# Apply FLO-SIC to the DFT calculation.

flosic_values_2 = flosic(mol, dft_object, fod1, fod2)

# Output the results. The output for FLO-SIC is given in the form of Python dictionaries.

print("ESIC: {}".format(flosic_values_1['etot_sic'] - dft_energy))

print('Total energy of H2 (DFT): %0.5f (should be %0.5f)' %
      (dft_energy, -1.13634167738585))
print('Total energy of H2 (FLO-SIC FULL): %0.5f (should be %0.5f) ' %
      (flosic_values_1['etot_sic'], -1.18032726019))
print(
    'Total energy of H2 (FLO-SIC POST-PROCESSING): % 0.5f (should be %0.5f) ' %
    (flosic_values_2['etot_sic'], -1.18032726019))
Пример #3
0
mf.max_cycle = maxcycle
mf.conv_tol = convtol
mf.grids.level = gridlevel
mf.xc = xc
mf.kernel()

# Get the DFT density.

ao = dft.numint.eval_ao(mol, mf.grids.coords, deriv=0)
dm_dft = mf.make_rdm1()
rho_dft = dft.numint.eval_rho(mol, ao, dm_dft[0], None, 'LDA', 0, None)

# Do the FLOSIC OS.

print('Starting FLOSIC calculation in OS mode.')
flosic_values = flosic(mol, mf, fod1, fod2)
flo = flosic_values['flo']

# Get the FLOSIC OS density.

dm_flo = dynamic_rdm(flo, mf.mo_occ)
rho_flo_os = dft.numint.eval_rho(mol, ao, dm_flo[0], None, 'LDA', 0, None)

# Get the mesh.

mesh = mf.grids.coords

# Do the FLOSIC SCF.

print('Starting FLOSIC calculation in SCF mode.')
mf2 = FLOSIC(mol, xc=xc, fod1=fod1, fod2=fod2)
Пример #4
0
def get_flosic_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1):
    # pyscf standard call for scf cycle 0.
    veff = uks.get_veff(ks=ks.calc_uks,
                        mol=ks.mol,
                        dm=dm,
                        dm_last=dm_last,
                        vhf_last=vhf_last,
                        hermi=hermi)

    if mol is None: mol = ks.mol
    # Build the hamiltonian to get the KS wave functions.
    dim = np.shape(ks.calc_uks.mo_coeff)
    s1e = ks.get_ovlp(mol)
    h1e = ks.get_hcore(mol)
    hamil = ks.get_fock(h1e, s1e, vhf_last, dm)

    # Get the KSO.
    ks_new = np.zeros((2, dim[1], dim[1]), dtype=np.float64)

    try:
        if dm_last == 0:
            # First SCF cycle: do nothing.
            pass
    except:
        # Every other DFT cycle: Build the KS wavefunctions with the Hamiltonian, then give them to the UKS object that is the input for flosic.
        trash, ks_new = ks.eig(hamil, s1e)
        ks_inter = np.array(ks_new)

        # Update UKS object.
        ks.calc_uks.mo_coeff = ks_inter.copy()

    # If ldax is enabled, the xc functional is set to LDA exchange only.
    if ks.ldax == True:
        xc_sav = ks.calc_uks.xc
        ks.calc_uks.xc = 'LDA,'

    # Call the FLOSIC routine with the UKS object.

    # This for the fixed Vsic modus.

    # If Vsic values are present and the Vsic potential should not
    # be updated use these values.
    # (THa: the outer if ... clause was added to prevent the
    # sic potentials to be calculated during initialization)
    _t0 = time.time()
    #print('>> ks.fixed_vsic', ks.fixed_vsic)
    #print('>>', ks.neval_vsic)
    #sys.exit()
    if ks.neval_vsic > -1:
        if ks.fixed_vsic != 0.0 and (ks.num_iter % ks.vsic_every) != 0:
            if ks.verbose >= 4:
                print('Use fixed Vsic (cycle = %i)' % ks.num_iter)
            flo_veff = flosic(ks.mol, ks.calc_uks, ks.fod1, ks.fod2,\
            datatype=np.float64,calc_forces=ks.calc_forces,debug=ks.debug,\
            nuclei=ks.nuclei,l_ij=ks.l_ij,ods=ks.ods,\
            fixed_vsic=ks.fixed_vsic,ham_sic=ks.ham_sic)

        # If no Vsic values are present or the the Vsic values should be
        # updated calcualte new Vsic values.

        # !! THa: Possible BUG
        # ks.fixed_vsic == 0.0 may never be 'True' because
        # a float-value is amost never exactly zero
        # better use: np.isclose(ks.fixed_vsic, 0.0)
        # !!
        if ks.fixed_vsic == 0.0 or (ks.num_iter % ks.vsic_every) == 0:
            if ks.verbose >= 4:
                print('Calculate new Vsic (cycle = %i)' % ks.num_iter)
            flo_veff = flosic(ks.mol,ks.calc_uks,ks.fod1,ks.fod2,\
            datatype=np.float64,calc_forces=ks.calc_forces,debug=ks.debug,\
            nuclei=ks.nuclei,l_ij=ks.l_ij,ods=ks.ods,ham_sic=ks.ham_sic)
            ks.fixed_vsic = flo_veff['fixed_vsic']
    else:
        flo_veff = veff

    _t1 = time.time()
    if mol.verbose > 3:
        print("FLO-SIC time for SIC potential: {0:0.1f} [s]".format(_t1 - _t0))
    # increase a magic counter
    ks.num_iter = ks.num_iter + 1

    # If ldax is enabled, the change to xc is only meant for the FLO-SIC part and
    # therefore has to be changed back.
    if ks.ldax == True:
        ks.calc_uks.xc = xc_sav

    # Assign the return values.
    # The total energies of DFT and FLO-SIC
    if ks.neval_vsic > -1:
        sic_etot = flo_veff['etot_sic']
        dft_etot = flo_veff['etot_dft']
        # The FLOs.
        ks.flo = flo_veff['flo']
        # The FOD forces.
        ks.fforces = flo_veff['fforces']
        # The FLO-SIC H**O energy eigenvalue.
        ks.homo_flosic = flo_veff['homo_sic']
        ks.evalues = flo_veff['evalues']
        ks.lambda_ij = flo_veff['lambda_ij']
        # Developer modus: atomic forces (AF)
        if ks.debug == True:
            ks.AF = flo_veff['AF']
    else:
        sic_etot = ks.e_tot
        dft_etot = ks.e_tot
        ks.flo = ks.mo_coeff
        ks.homo_flosic = 0.0

    try:
        # First SCF cycle: veff = veff_dft and the SIC is zero.
        if dm_last == 0:
            sic_veff = veff
            sic_etot = dft_etot
    except:
        # Every other DFT cycle: Build veff as sum of the regular veff and the SIC
        # potential.
        sic_veff = veff + flo_veff['hamil']

        # Update the density matrix.
        dm_new = dynamic_rdm(ks.flo, ks.calc_uks.mo_occ)
        dm = dm_new.copy()
        ks.mo_coeff = ks.flo

    # Give back the FLO-SIC energy correction and the corrected potential. This libtagarray
    # formalism is defined by pyscf.
    sic_back = sic_etot - dft_etot
    veff_sic = lib.tag_array(sic_veff,
                             ecoul=veff.ecoul,
                             exc=veff.exc,
                             vj=veff.vj,
                             vk=veff.vk,
                             esic=(sic_back))

    # Return the exchange-correlation energy and the FLO-SIC energy correction.
    ks.exc = veff.exc
    ks.esic = sic_back

    # increase another magic counter ;-)
    ks.neval_vsic += 1

    return veff_sic
Пример #5
0
    def calculate(self,
                  atoms,
                  properties=['energy'],
                  system_changes=['positions']):
        self.num_iter += 1
        atoms = self.get_atoms()
        self.atoms = atoms
        Calculator.calculate(self, atoms, properties, system_changes)
        if self.mode == 'dft':
            # DFT only mode
            from pyscf import gto, scf, grad, dft
            [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms)
            nuclei = ase2pyscf(nuclei)
            mol = gto.M(atom=nuclei,
                        basis=self.basis,
                        spin=self.spin,
                        charge=self.charge)
            mf = scf.UKS(mol)
            mf.xc = self.xc
            # Verbosity of the mol object (o lowest output, 4 might enough output for debugging)
            mf.verbose = self.verbose
            mf.max_cycle = self.max_cycle
            mf.conv_tol = self.conv_tol
            mf.grids.level = self.grid
            if self.n_rad is not None and self.n_ang is not None:
                mf.grids.atom_grid = (self.n_rad, self.n_ang)
            mf.grids.prune = prune_dict[self.prune]
            e = mf.kernel()
            self.mf = mf
            self.results['energy'] = e * Ha
            self.results['dipole'] = dipole = mf.dip_moment(verbose=0)
            self.results['evalues'] = mf.mo_energy
        if self.mode == 'flosic-os':
            # FLOSIC SCF mode
            from pyscf import gto, scf
            [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms)
            # FLOSIC one shot mode
            #mf = flosic(self.atoms,charge=self.charge,spin=self.spin,xc=self.xc,basis=self.basis,debug=False,verbose=self.verbose)
            # Effective core potentials need so special treatment.
            if self.ecp == None:
                if self.ghost == False:
                    mol = gto.M(atom=ase2pyscf(nuclei),
                                basis=self.basis,
                                spin=self.spin,
                                charge=self.charge)
                if self.ghost == True:
                    mol = gto.M(atom=ase2pyscf(nuclei),
                                basis=self.basis,
                                spin=self.spin,
                                charge=self.charge)
                    mol.basis = {
                        'default': self.basis,
                        'GHOST1': gto.basis.load('sto3g', 'H'),
                        'GHOST2': gto.basis.load('sto3g', 'H')
                    }
            if self.ecp != None:
                mol = gto.M(atom=ase2pyscf(nuclei),
                            basis=self.basis,
                            spin=self.spin,
                            charge=self.charge,
                            ecp=self.ecp)
            mf = scf.UKS(mol)
            mf.xc = self.xc
            # Verbosity of the mol object (o lowest output, 4 might enough output for debugging)
            mf.verbose = self.verbose
            # Binary output format of pyscf.
            # Save MOs, orbital energies, etc.
            if self.use_chk == True and self.use_newton == False:
                mf.chkfile = 'pyflosic.chk'
            # Load from previous run, if exist, the checkfile.
            # Hopefully this will speed up the calculation.
            if self.use_chk == True and self.use_newton == False and os.path.isfile(
                    'pyflosic.chk'):
                mf.init_guess = 'chk'
                mf.update('pyflosic.chk')
            if self.use_newton == True:
                mf = mf.as_scanner()
                mf = mf.newton()
            mf.max_cycle = self.max_cycle
            mf.conv_tol = self.conv_tol
            mf.grids.level = self.grid
            if self.n_rad is not None and self.n_ang is not None:
                mf.grids.atom_grid = (self.n_rad, self.n_ang)
            mf.grids.prune = prune_dict[self.prune]
            e = mf.kernel()
            self.mf = mf
            mf = flosic(mol,
                        mf,
                        fod1,
                        fod2,
                        sysname=None,
                        datatype=np.float64,
                        print_dm_one=False,
                        print_dm_all=False,
                        debug=self.debug,
                        calc_forces=True,
                        ham_sic=self.ham_sic)
            self.results['energy'] = mf['etot_sic'] * Ha
            # unit conversion from Ha/Bohr to eV/Ang
            #self.results['fodforces'] = -1*mf['fforces']/(Ha/Bohr)
            self.results['fodforces'] = -1 * mf['fforces'] * (Ha / Bohr)
            print('Analytical FOD force [Ha/Bohr]')
            print(mf['fforces'])
            print('fmax = %0.6f [Ha/Bohr]' % np.sqrt(
                (mf['fforces']**2).sum(axis=1).max()))
            self.results['dipole'] = mf['dipole']
            self.results['evalues'] = mf['evalues']
        if self.mode == 'flosic-scf':
            #if self.mf is None:
            # FLOSIC SCF mode
            from pyscf import gto
            [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms)
            # Effective core potentials need so special treatment.
            if self.ecp == None:
                if self.ghost == False:
                    mol = gto.M(atom=ase2pyscf(nuclei),
                                basis=self.basis,
                                spin=self.spin,
                                charge=self.charge)
                if self.ghost == True:
                    mol = gto.M(atom=ase2pyscf(nuclei),
                                basis=self.basis,
                                spin=self.spin,
                                charge=self.charge)
                    mol.basis = {
                        'default': self.basis,
                        'GHOST1': gto.basis.load('sto3g', 'H'),
                        'GHOST2': gto.basis.load('sto3g', 'H')
                    }
            if self.ecp != None:
                mol = gto.M(atom=ase2pyscf(nuclei),
                            basis=self.basis,
                            spin=self.spin,
                            charge=self.charge,
                            ecp=self.ecp)
            if self.efield != None:
                m0 = FLOSIC(mol=mol,
                            xc=self.xc,
                            fod1=fod1,
                            fod2=fod2,
                            grid_level=self.grid,
                            debug=self.debug,
                            l_ij=self.l_ij,
                            ods=self.ods,
                            fixed_vsic=self.fixed_vsic,
                            num_iter=self.num_iter,
                            vsic_every=self.vsic_every,
                            ham_sic=self.ham_sic)
                # test efield to enforce some pseudo chemical environment
                # and break symmetry of density
                m0.grids.level = self.grid
                m0.conv_tol = self.conv_tol
                # small efield
                m0.max_cycle = 1
                h = -0.0001  #-0.1
                apply_field(mol, m0, E=(0, 0, 0 + h))
                m0.kernel()
            mf = FLOSIC(mol=mol,
                        xc=self.xc,
                        fod1=fod1,
                        fod2=fod2,
                        grid_level=self.grid,
                        calc_forces=self.calc_forces,
                        debug=self.debug,
                        l_ij=self.l_ij,
                        ods=self.ods,
                        fixed_vsic=self.fixed_vsic,
                        num_iter=self.num_iter,
                        vsic_every=self.vsic_every,
                        ham_sic=self.ham_sic)
            # Verbosity of the mol object (o lowest output, 4 might enough output for debugging)
            mf.verbose = self.verbose
            # Binary output format of pyscf.
            # Save MOs, orbital energies, etc.
            if self.use_chk == True and self.use_newton == False:
                mf.chkfile = 'pyflosic.chk'
            # Load from previous run, if exist, the checkfile.
            # Hopefully this will speed up the calculation.
            if self.use_chk == True and self.use_newton == False and os.path.isfile(
                    'pyflosic.chk'):
                mf.init_guess = 'chk'
                mf.update('pyflosic.chk')
            if self.use_newton == True:
                mf = mf.as_scanner()
                mf = mf.newton()
            mf.max_cycle = self.max_cycle
            mf.conv_tol = self.conv_tol
            mf.grids.level = self.grid
            if self.n_rad is not None and self.n_ang is not None:
                mf.grids.atom_grid = (self.n_rad, self.n_ang)
                mf.calc_uks.grids.atom_grid = (self.n_rad, self.n_ang)
            mf.grids.prune = prune_dict[self.prune]
            mf.calc_uks.grids.prune = prune_dict[self.prune]
            e = mf.kernel()
            self.mf = mf
            # Return some results to the pyflosic_ase_caculator object.
            self.results['esic'] = mf.esic * Ha
            self.results['energy'] = e * Ha
            self.results['fixed_vsic'] = mf.fixed_vsic

            # if self.mf is not None:
            #     from pyscf import gto
            #     [geo,nuclei,fod1,fod2,included] =  xyz_to_nuclei_fod(self.atoms)
            #     # Effective core potentials need so special treatment.
            #     if self.ecp == None:
            #         if self.ghost == False:
            #             mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis,spin=self.spin,charge=self.charge)
            #         if self.ghost == True:
            #             mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis,spin=self.spin,charge=self.charge)
            #             mol.basis ={'default':self.basis,'GHOST1':gto.basis.load('sto3g', 'H'),'GHOST2':gto.basis.load('sto3g', 'H')}
            #     if self.ecp != None:
            #         mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis,spin=self.spin,charge=self.charge,ecp=self.ecp)
            #     self.mf.num_iter = self.num_iter
            #     self.mf.max_cycle = self.max_cycle
            #     self.mf.mol = mol
            #     self.mf.fod1 = fod1
            #     self.mf.fod2 = fod2
            #     e = self.mf.kernel()
            #     # Return some results to the pyflosic_ase_caculator object.
            #     self.results['esic'] = self.mf.esic*Ha
            #     self.results['energy'] = e*Ha
            #     self.results['fixed_vsic'] = self.mf.fixed_vsic
            #
            if self.fopt == 'force' or self.fopt == 'esic-force':
                #
                # The standard optimization uses
                # the analytical FOD forces
                #
                fforces = self.mf.get_fforces()
                #fforces = -1*fforce
                # unit conversion Hartree/Bohr to eV/Angstroem
                #self.results['fodforces'] = -1*fforces*(Ha/Bohr)
                self.results['fodforces'] = fforces * (Ha / Bohr)
                print('Analytical FOD force [Ha/Bohr]')
                print(fforces)
                print('fmax = %0.6f [Ha/Bohr]' % np.sqrt(
                    (fforces**2).sum(axis=1).max()))

            if self.fopt == 'lij':
                #
                # This is under development.
                # Trying to replace the FOD forces.
                #
                self.lambda_ij = self.mf.lambda_ij
                self.results['lambda_ij'] = self.mf.lambda_ij
                #fforces = []
                #nspin = 2
                #for s in range(nspin):
                #	# printing the lampda_ij matrix for both spin channels
                #	print 'lambda_ij'
                #	print lambda_ij[s,:,:]
                #	print 'RMS lambda_ij'
                #	M = lambda_ij[s,:,:]
                #	fforces_tmp =  (M-M.T)[np.triu_indices((M-M.T).shape[0])]
                #	fforces.append(fforces_tmp.tolist())
                #print np.array(fforces).shape
                try:
                    #
                    # Try to calculate the FOD forces from the differences
                    # of SIC eigenvalues
                    #
                    evalues_old = self.results['evalues']
                    print(evalues_old)
                    evalues_new = self.mf.evalues
                    print(evalues_new)
                    delta_evalues_up = (evalues_old[0][0:len(fod1)] -
                                        evalues_new[0][0:len(fod1)]).tolist()
                    delta_evalues_dn = (evalues_old[1][0:len(fod2)] -
                                        evalues_new[1][0:len(fod2)]).tolist()
                    print(delta_evalues_up)
                    print(delta_evalues_dn)
                    lij_force = delta_evalues_up
                    lij_force.append(delta_evalues_dn)
                    lij_force = np.array(lij_force)
                    lij_force = np.array(lij_force,
                                         (np.shape(lij_force)[0], 3))
                    print('FOD force evalued from evalues')
                    print(lij_force)
                    self.results['fodforces'] = lij_force
                except:
                    #
                    # If we are in the first iteration
                    # we can still use the analystical FOD forces
                    # as starting values
                    #
                    fforces = self.mf.get_fforces()
                    print(fforces)
                    #self.results['fodforces'] = -1*fforces*(Ha/Bohr)
                    self.results['fodforces'] = fforces * (Ha / Bohr)
                    print('Analytical FOD force [Ha/Bohr]')
                    print(fforces)
                    print('fmax = %0.6f [Ha/Bohr]' % np.sqrt(
                        (fforces**2).sum(axis=1).max()))

            self.results['dipole'] = self.mf.dip_moment()
            self.results['evalues'] = self.mf.evalues

        if atoms is not None:
            self.atoms = atoms.copy()