示例#1
0
    def init_geom(self, position_file, unitcell_file):
        """initialise the geometry."""
        if self.Def["PBC"]==1:
            PBCs = True
        else:
            PBCs = False
        # If build geometry is turned on then build the geometry
        if self.Def['build_geom'] == 1:
            a, crys_err = self.calculate_crystal_sep()
            # if the calculation of the crystal separation a is successful:
            if crys_err == 0:
                mycry = Crystal(a=a, lattice="cubic")
                mycry.populateUnitCell(self.Def['crystal'], geom_filename=position_file, uc_filename=unitcell_file, nx=self.Def['nx'], ny=self.Def['ny'], nz=self.Def['nz'], PBCs=PBCs)
        # Read in the geometry from file
        NAtom, Pos, AtomType = ReadGeom(position_file)
        # If PBCs are turned on then read in the unit cell
        if PBCs:
            a1, a2, a3 = ReadUnitCell(unitcell_file)
        else:
            a1, a2, a3 = np.array((0.0, 0.0, 0.0)), np.array((0.0, 0.0, 0.0)), np.array((0.0, 0.0, 0.0))

        # Write out the geometry
        WriteXYZ(self, NAtom, '', AtomType, Pos)

        # Transfer geometry to the JobClass
        self.NAtom    = NAtom
        self.Pos      = Pos
        self.UnitCell = [a1, a2, a3]

        self.AtomType = AtomType
        self.NOrb     = [self.Model.atomic[self.AtomType[a]]['NOrbitals'] for a in range(self.NAtom)]

        verboseprint(self.Def['verbose'], "Atom positions:")
        verboseprint(self.Def['verbose'], self.Pos)
示例#2
0
    def optimisation_routine2(self, num_rho):
        """
        Optimisation routine where we try to solve for the norm squared of the
        optimal density matrix with the constraint that the sum of the
        coefficients is equal to one. To include the constraint we set up the
        problem:

        minimise: alpha_i M_ij alpha_j - lambda (sum_i alpha_i - 1)

        where M_ij = Tr(R_i^dag R_j). We then differentiate with respect to
        alpha_k and set to zero to minimise:

        2 M alpha - lambda = 0

        We solve this equation. We have to add a buffer row and column to
        include lambda as well as the constraint that the sum of alpha is
        equal to one. We absorb the 2 into lambda:

        {M_11   M_12    ...    -1   {alpha_1     {0
         M_21   M_22    ...    -1    alpha_2      0
         .              .            .            .
         .                .          .        =   .
         .                  .        .            .
         -1     -1      ...     0}   lambda}      -1}

        """
        small = 1e-10
        verboseprint(self.Job.Def['extraverbose'], "optimisation_routine2")
        # If there is only one density matrix the solution is simple.
        if num_rho == 1:
            return np.array([1.0], dtype='double'), 0

        alpha = np.zeros(num_rho+1, dtype='double')
        Mmat = np.matrix(np.zeros((num_rho+1, num_rho+1), dtype='complex'))
        # make all the elements -1
        Mmat.fill(-1.0)
        # replace the bottom right hand corner by 0
        Mmat[-1,-1] = 0.0
        # calculate the rest of the Mmat.
        for i in range(num_rho):
            Mmat[i, i] = np.trace(np.matrix(self.residue[i])*np.matrix(self.residue[i]).H)
            for j in range(i+1, num_rho):
                Mmat[i, j] = np.trace(np.matrix(self.residue[i])*np.matrix(self.residue[j]).H)
                # if np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])) != Mmat[i, j].conj():
                #     print "Mmat[%i,%i] = %f. Mmat[%i,%i].conj() = %f." % (j, i, np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])), i, j, Mmat[i, j].conj())
                Mmat[j, i] = Mmat[i, j].conj()
        # if abs(np.linalg.det(Mmat)) < small:
        #     return alpha, 1

        RHS = np.zeros(num_rho+1, dtype = 'double')
        RHS[-1] = -1.0
        alpha = np.linalg.solve(Mmat, RHS)
        myscale = abs(np.sum(alpha)-alpha[-1])
        if abs(myscale-1.0) > small:
            print("ERROR: optimisation_routine2 -- sum alpha = %f. alpha must sum to 1.0." % myscale)
            print(alpha)
            return alpha, 1
        # if successful then return result and no error code.
        return alpha, 0
示例#3
0
    def GR_Pulay(self, scf_iteration):
        """
        This is the guaranteed reduction Pulay mixing scheme proposed in:
            Bowler, D. R., and M. J. Gillan. "An efficient and robust technique
            for achieving self consistency in electronic structure
            calculations." Chemical Physics Letters 325.4 (2000): 473-476.

        If the number of density matrices to be used, num_rho, is 1, it reduces
        to linear mixing.

        The scf_iteration is a required input because when scf_iteration is
        less than num_rho then scf_iteration is the number of density matrices
        that should be used.

        The output is an updated self.rhotot to be used in the construction of
        the Fock matrix. Also, self.inputrho, self.outputrho and self.residue
        are updated for the next iteration.
        """
        num_rho = self.Job.Def['num_rho']
        # If the number of scf iterations is less than num_rho replace it by
        # the number of scf iterations (as there will only be that number of
        # density matrices).
        if scf_iteration < num_rho:
            num_rho = scf_iteration

        # Shift along the density and residue matrices
        for ii in range(num_rho - 1):
            self.inputrho[num_rho - 1 - ii] = np.copy(self.inputrho[num_rho -
                                                                    2 - ii])
            self.outputrho[num_rho - 1 - ii] = np.copy(self.outputrho[num_rho -
                                                                      2 - ii])
            self.residue[num_rho - 1 - ii] = np.copy(self.residue[num_rho - 2 -
                                                                  ii])

        # Add in the new density and residue matrices
        self.inputrho[0] = self.rhotot
        self.outputrho[0] = self.rho
        self.residue[0] = self.rho - self.rhotot

        # Calculate the values of alpha to minimise the residue
        alpha, igo = self.optimisation_routine(num_rho)
        if igo == 1:
            print(
                "WARNING: Unable to optimise alpha for combining density matrices. Proceeding using guess."
            )
            # Guess for alpha is just 1.0 divided by the number of density matrices
            alpha = np.zeros((num_rho), dtype='double')
            alpha.fill(1.0 / num_rho)
        verboseprint(self.Job.Def['extraverbose'], "alpha: ", alpha)
        # Create an optimised rhotot and an optimised rho and do linear mixing to make next input matrix
        self.rhotot = sum(alpha[i] * self.inputrho[i] for i in range(num_rho))
        self.rho = sum(alpha[i] * self.outputrho[i] for i in range(num_rho))
        self.linear_mixing()
示例#4
0
    def optimisation_routine1(self, num_rho):
        """
        Optimisation routine where we try to solve for the norm squared of the
        optimal density matrix with the constraint that the sum of the
        coefficients is equal to one. To include the constraint we set up the
        problem:

        minimise: alpha_i M_ij alpha_j - lambda (sum_i alpha_i - 1)

        where M_ij = Tr(R_i^dag R_j). We then differentiate with respect to
        alpha_k and set to zero to minimise:

        2 M alpha = lambda

        We solve this equation for lambda = 1. We then can simply scale alpha,
        such that sum_i alpha_i = 1, which is equivalent to having solved for
        a different lambda.
        """
        verboseprint(self.Job.Def['extraverbose'], "optimisation_routine")
        small = 1e-14
        # If there is only one density matrix the solution is simple.
        if num_rho == 1:
            return np.array([1.0], dtype='double'), 0

        alpha = np.zeros(num_rho, dtype='double')
        Mmat = np.matrix(np.zeros((num_rho, num_rho), dtype='complex'))
        lamb = 0.5 * np.ones(num_rho, dtype='double')
        for i in range(num_rho):
            Mmat[i, i] = np.trace(
                np.matrix(self.residue[i]) * np.matrix(self.residue[i]).H)
            for j in range(i + 1, num_rho):
                Mmat[i, j] = np.trace(
                    np.matrix(self.residue[i]) * np.matrix(self.residue[j]).H)
                # if np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])) != Mmat[i, j].conj():
                #     print "Mmat[%i,%i] = %f. Mmat[%i,%i].conj() = %f." % (j, i, np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])), i, j, Mmat[i, j].conj())
                Mmat[j, i] = Mmat[i, j].conj()
        # if np.linalg.det(Mmat) < small:
        #     return alpha, 1

        alpha = np.linalg.solve(Mmat, lamb)
        myscale = np.sum(alpha)
        if myscale == 0:
            print(
                "ERROR: alpha summed to 0 in optimisation_routine. Cannot be scaled to 1."
            )
            print(alpha)
            return alpha, 1
        else:
            alpha = alpha / myscale
        return alpha, 0
示例#5
0
    def GR_Pulay(self, scf_iteration):
        """
        This is the guaranteed reduction Pulay mixing scheme proposed in:
            Bowler, D. R., and M. J. Gillan. "An efficient and robust technique
            for achieving self consistency in electronic structure
            calculations." Chemical Physics Letters 325.4 (2000): 473-476.

        If the number of density matrices to be used, num_rho, is 1, it reduces
        to linear mixing.

        The scf_iteration is a required input because when scf_iteration is
        less than num_rho then scf_iteration is the number of density matrices
        that should be used.

        The output is an updated self.rhotot to be used in the construction of
        the Fock matrix. Also, self.inputrho, self.outputrho and self.residue
        are updated for the next iteration.
        """
        num_rho = self.Job.Def['num_rho']
        # If the number of scf iterations is less than num_rho replace it by
        # the number of scf iterations (as there will only be that number of
        # density matrices).
        if scf_iteration < num_rho:
            num_rho = scf_iteration

        # Shift along the density and residue matrices
        for ii in range(num_rho-1):
            self.inputrho[num_rho - 1 - ii] = np.copy(self.inputrho[num_rho - 2 - ii])
            self.outputrho[num_rho - 1 - ii] = np.copy(self.outputrho[num_rho - 2 - ii])
            self.residue[num_rho - 1 - ii] = np.copy(self.residue[num_rho - 2 - ii])

        # Add in the new density and residue matrices 
        self.inputrho[0] = self.rhotot
        self.outputrho[0] = self.rho
        self.residue[0] = self.rho - self.rhotot

        # Calculate the values of alpha to minimise the residue
        alpha, igo = self.optimisation_routine(num_rho)
        if igo == 1:
            print("WARNING: Unable to optimise alpha for combining density matrices. Proceeding using guess.")
            # Guess for alpha is just 1.0 divided by the number of density matrices
            alpha = np.zeros((num_rho), dtype='double')
            alpha.fill(1.0/num_rho)
        verboseprint(self.Job.Def['extraverbose'], "alpha: ", alpha)
        # Create an optimised rhotot and an optimised rho and do linear mixing to make next input matrix
        self.rhotot = sum(alpha[i]*self.inputrho[i] for i in range(num_rho))
        self.rho = sum(alpha[i]*self.outputrho[i] for i in range(num_rho))
        self.linear_mixing()
示例#6
0
    def optimisation_routine1(self, num_rho):
        """
        Optimisation routine where we try to solve for the norm squared of the
        optimal density matrix with the constraint that the sum of the
        coefficients is equal to one. To include the constraint we set up the
        problem:

        minimise: alpha_i M_ij alpha_j - lambda (sum_i alpha_i - 1)

        where M_ij = Tr(R_i^dag R_j). We then differentiate with respect to
        alpha_k and set to zero to minimise:

        2 M alpha = lambda

        We solve this equation for lambda = 1. We then can simply scale alpha,
        such that sum_i alpha_i = 1, which is equivalent to having solved for
        a different lambda.
        """
        verboseprint(self.Job.Def['extraverbose'], "optimisation_routine")
        small = 1e-14
        # If there is only one density matrix the solution is simple.
        if num_rho == 1:
            return np.array([1.0], dtype='double'), 0

        alpha = np.zeros(num_rho, dtype='double')
        Mmat = np.matrix(np.zeros((num_rho, num_rho), dtype='complex'))
        lamb = 0.5*np.ones(num_rho, dtype='double')
        for i in range(num_rho):
            Mmat[i, i] = np.trace(np.matrix(self.residue[i])*np.matrix(self.residue[i]).H)
            for j in range(i+1, num_rho):
                Mmat[i, j] = np.trace(np.matrix(self.residue[i])*np.matrix(self.residue[j]).H)
                # if np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])) != Mmat[i, j].conj():
                #     print "Mmat[%i,%i] = %f. Mmat[%i,%i].conj() = %f." % (j, i, np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])), i, j, Mmat[i, j].conj())
                Mmat[j, i] = Mmat[i, j].conj()
        # if np.linalg.det(Mmat) < small:
        #     return alpha, 1

        alpha = np.linalg.solve(Mmat, lamb)
        myscale = np.sum(alpha)
        if myscale == 0:
            print("ERROR: alpha summed to 0 in optimisation_routine. Cannot be scaled to 1.")
            print(alpha)
            return alpha, 1
        else:
            alpha = alpha/myscale
        return alpha, 0
示例#7
0
文件: main.py 项目: mec07/PyLATO
def print_results(Job):
    verboseprint(Job.Def['verbose'], "Energy eigenvalues: ")
    verboseprint(Job.Def['verbose'], Job.e)
    if Job.Def['Hamiltonian'] == "collinear":
        verboseprint(Job.Def['extraverbose'], "Mulliken charges: ", Job.Hamilton.q)
        verboseprint(Job.Def['extraverbose'], (Job.Hamilton.s).T)

    # Write out information about the simulation if it is specified in the job definition
    print("\n\n\nSuccessfully completed calculation!")
    WriteSimulationResults(Job)
示例#8
0
文件: main.py 项目: gharib85/PyLATO
def print_results(Job):
    verboseprint(Job.Def['verbose'], "Energy eigenvalues: ")
    verboseprint(Job.Def['verbose'], Job.e)
    if Job.Def['Hamiltonian'] == "collinear":
        verboseprint(Job.Def['extraverbose'], "Mulliken charges: ",
                     Job.Hamilton.q)
        verboseprint(Job.Def['extraverbose'], (Job.Hamilton.s).T)

    # Write out information about the simulation if it is specified in the job definition
    print("\n\n\nSuccessfully completed calculation!")
    WriteSimulationResults(Job)
示例#9
0
    def McWeeny_iterations(self, rho):
        """
        Iterations of the McWeeny scheme for the inputted rho.
        Return a True/False flag that indicates convergence, the number of
        iterations required to reach convergence, the error and the converged density
        matrix.
        """
        converge_flag = False
        for ii in range(self.Job.Def['McWeeny_max_loops']):
            # McWeeny transformation
            rho = 3*np.dot(rho, np.dot(rho, rho)) - 2*np.dot(rho, rho)
            err = self.idempotency_error(rho)
            verboseprint(self.Job.Def['extraverbose'], "McWeeny iteration: ", ii, "; Idempotency error = ", err)
            if err < self.Job.Def['McWeeny_tol']:
                converge_flag = True
                return converge_flag, ii, err, rho
            # Check to make sure that the error hasn't become a nan.
            elif np.isnan(err):
                return converge_flag, ii, err, rho

        # if it gets to this statement then it probably hasn't converged.
        return converge_flag, ii, err, rho
示例#10
0
    def McWeeny_iterations(self, rho):
        """
        Iterations of the McWeeny scheme for the inputted rho.
        Return a True/False flag that indicates convergence, the number of
        iterations required to reach convergence, the error and the converged density
        matrix.
        """
        converge_flag = False
        for ii in range(self.Job.Def['McWeeny_max_loops']):
            # McWeeny transformation
            rho = 3 * np.dot(rho, np.dot(rho, rho)) - 2 * np.dot(rho, rho)
            err = self.idempotency_error(rho)
            verboseprint(self.Job.Def['extraverbose'], "McWeeny iteration: ",
                         ii, "; Idempotency error = ", err)
            if err < self.Job.Def['McWeeny_tol']:
                converge_flag = True
                return converge_flag, ii, err, rho
            # Check to make sure that the error hasn't become a nan.
            elif np.isnan(err):
                return converge_flag, ii, err, rho

        # if it gets to this statement then it probably hasn't converged.
        return converge_flag, ii, err, rho
示例#11
0
    def McWeeny(self):
        """
        Make the density matrix idempotent using the McWeeny transformation,
        R.McWeeny, Rev. Mod. Phys. (1960):

        rho_n+1 = 3*rho_n^3 - 2*rho_n^2

        """
        if self.Job.isNoncollinearHami:
            rho_temp = self.rhotot
        else:
            rho_temp = self.rho

        # Make sure that it isn't already idempotent
        err_orig = self.idempotency_error(rho_temp)
        if err_orig < self.Job.Def['McWeeny_tol']:
            # if already idempotent then don't do anything, just exit function
            return

        flag, iterations, err, rho_temp = self.McWeeny_iterations(rho_temp)
        # Check that the number of electrons hasn't changed
        num_electrons = sum(rho_temp[i, i] for i in range(len(rho_temp)))
        if abs(num_electrons - self.NElectrons) > self.Job.Def['McWeeny_tol']:
            verboseprint(
                self.Job.Def['verbose'],
                "McWeeny transformation changed the number of electrons, "
                "difference = ", num_electrons - self.NElectrons)
            # Turn off using the McWeeny transformation as once it doesn't work it seems to not work again.
            self.Job.Def["McWeeny"] = 0
            return

        # if the flag is false it means that idempotency was reduced below the tolerance
        if not flag:
            # if the iterations did not converge but the idempotency error has
            # gotten smaller then print a warning but treat as a success.
            if err < err_orig:
                verboseprint(
                    self.Job.Def['verbose'],
                    "Max iterations, {} reached. Idempotency error = {}".
                    format(iterations, err))
                flag = True
            else:
                verboseprint(
                    self.Job.Def['verbose'],
                    "McWeeny transformation unsuccessful. Proceeding using input density matrix."
                )
                # Turn off using the McWeeny transformation as once it doesn't work it seems to not work again.
                self.Job.Def["McWeeny"] = 0

        # if this is going to be treated like a success then reassign rho_temp.
        if flag:
            if self.Job.isNoncollinearHami:
                self.rhotot = rho_temp
            else:
                self.rho = rho_temp
示例#12
0
    def McWeeny(self):
        """
        Make the density matrix idempotent using the McWeeny transformation,
        R.McWeeny, Rev. Mod. Phys. (1960):

        rho_n+1 = 3*rho_n^3 - 2*rho_n^2

        """
        if self.Job.isNoncollinearHami:
            rho_temp = self.rhotot
        else:
            rho_temp = self.rho

        # Make sure that it isn't already idempotent
        err_orig = self.idempotency_error(rho_temp)
        if err_orig < self.Job.Def['McWeeny_tol']:
            # if already idempotent then don't do anything, just exit function
            return

        flag, iterations, err, rho_temp = self.McWeeny_iterations(rho_temp)
        # Check that the number of electrons hasn't changed
        num_electrons = sum(rho_temp[i, i] for i in range(len(rho_temp)))
        if abs(num_electrons - self.NElectrons) > self.Job.Def['McWeeny_tol']:
            verboseprint(
                self.Job.Def['verbose'],
                "McWeeny transformation changed the number of electrons, "
                "difference = ", num_electrons - self.NElectrons)
            # Turn off using the McWeeny transformation as once it doesn't work it seems to not work again.
            self.Job.Def["McWeeny"] = 0
            return

        # if the flag is false it means that idempotency was reduced below the tolerance
        if not flag:
            # if the iterations did not converge but the idempotency error has
            # gotten smaller then print a warning but treat as a success.
            if err < err_orig:
                verboseprint(self.Job.Def['verbose'], "Max iterations, {} reached. Idempotency error = {}".format(iterations, err))
                flag = True
            else:
                verboseprint(self.Job.Def['verbose'], "McWeeny transformation unsuccessful. Proceeding using input density matrix.")
                # Turn off using the McWeeny transformation as once it doesn't work it seems to not work again.
                self.Job.Def["McWeeny"] = 0

        # if this is going to be treated like a success then reassign rho_temp.
        if flag:
            if self.Job.isNoncollinearHami:
                self.rhotot = rho_temp
            else:
                self.rho = rho_temp
示例#13
0
    def optimisation_routine2(self, num_rho):
        """
        Optimisation routine where we try to solve for the norm squared of the
        optimal density matrix with the constraint that the sum of the
        coefficients is equal to one. To include the constraint we set up the
        problem:

        minimise: alpha_i M_ij alpha_j - lambda (sum_i alpha_i - 1)

        where M_ij = Tr(R_i^dag R_j). We then differentiate with respect to
        alpha_k and set to zero to minimise:

        2 M alpha - lambda = 0

        We solve this equation. We have to add a buffer row and column to
        include lambda as well as the constraint that the sum of alpha is
        equal to one. We absorb the 2 into lambda:

        {M_11   M_12    ...    -1   {alpha_1     {0
         M_21   M_22    ...    -1    alpha_2      0
         .              .            .            .
         .                .          .        =   .
         .                  .        .            .
         -1     -1      ...     0}   lambda}      -1}

        """
        small = 1e-10
        verboseprint(self.Job.Def['extraverbose'], "optimisation_routine2")
        # If there is only one density matrix the solution is simple.
        if num_rho == 1:
            return np.array([1.0], dtype='double'), 0

        alpha = np.zeros(num_rho + 1, dtype='double')
        Mmat = np.matrix(np.zeros((num_rho + 1, num_rho + 1), dtype='complex'))
        # make all the elements -1
        Mmat.fill(-1.0)
        # replace the bottom right hand corner by 0
        Mmat[-1, -1] = 0.0
        # calculate the rest of the Mmat.
        for i in range(num_rho):
            Mmat[i, i] = np.trace(
                np.matrix(self.residue[i]) * np.matrix(self.residue[i]).H)
            for j in range(i + 1, num_rho):
                Mmat[i, j] = np.trace(
                    np.matrix(self.residue[i]) * np.matrix(self.residue[j]).H)
                # if np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])) != Mmat[i, j].conj():
                #     print "Mmat[%i,%i] = %f. Mmat[%i,%i].conj() = %f." % (j, i, np.sum(np.matrix(self.residue[j]).H*np.matrix(self.residue[i])), i, j, Mmat[i, j].conj())
                Mmat[j, i] = Mmat[i, j].conj()
        # if abs(np.linalg.det(Mmat)) < small:
        #     return alpha, 1

        RHS = np.zeros(num_rho + 1, dtype='double')
        RHS[-1] = -1.0
        alpha = np.linalg.solve(Mmat, RHS)
        myscale = abs(np.sum(alpha) - alpha[-1])
        if abs(myscale - 1.0) > small:
            print(
                "ERROR: optimisation_routine2 -- sum alpha = %f. alpha must sum to 1.0."
                % myscale)
            print(alpha)
            return alpha, 1
        # if successful then return result and no error code.
        return alpha, 0
示例#14
0
def PerformSelfConsistency(Job):
    # Make the Fock matrix self-consistent
    SCFerror = 1.0e+99
    # flag to indicate if self-consistency has been obtained.
    SCFflag = False

    # if it's a noncollinear Hamiltonian we need to initialise rhotot
    if Job.isNoncollinearHami:
        Job.Electron.rhotot = Job.Electron.rho

    max_loops = int(Job.Def['scf_max_loops'])
    assert max_loops > 0, ("Performing self consistency calculation requires positive "
                           f"value of scf_max_loops. Current value is: {max_loops}")
    for ii in range(max_loops):
        #
        # Build the fock matrix (adds the density matrix dependent terms)
        Job.Hamilton.buildFock(Job)
        #
        # Diagonalise the Fock matrix
        Job.e, Job.psi = np.linalg.eigh(Job.Hamilton.fock)
        #
        # Occupy the orbitals according to the Fermi distribution
        Job.Electron.occupy(Job.e, Job.Def['el_kT'], Job.Def['mu_tol'], Job.Def['mu_max_loops'])
        #
        # Build the density matrix
        Job.Electron.densitymatrix()

        if Job.isNoncollinearHami:
            # Compare the difference between the new and the old on-site density matrix elements
            SCFerror = Job.Electron.SCFerror()
            verboseprint(Job.Def['verbose'], 'SCF loop = ', ii+1, '; SCF error = ', SCFerror)
            # Check if the SCF error is still larger than the tolerance
            if SCFerror > Job.Def['scf_tol']:
                # Update the density matrix by linear mixing
                # Job.Electron.linear_mixing()
                Job.Electron.GR_Pulay(ii+1)
            else:
                SCFflag = True
                break

            if Job.Def['McWeeny'] == 1:
                Job.Electron.McWeeny()

            verboseprint(Job.Def['extraverbose'], "number of electrons = "+str(Job.Electron.electronspersite().sum()))
            verboseprint(Job.Def['extraverbose'], "output rho idempotency error is: ", Job.Electron.idempotency_error(Job.Electron.rho))
            verboseprint(Job.Def['extraverbose'], "input rho idempotency error is: ", Job.Electron.idempotency_error(Job.Electron.rhotot))
            verboseprint(Job.Def['extraverbose'], "SCF charges = ", Job.Hamilton.q)
            verboseprint(Job.Def['extraverbose'], "Magnetic moments = ", Job.Electron.spinpersite().T)

        else:
            #
            # Compute the net charge on each site
            q = Job.Electron.chargepersite()
            #
            # Compute the net spin on each site
            s = Job.Electron.spinpersite()
            #
            # Compute the error in the charges, and update the charges and spin
            SCFerror = math.sqrt(np.vdot(q-Job.Hamilton.q, q-Job.Hamilton.q) / Job.NAtom)
            verboseprint(Job.Def['verbose'], 'SCF loop = ', ii+1, '; SCF error = ', SCFerror)
            verboseprint(Job.Def['extraverbose'], 'SCF charges = ', Job.Hamilton.q)
            #
            # IF SCF is on check if the SCF error is still larger than the tolerance.
            if SCFerror > Job.Def['scf_tol']:
                #
                # Update the input charges and spins
                Job.Hamilton.q = Job.Hamilton.q + Job.Def['scf_mix'] * (q-Job.Hamilton.q)
                Job.Hamilton.s = Job.Hamilton.s + Job.Def['scf_mix'] * (s-Job.Hamilton.s)
            #
            # If SCF error is smaller than or equal to the tolerance then leave loop
            else:
                SCFflag = True
                break

            # if the McWeeny flag is on then purify the density matrix
            if Job.Def['McWeeny'] == 1:
                Job.Electron.McWeeny()

    # Print out number of SCF loops taken
    verboseprint(Job.Def['verbose'], "Number of SCF loops: ", ii+1)
    # if self-consistency is not obtained then raise an error and exit.
    if SCFflag is False:
        raise SelfConsistencyError(
            ("ERROR: Self-consistency not obtained within maximum number of "
             "cycles: {}".format(max_loops))
        )

    return SCFflag
示例#15
0
def PerformSelfConsistency(Job):
    # Make the Fock matrix self-consistent
    SCFerror = 1.0e+99
    # flag to indicate if self-consistency has been obtained.
    SCFflag = False

    # if it's a noncollinear Hamiltonian we need to initialise rhotot
    if Job.isNoncollinearHami:
        Job.Electron.rhotot = Job.Electron.rho

    max_loops = int(Job.Def['scf_max_loops'])
    assert max_loops > 0, (
        "Performing self consistency calculation requires positive "
        f"value of scf_max_loops. Current value is: {max_loops}")
    for ii in range(max_loops):
        #
        # Build the fock matrix (adds the density matrix dependent terms)
        Job.Hamilton.buildFock(Job)
        #
        # Diagonalise the Fock matrix
        Job.e, Job.psi = np.linalg.eigh(Job.Hamilton.fock)
        #
        # Occupy the orbitals according to the Fermi distribution
        Job.Electron.occupy(Job.e, Job.Def['el_kT'], Job.Def['mu_tol'],
                            Job.Def['mu_max_loops'])
        #
        # Build the density matrix
        Job.Electron.densitymatrix()

        if Job.isNoncollinearHami:
            # Compare the difference between the new and the old on-site density matrix elements
            SCFerror = Job.Electron.SCFerror()
            verboseprint(Job.Def['verbose'], 'SCF loop = ', ii + 1,
                         '; SCF error = ', SCFerror)
            # Check if the SCF error is still larger than the tolerance
            if SCFerror > Job.Def['scf_tol']:
                # Update the density matrix by linear mixing
                # Job.Electron.linear_mixing()
                Job.Electron.GR_Pulay(ii + 1)
            else:
                SCFflag = True
                break

            if Job.Def['McWeeny'] == 1:
                Job.Electron.McWeeny()

            verboseprint(
                Job.Def['extraverbose'], "number of electrons = " +
                str(Job.Electron.electronspersite().sum()))
            verboseprint(Job.Def['extraverbose'],
                         "output rho idempotency error is: ",
                         Job.Electron.idempotency_error(Job.Electron.rho))
            verboseprint(Job.Def['extraverbose'],
                         "input rho idempotency error is: ",
                         Job.Electron.idempotency_error(Job.Electron.rhotot))
            verboseprint(Job.Def['extraverbose'], "SCF charges = ",
                         Job.Hamilton.q)
            verboseprint(Job.Def['extraverbose'], "Magnetic moments = ",
                         Job.Electron.spinpersite().T)

        else:
            #
            # Compute the net charge on each site
            q = Job.Electron.chargepersite()
            #
            # Compute the net spin on each site
            s = Job.Electron.spinpersite()
            #
            # Compute the error in the charges, and update the charges and spin
            SCFerror = math.sqrt(
                np.vdot(q - Job.Hamilton.q, q - Job.Hamilton.q) / Job.NAtom)
            verboseprint(Job.Def['verbose'], 'SCF loop = ', ii + 1,
                         '; SCF error = ', SCFerror)
            verboseprint(Job.Def['extraverbose'], 'SCF charges = ',
                         Job.Hamilton.q)
            #
            # IF SCF is on check if the SCF error is still larger than the tolerance.
            if SCFerror > Job.Def['scf_tol']:
                #
                # Update the input charges and spins
                Job.Hamilton.q = Job.Hamilton.q + Job.Def['scf_mix'] * (
                    q - Job.Hamilton.q)
                Job.Hamilton.s = Job.Hamilton.s + Job.Def['scf_mix'] * (
                    s - Job.Hamilton.s)
            #
            # If SCF error is smaller than or equal to the tolerance then leave loop
            else:
                SCFflag = True
                break

            # if the McWeeny flag is on then purify the density matrix
            if Job.Def['McWeeny'] == 1:
                Job.Electron.McWeeny()

    # Print out number of SCF loops taken
    verboseprint(Job.Def['verbose'], "Number of SCF loops: ", ii + 1)
    # if self-consistency is not obtained then raise an error and exit.
    if SCFflag is False:
        raise SelfConsistencyError(
            ("ERROR: Self-consistency not obtained within maximum number of "
             "cycles: {}".format(max_loops)))

    return SCFflag