Example #1
0
class ExchangeNCL(Exchange):
    """
    Non-collinear exchange
    """
    def set_tbmodels(self, tbmodels):
        """
        tbmodels should be in spinor form.
        The basis should be orb1_up, orb2_up,...orbn_up, orb1_dn, orb2_dn....
        """
        self.tbmodel = tbmodels
        # TODO: check if tbmodels are really a tbmodel with SOC.
        self.G = TBGreen(self.tbmodel, self.kmesh, self.efermi)
        self.norb = self.G.norb
        self.nbasis = self.G.nbasis
        self.rho = np.zeros((self.nbasis, self.nbasis), dtype=float)
        self.A_ijR = defaultdict(lambda: np.zeros((4, 4), dtype=complex))
        #self.HR0 = self.tbmodel.ham_R0
        self.HR0 = self.G.H0
        if not self.G.is_orthogonal:
            self.S0 = self.G.S0
        self._is_collinear = False
        self.Pdict = {}

    def _prepare_NijR(self):
        self.N = {}
        for R in self.Rlist:
            self.N[R] = np.zeros((self.nbasis, self.nbasis), dtype=complex)

    def _prepare_Patom(self):
        for iatom in self.ind_mag_atoms:
            self.Pdict[iatom] = pauli_block_sigma_norm(self.get_H_atom(iatom))

    def get_H_atom(self, iatom):
        orbs = self.iorb(iatom)
        return self.HR0[np.ix_(orbs, orbs)]

    def get_P_iatom(self, iatom):
        """ Calculate the norm of the Hamiltonian vector.
        For each atom, the local hamiltonian of each orbital H(2*2 matrix)
        can be written as H0* I + H1* sigma1 + H2*sigma2 + H3 *sigma3
        where sigma is a Pauli matrix. return the norm of (H1, H2, H3) vector
        :param iatom: index of atom
        :returns: a matrix of norms P. P[i, j] is for orbital i and j.
        :rtype: complex matrix of shape norb_i * norb_i.
        """
        if self.Pdict == {}:
            self._prepare_Patom()
        return self.Pdict[iatom]

    def GR_atom(self, GR, iatom, jatom):
        """Given a green's function matrix, return the [iatom, jatom] component.

        :param GR:  Green's function matrix
        :param iatom:  index of atom i
        :param jatom:  index of atom j
        :returns:  G_ij
        :rtype:  complex matrix.

        """
        orbi = self.iorb(iatom)
        orbj = self.iorb(jatom)
        return GR[np.ix_(orbi, orbj)]

    def get_A_ijR(self, G, iatom, jatom, de):
        """ calculate A from G for a energy slice (de).
        It take the
        .. math::
           A^{uv} = p T^u p T^v dE / pi

        where u, v are I, x, y, z (index 0, 1,2,3). p(i) = self.get_P_iatom(iatom)
        T^u(ijR)  (u=0,1,2,3) = pauli_block_all(G)

        :param G: Green's function for all R, i, j.
        :param iatom: i
        :param jatom: j
        :param de:  energy step. used for integeration
        :returns: a matrix of A_ij(u, v), where u, v =(0)0, x(1), y(2), z(3)
        :rtype:  4*4 matrix
        """
        for R in self.R_ijatom_dict:
            # G[i, j, R]
            GR = G[R]
            if (iatom, jatom) in self.R_ijatom_dict[R]:
                Gij = self.GR_atom(GR, iatom, jatom)
                # GijR , I, x, y, z component.
                Gij_Ixyz = pauli_block_all(Gij)

                # G(j, i, -R)
                Rm = tuple(-x for x in R)
                GRm = G[Rm]
                Gji = self.GR_atom(GRm, jatom, iatom)
                Gji_Ixyz = pauli_block_all(Gji)
                tmp = np.zeros((4, 4), dtype=complex)
                for a in range(4):
                    for b in range(4):
                        AijRab = np.matmul(
                            np.matmul(self.get_P_iatom(iatom), Gij_Ixyz[a]),
                            np.matmul(self.get_P_iatom(jatom), Gji_Ixyz[b]))
                        # trace over orb
                        tmp[a, b] = np.trace(AijRab)
                # Note: the full complex, rather than Re or Im part is stored into A_ijR.
                self.A_ijR[(R, iatom, jatom)] += tmp * de / np.pi

    def get_all_A(self, G, de):
        """
        Calculate all A matrix elements
        Loop over all magnetic atoms.
        :param G: Green's function.
        :param de: energy step.
        """
        for iatom in self.ind_mag_atoms:
            for jatom in self.ind_mag_atoms:
                self.get_A_ijR(G, iatom, jatom, de)

    def A_to_Jtensor(self):
        """
        Calculate J tensors from A.
        If we assume the exchange can be written as a bilinear tensor form,
        J_{isotropic} = Tr Im (A^{00} - A^{xx} - A^{yy} - A^{zz})
        J_{anisotropic}_uv = Tr Im (2A)
        DMI =  Tr Re (A^{0z} - A^{z0} )
        """
        self.Jani = {}
        self.DMI = {}

        self.Jprime = {}
        self.B = {}
        self.exchange_Jdict = {}
        for key, val in self.A_ijR.items():
            # key:(R, iatom, jatom)
            R, iatom, jatom = key

            Rm = tuple(-x for x in R)
            valm = self.A_ijR[(Rm, jatom, iatom)]

            ispin = self.ispin(iatom)
            jspin = self.ispin(jatom)
            keyspin = (R, ispin, jspin)

            is_nonself = not (R == (0, 0, 0) and iatom == jatom)
            Jiso = np.zeros((3, 3), dtype=float)
            Ja = np.zeros((3, 3), dtype=float)
            Dtmp = np.zeros(3, dtype=float)
            # Heisenberg like J.
            for i in range(3):
                Jiso[i, i] += np.imag(val[0, 0] - val[1, 1] - val[2, 2] -
                                      val[3, 3])
                #Jiso[i, i] += np.imag(val[0, 0] - val[3, 3])

            if is_nonself:
                self.exchange_Jdict[keyspin] = Jiso[0, 0]

            # off-diagonal ansiotropic exchange
            for i in range(3):
                for j in range(3):
                    if i != j:
                        #Ja[i,j] = np.imag(val[i + 1, j + 1] + valm[i + 1, j + 1])
                        Ja[i, j] = -np.imag(val[i + 1, j + 1] +
                                            valm[i + 1, j + 1]) / 2.0
                        #Ja[i,j] =  -np.imag(val[i+1, j+1])
            if is_nonself:
                self.Jani[keyspin] = Ja

            # DMI
            for i in range(3):
                Dtmp[i] = np.real(val[0, i + 1] - val[i + 1, 0])
                # Dx = Jyz-Jzy
                # Dy = Jzx-Jxz
                # Dz = Jxy-Jyx
                #Dtmp[0] = np.imag(val[2, 3] - val[3, 2])
                #Dtmp[1] = np.imag(val[3, 1] - val[1, 3])
                #Dtmp[2] = np.imag(val[1, 2] - val[2, 1])
            if is_nonself:
                self.DMI[keyspin] = Dtmp

            # isotropic exchange into bilinear and biqudratic parts:
            # Jprime SiSj and B (SiSj)^2
            if is_nonself:
                Si = self.spinat[iatom]
                Sj = self.spinat[jatom]
                Jprime = np.imag(val[0, 0] - val[3, 3]) - 2 * np.sign(
                    np.dot(Si, Sj)) * np.imag(val[3, 3])
                #Jprime = np.imag(val[0, 0] - 3*val[3, 3])
                B = np.imag(val[3, 3])
                self.B[keyspin] = Jprime, B

    def get_N_e(self, GR, de):
        """
        calcualte density matrix for all R,i, j
        """
        self.N=defaultdict(lambda: 0.0)
        for R, G in GR.items():
            self.N[R] += -1.0 / np.pi * np.imag(G * de)

    def get_rho_e(self, rhoR, de):
        """ add component to density matrix from a green's function
        :param GR: Green's funciton in real space.
        :param de: energy step
        """
        #GR0 = GR[(0, 0, 0)]
        #if self.G.is_orthogonal:
        #    self.rho += -1.0 / np.pi * np.imag(GR0 * de)
        #else:
        #    self.rho += -1.0 / np.pi * np.imag(self.S0@GR0 * de)
        self.rho += -1.0 / np.pi * np.imag(rhoR[0, 0, 0] * de)

    def get_total_charges(self):
        return np.sum(np.real(np.diag(self.rho)))

    def get_rho_atom(self):
        """
        calculate charge and spin for each atom.
        """
        rho = {}
        self.charges = np.zeros(len(self.atoms), dtype=float)
        self.spinat = np.zeros((len(self.atoms), 3), dtype=float)
        for iatom in self.orb_dict:
            iorb = self.iorb(iatom)
            tmp = self.rho[np.ix_(iorb, iorb)]
            # *2 because there is a 1/2 in the paui_block_all function
            rho[iatom] = np.array(
                [np.trace(x) * 2 for x in pauli_block_all(tmp)])
            self.charges[iatom] = np.real(rho[iatom][0])
            self.spinat[iatom, :] = np.real(rho[iatom][1:])
        self.rho_dict = rho
        return self.rho_dict

    def calculate_DMI_NJT(self):
        """
        calculate exchange and DMI with the
        D(i,j) =
        """
        Ddict_NJT = {}
        Jdict_NJT = {}
        for R in self.short_Rlist:
            N = self.N[tuple(-np.array(R))]  # density matrix
            t = self.tbmodel.get_hamR(R)  # hopping parameter
            for iatom in self.ind_mag_atoms:
                orbi = self.iorb(iatom)
                ni = len(orbi)
                for jatom in self.ind_mag_atoms:
                    orbj = self.iorb(jatom)
                    nj = len(orbj)
                    Nji = N[np.ix_(orbj, orbi)]
                    tij = t[np.ix_(orbi, orbj)]
                    D = np.zeros(3, dtype=float)
                    J = np.zeros(3, dtype=float)
                    for dim in range(3):
                        #S_i = pauli_mat(ni, dim +
                        #                1)  #*self.rho[np.ix_(orbi, orbi)]
                        #S_j = pauli_mat(nj, dim +
                        #                1)  #*self.rho[np.ix_(orbj, orbj)]
                        S_i = pauli_mat(ni, dim +
                                        1)  *self.rho[np.ix_(orbi, orbi)]
                        S_j = pauli_mat(nj, dim +
                                        1)  *self.rho[np.ix_(orbj, orbj)]

                        # [S, t]+  = Si tij + tij Sj, where
                        # Si and Sj are the spin operator
                        # Here we do not have L operator, so J-> S
                        Jt = np.matmul(S_i, tij) + np.matmul(tij, S_j)

                        Jtminus = np.matmul(S_i, tij) - np.matmul(tij, S_j)
                        # D = -1/2 Tr Nji [J, tij]
                        # Trace over spin and orb
                        D[dim] = -0.5 * np.imag(np.trace(np.matmul(Nji, Jt)))
                        J[dim] = -0.5 * np.imag(
                            np.trace(np.matmul(Nji, Jtminus)))
                    ispin = self.ispin(iatom)
                    jspin = self.ispin(jatom)
                    Ddict_NJT[(R, ispin, jspin)] = D
                    Jdict_NJT[(R, ispin, jspin)] = J
        self.Jdict_NJT = Jdict_NJT
        self.Ddict_NJT = Ddict_NJT
        return Ddict_NJT

    def calculate_all(self):
        """
        The top level.
        """
        print("Green's function Calculation started.")

        widgets = [
            ' [',
            progressbar.Timer(),
            '] ',
            progressbar.Bar(),
            ' (',
            progressbar.ETA(),
            ') ',
        ]
        bar = progressbar.ProgressBar(maxval=self.contour.npoints,
                                      widgets=widgets)
        bar.start()
        for ie in range(self.contour.npoints):
            bar.update(ie)
            #if self.ne is not none and self.get_total_charges(
            #) > self.ne and ie not in self.elistc:
            #    self._prepare_elistc(self, ie)
            #    continue
            e = self.contour.path[ie]
            de = self.contour.de[ie]
            GR, rhoR = self.G.get_GR(self.short_Rlist, energy=e, get_rho=True)
            self.get_rho_e(rhoR, de)
            self.get_all_A(GR, de)
            if self.calc_NJt:
                self.get_N_e(GR, de)

        self.get_rho_atom()
        self.A_to_Jtensor()
        #self.calculate_DMI_NJT()
        bar.finish()

    def _prepare_index_spin(self):
        # index_spin: index in spin hamiltonian of atom. starts from 1. -1 means not considered.
        ind_matoms = []
        self.index_spin = []
        ispin = 0
        for i, sym in enumerate(self.atoms.get_chemical_symbols()):
            if sym in self.magnetic_elements:
                ind_matoms.append(i)
                self.index_spin.append(ispin)
                ispin += 1
            else:
                self.index_spin.append(-1)

    def write_output(self, path='TB2J_results'):
        self._prepare_index_spin()
        output = SpinIO(
            atoms=self.atoms,
            charges=self.charges,
            spinat=self.spinat,
            index_spin=self.index_spin,
            colinear=False,
            distance_dict=self.distance_dict,
            exchange_Jdict=self.exchange_Jdict,
            dmi_ddict=self.DMI,
            NJT_Jdict=self.Jdict_NJT,
            NJT_ddict=self.Ddict_NJT,
            Jani_dict=self.Jani,
            biquadratic_Jdict=self.B,
        )
        output.write_all(path=path)

    def run(self, path='TB2J_results'):
        self.calculate_all()
        self.write_output(path=path)
Example #2
0
class ExchangeNCL(Exchange):
    """
    Non-collinear exchange
    """
    def set_tbmodels(self, tbmodels):
        """
        tbmodels should be in spinor form.
        The basis should be orb1_up, orb2_up,...orbn_up, orb1_dn, orb2_dn....
        """
        self.tbmodel = tbmodels
        # TODO: check if tbmodels are really a tbmodel with SOC.
        self.G = TBGreen(self.tbmodel,
                         self.kmesh,
                         self.efermi,
                         use_cache=self._use_cache,
                         nproc=self.np)
        self.norb = self.G.norb
        self.nbasis = self.G.nbasis
        self.rho = np.zeros((self.nbasis, self.nbasis), dtype=complex)
        self.A_ijR_list = defaultdict(lambda: [])
        self.A_ijR = defaultdict(lambda: np.zeros((4, 4), dtype=complex))
        self.HR0 = self.G.H0
        self._is_collinear = False
        self.Pdict = {}

    def _prepare_NijR(self):
        self.N = {}
        for R in self.Rlist:
            self.N[R] = np.zeros((self.nbasis, self.nbasis), dtype=complex)

    def _prepare_Patom(self):
        for iatom in self.ind_mag_atoms:
            self.Pdict[iatom] = pauli_block_sigma_norm(self.get_H_atom(iatom))

    def get_H_atom(self, iatom):
        orbs = self.iorb(iatom)
        return self.HR0[self.orb_slice[iatom], self.orb_slice[iatom]]

    def get_P_iatom(self, iatom):
        """ Calculate the norm of the Hamiltonian vector.
        For each atom, the local hamiltonian of each orbital H(2*2 matrix)
        can be written as H0* I + H1* sigma1 + H2*sigma2 + H3 *sigma3
        where sigma is a Pauli matrix. return the norm of (H1, H2, H3) vector
        :param iatom: index of atom
        :returns: a matrix of norms P. P[i, j] is for orbital i and j.
        :rtype: complex matrix of shape norb_i * norb_i.
        """
        if self.Pdict == {}:
            self._prepare_Patom()
        return self.Pdict[iatom]

    def GR_atom(self, GR, iatom, jatom):
        """Given a green's function matrix, return the [iatom, jatom] component.

        :param GR:  Green's function matrix
        :param iatom:  index of atom i
        :param jatom:  index of atom j
        :returns:  G_ij
        :rtype:  complex matrix.

        """
        orbi = self.iorb(iatom)
        orbj = self.iorb(jatom)
        #return GR[np.ix_(orbi, orbj)]
        return GR[self.orb_slice[iatom], self.orb_slice[jatom]]

    def get_A_ijR(self, G, R, iatom, jatom):
        """ calculate A from G for a energy slice (de).
        It take the
        .. math::
           A^{uv} = p T^u p T^v dE / pi

        where u, v are I, x, y, z (index 0, 1,2,3). p(i) = self.get_P_iatom(iatom)
        T^u(ijR)  (u=0,1,2,3) = pauli_block_all(G)

        :param G: Green's function for all R, i, j.
        :param iatom: i
        :param jatom: j
        :param de:  energy step. used for integeration
        :returns: a matrix of A_ij(u, v), where u, v =(0)0, x(1), y(2), z(3)
        :rtype:  4*4 matrix
        """
        GR = G[R]
        Gij = self.GR_atom(GR, iatom, jatom)
        Gij_Ixyz = pauli_block_all(Gij)

        # G(j, i, -R)
        Rm = tuple(-x for x in R)
        GRm = G[Rm]
        Gji = self.GR_atom(GRm, jatom, iatom)
        Gji_Ixyz = pauli_block_all(Gji)

        tmp = np.zeros((4, 4), dtype=complex)
        for a in range(4):
            pGp = self.get_P_iatom(iatom) @ Gij_Ixyz[a] @ self.get_P_iatom(
                jatom)
            for b in range(4):
                AijRab = np.matmul(pGp, Gji_Ixyz[b])
                tmp[a, b] = np.trace(AijRab)
        return tmp / np.pi

    def get_all_A(self, G):
        """
        Calculate all A matrix elements
        Loop over all magnetic atoms.
        :param G: Green's function.
        :param de: energy step.
        """
        A_ijR_list = {}
        for iR, R in enumerate(self.R_ijatom_dict):
            for (iatom, jatom) in self.R_ijatom_dict[R]:
                A = self.get_A_ijR(G, R, iatom, jatom)
                A_ijR_list[(R, iatom, jatom)] = A
        return A_ijR_list

    def A_to_Jtensor(self):
        """
        Calculate J tensors from A.
        If we assume the exchange can be written as a bilinear tensor form,
        J_{isotropic} = Tr Im (A^{00} - A^{xx} - A^{yy} - A^{zz})
        J_{anisotropic}_uv = Tr Im (2A)
        DMI =  Tr Re (A^{0z} - A^{z0} )
        """
        self.Jani = {}
        self.DMI = {}

        self.Jprime = {}
        self.B = {}
        self.exchange_Jdict = {}
        self.debug_dict = {'DMI2': {}}
        for key, val in self.A_ijR.items():
            # key:(R, iatom, jatom)
            R, iatom, jatom = key

            Rm = tuple(-x for x in R)
            valm = self.A_ijR[(Rm, jatom, iatom)]

            ispin = self.ispin(iatom)
            jspin = self.ispin(jatom)
            keyspin = (R, ispin, jspin)

            is_nonself = not (R == (0, 0, 0) and iatom == jatom)
            Jiso = np.zeros((3, 3), dtype=float)
            Ja = np.zeros((3, 3), dtype=float)
            Dtmp = np.zeros(3, dtype=float)
            Dtmp2 = np.zeros(3, dtype=float)
            # Heisenberg like J.
            for i in range(3):
                Jiso[i, i] += np.imag(val[0, 0] - val[1, 1] - val[2, 2] -
                                      val[3, 3])

            if is_nonself:
                self.exchange_Jdict[keyspin] = Jiso[0, 0]

            # off-diagonal anisotropic exchange
            for i in range(3):
                for j in range(3):
                    Ja[i, j] = np.imag(val[i + 1, j + 1] + valm[i + 1, j + 1])
            if is_nonself:
                self.Jani[keyspin] = Ja

            # DMI
            for i in range(3):
                Dtmp[i] = np.real(val[0, i + 1] - val[i + 1, 0])

            # Dx = Jyz-Jzy
            # Dy = Jzx-Jxz
            # Dz = Jxy-Jyx
            Dtmp2[0] = np.imag(val[2, 3] - val[3, 2])
            Dtmp2[1] = np.imag(val[3, 1] - val[1, 3])
            Dtmp2[2] = np.imag(val[1, 2] - val[2, 1])
            if is_nonself:
                self.DMI[keyspin] = Dtmp
                self.debug_dict['DMI2'][keyspin] = Dtmp2

            # isotropic exchange into bilinear and biqudratic parts:
            # Jprime SiSj and B (SiSj)^2
            if is_nonself:
                Si = self.spinat[iatom]
                Sj = self.spinat[jatom]
                Jprime = np.imag(val[0, 0] - val[3, 3]) - 2 * np.sign(
                    np.dot(Si, Sj)) * np.imag(val[3, 3])
                #Jprime = np.imag(val[0, 0] - 3*val[3, 3])
                B = np.imag(val[3, 3])
                self.B[keyspin] = Jprime, B

    def get_N_e(self, GR, de):
        """
        calcualte density matrix for all R,i, j
        """
        self.N = defaultdict(lambda: 0.0)
        for R, G in GR.items():
            self.N[R] += -1.0 / np.pi * np.imag(G * de)

    def get_rho_e(self, rhoR):
        """ add component to density matrix from a green's function
        :param GR: Green's funciton in real space.
        """
        return -1.0 / np.pi * rhoR[0, 0, 0]

    def get_total_charges(self):
        return np.sum(np.imag(np.diag(self.rho)))

    def get_rho_atom(self):
        """
        calculate charge and spin for each atom.
        """
        rho = {}
        self.charges = np.zeros(len(self.atoms), dtype=float)
        self.spinat = np.zeros((len(self.atoms), 3), dtype=float)
        for iatom in self.orb_dict:
            iorb = self.iorb(iatom)
            tmp = self.rho[np.ix_(iorb, iorb)]
            # *2 because there is a 1/2 in the paui_block_all function
            rho[iatom] = np.array(
                [np.trace(x) * 2 for x in pauli_block_all(tmp)])
            self.charges[iatom] = np.imag(rho[iatom][0])
            self.spinat[iatom, :] = np.imag(rho[iatom][1:])
        self.rho_dict = rho
        return self.rho_dict

    def calculate_DMI_NJT(self):
        """
        calculate exchange and DMI with the
        D(i,j) =
        """
        Ddict_NJT = {}
        Jdict_NJT = {}
        for R in self.short_Rlist:
            N = self.N[tuple(-np.array(R))]  # density matrix
            t = self.tbmodel.get_hamR(R)  # hopping parameter
            for iatom in self.ind_mag_atoms:
                orbi = self.iorb(iatom)
                ni = len(orbi)
                for jatom in self.ind_mag_atoms:
                    orbj = self.iorb(jatom)
                    nj = len(orbj)
                    Nji = N[np.ix_(orbj, orbi)]
                    tij = t[np.ix_(orbi, orbj)]
                    D = np.zeros(3, dtype=float)
                    J = np.zeros(3, dtype=float)
                    for dim in range(3):
                        #S_i = pauli_mat(ni, dim +
                        #                1)  #*self.rho[np.ix_(orbi, orbi)]
                        #S_j = pauli_mat(nj, dim +
                        #                1)  #*self.rho[np.ix_(orbj, orbj)]
                        # TODO: Note that rho is complex, not the imaginary part
                        S_i = pauli_mat(ni, dim + 1) * self.rho[np.ix_(
                            orbi, orbi)]
                        S_j = pauli_mat(nj, dim + 1) * self.rho[np.ix_(
                            orbj, orbj)]

                        # [S, t]+  = Si tij + tij Sj, where
                        # Si and Sj are the spin operator
                        # Here we do not have L operator, so J-> S
                        Jt = np.matmul(S_i, tij) + np.matmul(tij, S_j)

                        Jtminus = np.matmul(S_i, tij) - np.matmul(tij, S_j)
                        # D = -1/2 Tr Nji [J, tij]
                        # Trace over spin and orb
                        D[dim] = -0.5 * np.imag(np.trace(np.matmul(Nji, Jt)))
                        J[dim] = -0.5 * np.imag(
                            np.trace(np.matmul(Nji, Jtminus)))
                    ispin = self.ispin(iatom)
                    jspin = self.ispin(jatom)
                    Ddict_NJT[(R, ispin, jspin)] = D
                    Jdict_NJT[(R, ispin, jspin)] = J
        self.Jdict_NJT = Jdict_NJT
        self.Ddict_NJT = Ddict_NJT
        return Ddict_NJT

    def integrate(self, rhoRs, AijRs, method='simpson'):
        """
        AijRs: a list of AijR, 
        wherer AijR: array of ((nR, n, n, 4,4), dtype=complex)
        """
        if method == "trapezoidal":
            integrate = trapezoidal_nonuniform
        elif method == 'simpson':
            integrate = simpson_nonuniform

        self.rho = integrate(self.contour.path, rhoRs)
        for iR, R in enumerate(self.R_ijatom_dict):
            for (iatom, jatom) in self.R_ijatom_dict[R]:
                f = AijRs[(R, iatom, jatom)]
                self.A_ijR[(R, iatom, jatom)] = integrate(self.contour.path, f)

    def get_AijR_rhoR(self, e):
        GR, rhoR = self.G.get_GR(self.short_Rlist, energy=e, get_rho=True)
        AijR = self.get_all_A(GR)
        return AijR, self.get_rho_e(rhoR)

    def save_AijR(self, AijRs, fname):
        result = dict(path=self.contour.path, AijRs=AijRs)
        with open(fname, 'wb') as myfile:
            pickle.dump(result, myfile)

    def calculate_all(self):
        """
        The top level.
        """
        print("Green's function Calculation started.")

        widgets = [
            ' [',
            progressbar.Timer(),
            '] ',
            progressbar.Bar(),
            ' (',
            progressbar.ETA(),
            ') ',
        ]

        bar = progressbar.ProgressBar(maxval=self.contour.npoints,
                                      widgets=widgets)
        bar.start()
        rhoRs = []
        GRs = []
        AijRs = {}
        if self.np > 1:
            executor = ProcessPool(nodes=self.np)
            results = executor.map(self.get_AijR_rhoR, self.contour.path)
        else:
            results = map(self.get_AijR_rhoR, self.contour.path)

        for i, result in enumerate(results):
            bar.update(i)
            for iR, R in enumerate(self.R_ijatom_dict):
                for (iatom, jatom) in self.R_ijatom_dict[R]:
                    if (R, iatom, jatom) in AijRs:
                        AijRs[(R, iatom, jatom)].append(result[0][R, iatom,
                                                                  jatom])
                    else:
                        AijRs[(R, iatom, jatom)] = []
                        AijRs[(R, iatom, jatom)].append(result[0][R, iatom,
                                                                  jatom])
            rhoRs.append(result[1])
        if self.np > 1:
            executor.close()
            executor.join()
            executor.clear()

        #self.save_AijRs(AijRs)
        self.integrate(rhoRs, AijRs)

        self.get_rho_atom()
        self.A_to_Jtensor()
        bar.finish()

    def _prepare_index_spin(self):
        # index_spin: index in spin hamiltonian of atom. starts from 1. -1 means not considered.
        ind_matoms = []
        self.index_spin = []
        ispin = 0
        for i, sym in enumerate(self.atoms.get_chemical_symbols()):
            if sym in self.magnetic_elements:
                ind_matoms.append(i)
                self.index_spin.append(ispin)
                ispin += 1
            else:
                self.index_spin.append(-1)

    def write_output(self, path='TB2J_results'):
        self._prepare_index_spin()
        output = SpinIO(
            atoms=self.atoms,
            charges=self.charges,
            spinat=self.spinat,
            index_spin=self.index_spin,
            colinear=False,
            distance_dict=self.distance_dict,
            exchange_Jdict=self.exchange_Jdict,
            dmi_ddict=self.DMI,
            NJT_Jdict=self.Jdict_NJT,
            NJT_ddict=self.Ddict_NJT,
            Jani_dict=self.Jani,
            biquadratic_Jdict=self.B,
            debug_dict=self.debug_dict,
            description=self.description,
        )
        output.write_all(path=path)

    def finalize(self):
        self.G.clean_cache()

    def run(self, path='TB2J_results'):
        self.calculate_all()
        self.write_output(path=path)
        self.finalize()
Example #3
0
class ExchangeCL2(ExchangeCL):
    def set_tbmodels(self, tbmodels):
        """
        only difference is a colinear tag.
        """
        self.tbmodel_up, self.tbmodel_dn = tbmodels
        self.Gup = TBGreen(self.tbmodel_up,
                           self.kmesh,
                           self.efermi,
                           use_cache=self._use_cache,
                           cache_path='TB2J_results/cache/spinup')
        self.Gdn = TBGreen(self.tbmodel_dn,
                           self.kmesh,
                           self.efermi,
                           use_cache=self._use_cache,
                           cache_path='TB2J_results/cache/spindn')
        self.norb = self.Gup.norb
        self.nbasis = self.Gup.nbasis + self.Gdn.nbasis
        self.rho_up = np.zeros((self.norb, self.norb), dtype=float)
        self.rho_dn = np.zeros((self.norb, self.norb), dtype=float)
        self.JJ = defaultdict(lambda: 0.0j)
        self.Jorb = defaultdict(lambda: 0.0j)
        self.HR0_up = self.Gup.H0
        self.HR0_dn = self.Gdn.H0
        self.Delta = self.HR0_up - self.HR0_dn
        if self.Gup.is_orthogonal and self.Gdn.is_orthogonal:
            self.is_orthogonal = True
        else:
            self.is_orthogonal = False
            #self.S0=self.Gup.S0
        self._is_colinear = True

        self.exchange_Jdict = {}
        self.exchange_Jdict_orb = {}

        self.biquadratic = False

    def get_Delta(self, iatom):
        orbs = self.iorb(iatom)
        return self.Delta[np.ix_(orbs, orbs)]

    def GR_atom(self, GR, iatom, jatom):
        """Given a green's function matrix, return the [iatom, jatom] component.

        :param GR:  Green's function matrix
        :param iatom:  index of atom i
        :param jatom:  index of atom j
        :returns:  G_ij
        :rtype:  complex matrix.
        """
        orbi = self.iorb(iatom)
        orbj = self.iorb(jatom)
        return GR[np.ix_(orbi, orbj)]

    def get_A_ijR(self, Gup, Gdn, iatom, jatom, de):
        Rij_done = set()
        for R, ijpairs in self.R_ijatom_dict.items():
            if (iatom, jatom) in ijpairs and (R, iatom, jatom) not in Rij_done:
                Gij_up = self.GR_atom(Gup[R], iatom, jatom)
                Rm = tuple(-x for x in R)
                Gji_dn = self.GR_atom(Gdn[Rm], jatom, iatom)
                tmp = 0.0j
                #t = self.get_Delta(iatom) @ Gij_up @ self.get_Delta(jatom) @ Gji_dn
                t = np.einsum('ij, ji-> ij',
                              np.matmul(self.get_Delta(iatom), Gij_up),
                              np.matmul(self.get_Delta(jatom), Gji_dn))
                if self.biquadratic:
                    A = np.einsum('ij, ji-> ij',
                                  np.matmul(self.get_Delta(iatom), Gij_up),
                                  np.matmul(self.get_Delta(jatom), Gji_up))
                    C = np.einsum('ij, ji-> ij',
                                  np.matmul(self.get_Delta(iatom), Gij_down),
                                  np.matmul(self.get_Delta(jatom), Gji_down))
                tmp = np.sum(t)
                self.Jorb[(R, iatom, jatom)] += t * de / (4.0 * np.pi)
                self.JJ[(R, iatom, jatom)] += tmp * de / (4.0 * np.pi)
                Rij_done.add((R, iatom, jatom))
                if (Rm, jatom, iatom) not in Rij_done:
                    self.Jorb[(Rm, jatom, iatom)] += t * de / (4.0 * np.pi)
                    self.JJ[(Rm, jatom, iatom)] += tmp * de / (4.0 * np.pi)
                    Rij_done.add((Rm, jatom, iatom))

    def get_all_A(self, Gup, Gdn, de):
        """
        Calculate all A matrix elements
        Loop over all magnetic atoms.
        :param G: Green's function.
        :param de: energy step.
        """
        Rij_done = set()
        for R, ijpairs in self.R_ijatom_dict.items():
            for iatom, jatom in ijpairs:
                if (R, iatom, jatom) not in Rij_done:
                    Rm = tuple(-x for x in R)
                    if (Rm, jatom, iatom) in Rij_done:
                        raise KeyError(
                            f"Strange (Rm, jatom, iatom) has already been calculated! {(Rm, jatom, iatom)}"
                        )
                    Gij_up = self.GR_atom(Gup[R], iatom, jatom)
                    Gji_dn = self.GR_atom(Gdn[Rm], jatom, iatom)
                    tmp = 0.0j
                    #t = self.get_Delta(iatom) @ Gij_up @ self.get_Delta(jatom) @ Gji_dn
                    t = np.einsum('ij, ji-> ij',
                                  np.matmul(self.get_Delta(iatom), Gij_up),
                                  np.matmul(self.get_Delta(jatom), Gji_dn))
                    tmp = np.sum(t)
                    self.Jorb[(R, iatom, jatom)] += t * de / (4.0 * np.pi)
                    self.JJ[(R, iatom, jatom)] += tmp * de / (4.0 * np.pi)
                    Rij_done.add((R, iatom, jatom))
                    if (Rm, jatom, iatom) not in Rij_done:
                        self.Jorb[(Rm, jatom, iatom)] += t * de / (4.0 * np.pi)
                        self.JJ[(Rm, jatom, iatom)] += tmp * de / (4.0 * np.pi)
                        Rij_done.add((Rm, jatom, iatom))

    def A_to_Jtensor(self):
        for key, val in self.JJ.items():
            # key:(R, iatom, jatom)
            R, iatom, jatom = key
            ispin = self.ispin(iatom)
            jspin = self.ispin(jatom)
            keyspin = (R, ispin, jspin)
            is_nonself = not (R == (0, 0, 0) and iatom == jatom)
            Jij = np.imag(val) / np.sign(
                np.dot(self.spinat[iatom], self.spinat[jatom]))
            Jorbij = np.imag(self.Jorb[key]) / np.sign(
                np.dot(self.spinat[iatom], self.spinat[jatom]))
            if is_nonself:
                self.exchange_Jdict[keyspin] = Jij
                self.exchange_Jdict_orb[keyspin] = Jorbij

    def get_rho_e(self, rho_up, rho_dn, de):
        #GR0_up = GR_up[(0, 0, 0)]
        #GR0_dn = GR_dn[(0, 0, 0)]
        #if self.is_orthogonal:
        #    self.rho_up += -1.0 / np.pi * np.imag(GR0_up * de)
        #    self.rho_dn += -1.0 / np.pi * np.imag(GR0_dn * de)
        #else:
        #    self.rho_up += -1.0 / np.pi * np.imag(self.S0@GR0_up * de)
        #    self.rho_dn += -1.0 / np.pi * np.imag(self.S0@GR0_dn * de)
        self.rho_up += -1.0 / np.pi * np.imag(rho_up[(0, 0, 0)] * de)
        self.rho_dn += -1.0 / np.pi * np.imag(rho_dn[(0, 0, 0)] * de)

    def get_rho_atom(self):
        """
        charges and spins from density matrices
        """
        self.charges = np.zeros(len(self.atoms), dtype=float)
        self.spinat = np.zeros((len(self.atoms), 3), dtype=float)
        for iatom in self.orb_dict:
            iorb = self.iorb(iatom)
            tup = np.real(np.trace(self.rho_up[np.ix_(iorb, iorb)]))
            tdn = np.real(np.trace(self.rho_dn[np.ix_(iorb, iorb)]))
            # *2 because there is a 1/2 in the paui_block_all function
            self.charges[iatom] = tup + tdn
            self.spinat[iatom, 2] = tup - tdn

    def finalize(self):
        self.Gup.clean_cache()
        self.Gdn.clean_cache()
        path = 'TB2J_results/cache'
        if os.path.exists(path):
            shutil.rmtree(path)

    def calculate_all(self):
        """
        The top level.
        """
        print("Green's function Calculation started.")

        widgets = [
            ' [',
            progressbar.Timer(),
            '] ',
            progressbar.Bar(),
            ' (',
            progressbar.ETA(),
            ') ',
        ]
        bar = progressbar.ProgressBar(maxval=self.contour.npoints,
                                      widgets=widgets)
        bar.start()
        for ie in range(self.contour.npoints):
            bar.update(ie)
            e = self.contour.path[ie]
            de = self.contour.de[ie]
            GR_up, rho_up = self.Gup.get_GR(self.short_Rlist,
                                            energy=e,
                                            get_rho=True)
            GR_dn, rho_dn = self.Gdn.get_GR(self.short_Rlist,
                                            energy=e,
                                            get_rho=True)
            self.get_rho_e(rho_up, rho_dn, de)
            self.get_all_A(GR_up, GR_dn, de)
        self.get_rho_atom()
        self.A_to_Jtensor()
        bar.finish()

    def write_output(self, path='TB2J_results'):
        self._prepare_index_spin()
        output = SpinIO(
            atoms=self.atoms,
            charges=self.charges,
            spinat=self.spinat,
            index_spin=self.index_spin,
            colinear=True,
            distance_dict=self.distance_dict,
            exchange_Jdict=self.exchange_Jdict,
            exchange_Jdict_orb=self.exchange_Jdict_orb,
            dmi_ddict=None,
            NJT_Jdict=None,
            NJT_ddict=None,
            Jani_dict=None,
            biquadratic_Jdict=None,
            description=self.description,
        )
        output.write_all()
Example #4
0
class ExchangeCL2(ExchangeCL):
    def set_tbmodels(self, tbmodels):
        """
        only difference is a colinear tag.
        """
        self.tbmodel_up, self.tbmodel_dn = tbmodels
        self.Gup = TBGreen(self.tbmodel_up,
                           self.kmesh,
                           self.efermi,
                           use_cache=self._use_cache,
                           cache_path='TB2J_results/cache/spinup',
                           nproc=self.np)
        self.Gdn = TBGreen(self.tbmodel_dn,
                           self.kmesh,
                           self.efermi,
                           use_cache=self._use_cache,
                           cache_path='TB2J_results/cache/spindn',
                           nproc=self.np)
        self.norb = self.Gup.norb
        self.nbasis = self.Gup.nbasis + self.Gdn.nbasis
        self.rho_up_list = []
        self.rho_dn_list = []
        self.rho_up = np.zeros((self.norb, self.norb), dtype=float)
        self.rho_dn = np.zeros((self.norb, self.norb), dtype=float)
        self.Jorb_list = defaultdict(lambda: [])
        self.JJ_list = defaultdict(lambda: [])
        self.JJ = defaultdict(lambda: 0.0j)
        self.Jorb = defaultdict(lambda: 0.0j)
        self.HR0_up = self.Gup.H0
        self.HR0_dn = self.Gdn.H0
        self.Delta = self.HR0_up - self.HR0_dn
        if self.Gup.is_orthogonal and self.Gdn.is_orthogonal:
            self.is_orthogonal = True
        else:
            self.is_orthogonal = False
            #self.S0=self.Gup.S0
        self._is_colinear = True

        self.exchange_Jdict = {}
        self.exchange_Jdict_orb = {}

        self.biquadratic = False

    def _clean_tbmodels(self):
        del self.tbmodel_up
        del self.tbmodel_dn
        del self.Gup.tbmodel
        del self.Gdn.tbmodel

    def _adjust_emin(self):
        emin_up = self.Gup.find_energy_ingap(rbound=self.efermi -
                                             5.0) - self.efermi
        emin_dn = self.Gdn.find_energy_ingap(rbound=self.efermi -
                                             5.0) - self.efermi
        self.emin = min(emin_up, emin_dn)
        print(f"A gap is found at {self.emin}, set emin to it.")

    def get_Delta(self, iatom):
        #orbs = self.iorb(iatom)
        #return self.Delta[np.ix_(orbs, orbs)]
        s = self.orb_slice[iatom]
        return self.Delta[s, s]

    def GR_atom(self, GR, iatom, jatom):
        """Given a green's function matrix, return the [iatom, jatom] component.

        :param GR:  Green's function matrix
        :param iatom:  index of atom i
        :param jatom:  index of atom j
        :returns:  G_ij
        :rtype:  complex matrix.
        """
        #orbi = self.iorb(iatom)
        #orbj = self.iorb(jatom)
        #return GR[np.ix_(orbi, orbj)]
        return GR[self.orb_slice[iatom], self.orb_slice[jatom]]

    def get_A_ijR(self, Gup, Gdn, iatom, jatom):
        Rij_done = set()
        Jorb_list = dict()
        JJ_list = dict()
        for R, ijpairs in self.R_ijatom_dict.items():
            if (iatom, jatom) in ijpairs and (R, iatom, jatom) not in Rij_done:
                Gij_up = self.GR_atom(Gup[R], iatom, jatom)
                Rm = tuple(-x for x in R)
                Gji_dn = self.GR_atom(Gdn[Rm], jatom, iatom)
                tmp = 0.0j
                Deltai = self.get_Delta(iatom)
                Deltaj = self.get_Delta(jatom)
                t = np.einsum('ij, ji-> ij', np.matmul(Deltai, Gij_up),
                              np.matmul(Deltaj, Gji_dn))

                if self.biquadratic:
                    A = np.einsum('ij, ji-> ij', np.matmul(Deltai, Gij_up),
                                  np.matmul(Deltaj, Gji_up))
                    C = np.einsum('ij, ji-> ij', np.matmul(Deltai, Gij_down),
                                  np.matmul(Deltaj, Gji_down))
                tmp = np.sum(t)
                self.Jorb_list[(R, iatom, jatom)].append(t / (4.0 * np.pi))
                self.JJ_list[(R, iatom, jatom)].append(tmp / (4.0 * np.pi))
                Rij_done.add((R, iatom, jatom))
                if (Rm, jatom, iatom) not in Rij_done:
                    Jorb_list[(Rm, jatom, iatom)] = t / (4.0 * np.pi)
                    JJ_list[(Rm, jatom, iatom)] = tmp / (4.0 * np.pi)
                    Rij_done.add((Rm, jatom, iatom))
        return Jorb_list, JJ_list

    def get_all_A(self, Gup, Gdn):
        """
        Calculate all A matrix elements
        Loop over all magnetic atoms.
        :param G: Green's function.
        :param de: energy step.
        """
        Rij_done = set()
        Jorb_list = dict()
        JJ_list = dict()
        for R, ijpairs in self.R_ijatom_dict.items():
            for iatom, jatom in ijpairs:
                if (R, iatom, jatom) not in Rij_done:
                    Rm = tuple(-x for x in R)
                    if (Rm, jatom, iatom) in Rij_done:
                        raise KeyError(
                            f"Strange (Rm, jatom, iatom) has already been calculated! {(Rm, jatom, iatom)}"
                        )
                    Gij_up = self.GR_atom(Gup[R], iatom, jatom)
                    Gji_dn = self.GR_atom(Gdn[Rm], jatom, iatom)
                    tmp = 0.0j
                    #t = self.get_Delta(iatom) @ Gij_up @ self.get_Delta(jatom) @ Gji_dn
                    t = np.einsum('ij, ji-> ij',
                                  np.matmul(self.get_Delta(iatom), Gij_up),
                                  np.matmul(self.get_Delta(jatom), Gji_dn))
                    tmp = np.sum(t)
                    Jorb_list[(R, iatom, jatom)] = t / (4.0 * np.pi)
                    JJ_list[(R, iatom, jatom)] = tmp / (4.0 * np.pi)
                    Rij_done.add((R, iatom, jatom))
                    if (Rm, jatom, iatom) not in Rij_done:
                        Jorb_list[(Rm, jatom, iatom)] = t / (4.0 * np.pi)
                        JJ_list[(Rm, jatom, iatom)] = tmp / (4.0 * np.pi)
                        Rij_done.add((Rm, jatom, iatom))
        return Jorb_list, JJ_list

    def A_to_Jtensor(self):
        for key, val in self.JJ.items():
            # key:(R, iatom, jatom)
            R, iatom, jatom = key
            ispin = self.ispin(iatom)
            jspin = self.ispin(jatom)
            keyspin = (R, ispin, jspin)
            is_nonself = not (R == (0, 0, 0) and iatom == jatom)
            Jij = np.imag(val) / np.sign(
                np.dot(self.spinat[iatom], self.spinat[jatom]))
            Jorbij = np.imag(self.Jorb[key]) / np.sign(
                np.dot(self.spinat[iatom], self.spinat[jatom]))
            if is_nonself:
                self.exchange_Jdict[keyspin] = Jij
                self.exchange_Jdict_orb[keyspin] = Jorbij

    def get_rho_e(self, rho_up, rho_dn):
        #self.rho_up_list.append(-1.0 / np.pi * np.imag(rho_up[(0,0,0)]))
        #self.rho_dn_list.append(-1.0 / np.pi * np.imag(rho_dn[(0,0,0)]))
        rup = -1.0 / np.pi * rho_up[(0, 0, 0)]
        rdn = -1.0 / np.pi * rho_dn[(0, 0, 0)]
        return rup, rdn

    def get_rho_atom(self):
        """
        charges and spins from density matrices
        """
        self.charges = np.zeros(len(self.atoms), dtype=float)
        self.spinat = np.zeros((len(self.atoms), 3), dtype=float)
        for iatom in self.orb_dict:
            iorb = self.iorb(iatom)
            tup = np.real(np.trace(self.rho_up[np.ix_(iorb, iorb)]))
            tdn = np.real(np.trace(self.rho_dn[np.ix_(iorb, iorb)]))
            self.charges[iatom] = tup + tdn
            self.spinat[iatom, 2] = tup - tdn

    def finalize(self):
        self.Gup.clean_cache()
        self.Gdn.clean_cache()
        path = 'TB2J_results/cache'
        if os.path.exists(path):
            shutil.rmtree(path)

    def integrate(self, method="simpson"):
        if method == "trapezoidal":
            integrate = trapezoidal_nonuniform
        elif method == 'simpson':
            integrate = simpson_nonuniform
        self.rho_up = np.imag(integrate(self.contour.path, self.rho_up_list))
        self.rho_dn = np.imag(integrate(self.contour.path, self.rho_dn_list))
        for R, ijpairs in self.R_ijatom_dict.items():
            for iatom, jatom in ijpairs:
                self.Jorb[(R, iatom, jatom)] = integrate(
                    self.contour.path, self.Jorb_list[(R, iatom, jatom)])
                self.JJ[(R, iatom,
                         jatom)] = integrate(self.contour.path,
                                             self.JJ_list[(R, iatom, jatom)])

    def get_AijR_rhoR(self, e):
        GR_up, rho_up = self.Gup.get_GR(self.short_Rlist,
                                        energy=e,
                                        get_rho=True)
        GR_dn, rho_dn = self.Gdn.get_GR(self.short_Rlist,
                                        energy=e,
                                        get_rho=True)
        rup, rdn = self.get_rho_e(rho_up, rho_dn)
        Jorb_list, JJ_list = self.get_all_A(GR_up, GR_dn)
        return rup, rdn, Jorb_list, JJ_list

    def calculate_all(self):
        """
        The top level.
        """
        print("Green's function Calculation started.")

        widgets = [
            ' [',
            progressbar.Timer(),
            '] ',
            progressbar.Bar(),
            ' (',
            progressbar.ETA(),
            ') ',
        ]
        bar = progressbar.ProgressBar(maxval=len(self.contour.path),
                                      widgets=widgets)
        bar.start()
        if self.np == 1:
            results = map(self.get_AijR_rhoR, self.contour.path)
        else:
            pool = ProcessPool(nodes=self.np)
            results = pool.map(self.get_AijR_rhoR, self.contour.path)
        for i, result in enumerate(results):
            bar.update(i)
            rup, rdn, Jorb_list, JJ_list = result
            self.rho_up_list.append(rup)
            self.rho_dn_list.append(rdn)
            for iR, R in enumerate(self.R_ijatom_dict):
                for (iatom, jatom) in self.R_ijatom_dict[R]:
                    key = (R, iatom, jatom)
                    self.Jorb_list[key].append(Jorb_list[key])
                    self.JJ_list[key].append(JJ_list[key])
        if self.np > 1:
            pool.close()
            pool.join()
            pool.clear()
        self.integrate()
        self.get_rho_atom()
        self.A_to_Jtensor()
        bar.finish()

    def write_output(self, path='TB2J_results'):
        self._prepare_index_spin()
        output = SpinIO(
            atoms=self.atoms,
            charges=self.charges,
            spinat=self.spinat,
            index_spin=self.index_spin,
            colinear=True,
            distance_dict=self.distance_dict,
            exchange_Jdict=self.exchange_Jdict,
            exchange_Jdict_orb=self.exchange_Jdict_orb,
            dmi_ddict=None,
            NJT_Jdict=None,
            NJT_ddict=None,
            Jani_dict=None,
            biquadratic_Jdict=None,
            description=self.description,
        )
        output.write_all(path=path)