class TestGeometryPlot(GeometryPlotTester): run_for = { "sisl_geom": { "init_func": sisl.geom.graphene(orthogonal=True).plot }, "ghost_atoms": { "init_func": sisl.Geometry([[0, 0, 1], [1, 0, 0]], atoms=[sisl.Atom(6), sisl.Atom(-6)]).plot } }
def init_func_and_attrs(self, request): name = request.param if name == "sisl_geom": init_func = sisl.geom.graphene(orthogonal=True).plot elif name == "ghost_atoms": init_func = sisl.Geometry([[0, 0, 1], [1, 0, 0]], atoms=[sisl.Atom(6), sisl.Atom(-6)]).plot attrs = {} return init_func, attrs
def pre_prepare_ase_after_relax(initial_XV,final_XV,Ghost): """ """ import sisl if Ghost ==True: print ('Finding Ghosts in XV') #Ghost_initial = sisl.Geometry(initial_XV.xyz[-2], # atoms = initial_XV.atoms.Z[-2]) #Ghost_final = sisl.Geometry(initial_XV.xyz[-1], # atoms = initial_XV.atoms.Z[-1]) Ghost_initial_Info = sisl.Atom(-1 * initial_XV.atoms[-2].Z) Ghost_initial = sisl.Geometry( initial_XV.xyz[-2], atoms= sisl.Atom( -1* Ghost_initial_Info.Z , tag=Ghost_initial_Info.symbol+"_ghost")) Ghost_final_Info = sisl.Atom(-1 * initial_XV.atoms[-1].Z) Ghost_final = sisl.Geometry( initial_XV.xyz[-1], atoms=sisl.Atom( -1* Ghost_final_Info.Z,tag = Ghost_final_Info.symbol+"_ghost" )) trace_atom_initial = sisl.Geometry(initial_XV.xyz[-3], atoms = initial_XV.atoms.Z[-3]) trace_atom_final = sisl.Geometry(final_XV.xyz[-3], atoms = final_XV.atoms.Z[-3]) initial_XV = initial_XV.remove([-1]) initial_XV = initial_XV.remove([-1]) final_XV = final_XV.remove([-1]) final_XV = final_XV.remove([-1]) info_sisl = {'initial' : initial_XV, 'final' : final_XV, 'trace_atom_initial' : trace_atom_initial, 'trace_atom_final' : trace_atom_final, 'Ghost_initial' : Ghost_initial, 'Ghost_final' : Ghost_final, } else: trace_atom_initial = initial_XV[-1] trace_atom_final = final_XV[-1] info_sisl ={'initial' : initial_XV, 'final' : final_XV, 'trace_atom_initial' : trace_atom_initial, 'trace_atom_final' : trace_atom_final, } return info_sisl
def from_yaml(cls, file, nodes=()): """ Parse the yaml file """ from sisl_toolbox.siesta.minimizer._yaml_reader import read_yaml, parse_variable dic = read_yaml(file, nodes) element = dic["element"] tag = dic.get("tag") mass = dic.get("mass", None) # get default options for pseudo opts = NotNonePropertyDict() pseudo = dic["pseudo"] opts["logr"] = parse_variable(pseudo.get("log-radii"), unit="Ang").value opts["rcore"] = parse_variable(pseudo.get("core-correction"), unit="Ang").value opts["xc"] = pseudo.get("xc") opts["equation"] = pseudo.get("equation") opts["flavor"] = pseudo.get("flavor") define = pseudo.get("define", ('NEW_CC', 'FREE_FORMAT_RC_INPUT', 'NO_PS_CUTOFFS')) # Now on to parsing the valence shells orbs = [] for key in dic: if key not in _shell_order: continue # Now we know the occupation is a shell pseudo = dic[key].get("pseudo", {}) cutoff = parse_variable(pseudo.get("cutoff"), 2.1, "Ang").value charge = parse_variable(pseudo.get("charge"), 0.).value orbs.append(si.AtomicOrbital(key, m=0, R=cutoff, q0=charge)) atom = si.Atom(element, orbs, mass=mass, tag=tag) return cls(atom, define, **opts)
def test_tshs_warn(sisl_files, sisl_tmp): si = sisl.get_sile(sisl_files(_dir, 'si_pdos_kgrid.TSHS')) # check number of orbitals geom = si.read_geometry() geom._atoms = sisl.Atoms([sisl.Atom(i + 1) for i in range(geom.na)]) with pytest.warns(sisl.SislWarning, match='number of orbitals'): si.read_hamiltonian(geometry=geom) # check cell geom = si.read_geometry() geom.sc.cell[:, :] = 1. with pytest.warns(sisl.SislWarning, match='lattice vectors'): si.read_hamiltonian(geometry=geom) # check atomic coordinates geom = si.read_geometry() geom.xyz[0, :] += 10. with pytest.warns(sisl.SislWarning, match='atomic coordinates'): si.read_hamiltonian(geometry=geom) # check supercell geom = si.read_geometry() geom.set_nsc([1, 1, 1]) with pytest.warns(sisl.SislWarning, match='supercell'): si.read_hamiltonian(geometry=geom)
def dispatch(self, t=(-2.414, -0.168), beta=(-1.847, -3.077), a=1.42, orthogonal=False): distance = self._obj.distance da = 0.0005 R = (distance(0, a) + da, distance(1, a) + da, distance(2, a) + da) def construct(H, ia, atoms, atoms_xyz=None): idx_t012, rij_t012 = H.geometry.close(ia, R=R, atoms=atoms, atoms_xyz=atoms_xyz, ret_rij=True) H[ia, idx_t012[0]] = 0. H[ia, idx_t012[1]] = t[0] * np.exp(beta[0] * (rij_t012[1] - R[1])) H[ia, idx_t012[2]] = t[1] * np.exp(beta[1] * (rij_t012[2] - R[2])) # Define the graphene lattice C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1])) graphene = si.geom.graphene(a, C, orthogonal=orthogonal) # Define the Hamiltonian H = si.Hamiltonian(graphene) H.construct(construct) return H
def Ghost_block(sisl_images): """ """ import sisl import numpy as np Ghost_Species = np.array([]) Ghost_Species_name = np.array([]) count = 0 n_ghost = 0 for i in range(sisl_images[0].atoms.nspecie): count = int(count + 1) print(count, sisl_images[0].atoms.atom[i].Z) #print (count) if sisl_images[0].atoms.atom[i].Z < 0: n_ghost = n_ghost + 1 a = sisl.Atom(abs(sisl_images[0].atoms.atom[i].Z)) Ghost_Species = np.append(Ghost_Species, int(count)) Ghost_Species = np.append(Ghost_Species, int(sisl_images[0].atoms.atom[i].Z)) Ghost_Species_name = np.append(Ghost_Species_name, a.symbol + '_ghost') Ghost_Species = Ghost_Species.reshape(n_ghost, 2) Ghost_Species_name = Ghost_Species_name.reshape(n_ghost, 1) F = open('ghost_block_temp', 'w') F.writelines('\n') F.writelines('%block Geometry-Constraints\n') for i in range(Ghost_Species.shape[0]): F.writelines("species-i {:} \n".format(int(Ghost_Species[i][0]))) F.writelines('%endblock Geometry-Constraints\n') F.writelines('\n') F.writelines('%include parameters.fdf \n') F.close()
def dispatch(self, t=-2.7, a=1.42, orthogonal=False): # Define the graphene lattice da = 0.0005 C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=a + da)) graphene = si.geom.graphene(a, C, orthogonal=orthogonal) # Define the Hamiltonian H = si.Hamiltonian(graphene) H.construct([(da, a + da), (0, t)]) return H
def sview(g, ghosts_z=None, **kwargs): if ghosts_z is not None: g = g.copy() cw = warnings.catch_warnings() cw.__enter__() warnings.simplefilter("ignore") replacement = si.Atom(ghosts_z) for a in g.atoms._atom: if a.Z < 1: g.atoms.replace(a, replacement) g.atoms.reduce(in_place=True) cw.__exit__() aview(g.toASE(), **kwargs)
def dispatch(self, a=1.42, orthogonal=False): distance = self._obj.distance da = 0.0005 R = (distance(0, a) + da, distance(1, a) + da, distance(2, a) + da, distance(3, a) + da) # Define the graphene lattice C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1])) graphene = si.geom.graphene(a, C, orthogonal=orthogonal) # Define the Hamiltonian H = si.Hamiltonian(graphene, orthogonal=False) t = [(-0.45, 1), (-2.78, 0.117), (-0.15, 0.004), (-0.095, 0.002)] H.construct([R, t]) return H
def dispatch(self, set='A', a=1.42, orthogonal=False): distance = self._obj.distance da = 0.0005 H_orthogonal = True #U = 2.0 R = tuple(distance(i, a) + da for i in range(4)) if set == 'A': # same as simple t = (0, -2.7) #U = 0. elif set == 'B': # same as simple t = (0, -2.7) elif set == 'C': t = (0, -2.7, -0.2) elif set == 'D': t = (0, -2.7, -0.2, -0.18) elif set == 'E': # same as D, but specific for GNR t = (0, -2.7, -0.2, -0.18) elif set == 'F': # same as D, but specific for GNR t = [(0, 1), (-2.7, 0.11), (-0.09, 0.045), (-0.27, 0.065)] H_orthogonal = False elif set == 'G': # same as D, but specific for GNR t = [(0, 1), (-2.97, 0.073), (-0.073, 0.018), (-0.33, 0.026)] #U = 0. H_orthogonal = False else: raise ValueError( f"Set specification for {self.doi} does not exist, should be one of [A-G]" ) # Reduce size of R R = R[:len(t)] # Currently we do not carry over U, since it is not specified for the # sisl objects.... # Define the graphene lattice C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1])) graphene = si.geom.graphene(a, C, orthogonal=orthogonal) graphene.optimize_nsc([0, 1]) # Define the Hamiltonian H = si.Hamiltonian(graphene, orthogonal=H_orthogonal) H.construct([R, t]) return H
def dispatch(self, t=-2.7, a=1.42, orthogonal=False): distance = self._obj.distance da = 0.0005 R = (distance(0, a) + da, distance(1, a) + da) def construct(H, ia, atoms, atoms_xyz=None): idx_t01, rij_t01 = H.geometry.close(ia, R=R, atoms=atoms, atoms_xyz=atoms_xyz, ret_rij=True) H[ia, idx_t01[0]] = 0. H[ia, idx_t01[1]] = t * (a / rij_t01[1])**2 # Define the graphene lattice C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1])) graphene = si.geom.graphene(a, C, orthogonal=orthogonal) # Define the Hamiltonian H = si.Hamiltonian(graphene) H.construct(construct) return H
def pre_prepare_sisl (frac_or_cart_or_index,Initial_Geom,InitialAtomPosition,FinalAtomPosition,KickedAtomPosition,rtol=1e-2,atol=1e-2): """ """ import sisl from sisl import Atom if frac_or_cart_or_index == 'cartesian': print ("Cartesian ...") #Initial_Geom = Fdf.read_geometry() XYZ = Initial_Geom.xyz InitialASEXYZ = Initial_Geom FinalASEXYZ = Initial_Geom print ("Removing Vacancies Ang/Bohr") print ("Removing Index for Initial Atom:{}".format(AtomIndex(XYZ,InitialAtomPosition,rtol,atol))) print ("Removing Index for Final Atom:{}".format(AtomIndex(XYZ,FinalAtomPosition,rtol,atol))) # Fixing tags TraceAtom_Initial_Info = sisl.Atom(Initial_Geom.atoms.Z[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)]) TraceAtom_Initial = sisl.Geometry(xyz=Initial_Geom.xyz[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)], atoms= sisl.Atom(TraceAtom_Initial_Info.Z,tag=TraceAtom_Initial_Info.symbol+'_i' )) Ghost_initial = sisl.Geometry(xyz=Initial_Geom.xyz[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)], atoms= sisl.Atom(TraceAtom_Initial_Info.Z,tag=TraceAtom_Initial_Info.symbol+'_ghost' )) TraceAtom_Final_Info = sisl.Atom(Initial_Geom.atoms.Z[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)]) TraceAtom_Final = sisl.Geometry(xyz = FinalAtomPosition , atoms= sisl.Atom(TraceAtom_Final_Info.Z,tag=TraceAtom_Final_Info.symbol+'_i')) Ghost_final = sisl.Geometry(xyz = FinalAtomPosition , atoms= sisl.Atom(TraceAtom_Final_Info.Z,tag=TraceAtom_Final_Info.symbol+'_ghost')) InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(XYZ,InitialAtomPosition,rtol,atol)) FinalASEXYZ = InitialASEXYZ elif frac_or_cart_or_index == 'frac': #print ("Removing Vacancies Fractional NOT Implemented") print ("Fractional ...") Frac = Initial_Geom.fxyz InitialASEXYZ = Initial_Geom FinalASEXYZ = Initial_Geom print ("Removing Vacancies Ang/Bohr") print ("Removing Index for Initial Atom:{}".format(AtomIndex(Frac,InitialAtomPosition,rtol,atol))) print ("Removing Index for Final Atom:{}".format(AtomIndex(Frac,FinalAtomPosition,rtol,atol))) trace_atom_initial = sisl.Geometry(Initial_Geom.xyz[AtomIndex(Frac,InitialAtomPosition,rtol,atol)], atoms= Initial_Geom.atoms.Z[AtomIndex(Frac,InitialAtomPosition,rtol,atol)]) trace_atom_final = sisl.Geometry(Initial_Geom.xyz[AtomIndex(Frac,FinalAtomPosition,rtol,atol)], atoms= Initial_Geom.atoms.Z[AtomIndex(Frac,FinalAtomPosition,rtol,atol)]) InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(Frac,InitialAtomPosition,rtol,atol)) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(Frac,FinalAtomPosition,rtol,atol)) if AtomIndex(Frac,FinalAtomPosition,rtol,atol) > AtomIndex(Frac,InitialAtomPosition,rtol,atol): print ("Order : Final Atom Position > Initial Atom Position") InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(Frac,FinalAtomPosition,rtol,atol)-1) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(Frac,InitialAtomPosition,rtol,atol)) if AtomIndex(Frac,FinalAtomPosition,rtol,atol) < AtomIndex(Frac,InitialAtomPosition,rtol,atol): print ("Order : Initial Atom Position > Final Atom Position") InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(Frac,FinalAtomPosition,rtol,atol)) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(Frac,InitialAtomPosition,rtol,atol)-1) else: print('index') #Frac = Initial_Geom.fxyz InitialASEXYZ = Initial_Geom FinalASEXYZ = Initial_Geom print ("Removing Vacancies Ang/Bohr") print ("Removing Index for Initial Atom:{}".format(InitialAtomPosition-1)) print ("Removing Index for Final Atom:{}".format(FinalAtomPosition-1)) #trace_atom_A_initial = sisl.Geometry(Initial_Geom.xyz[InitialAtomPosition-1], # atoms= Initial_Geom.atoms.Z[InitialAtomPosition-1]) #trace_atom_B_final = sisl.Geometry(Initial_Geom.xyz[FinalAtomPosition-1], # atoms= Initial_Geom.atoms.Z[FinalAtomPosition-1]) trace_atom_A_initial = sisl.Geometry(Initial_Geom.xyz[InitialAtomPosition-1], atoms= Initial_Geom.atoms.Z[InitialAtomPosition-1]) trace_atom_A_final = sisl.Geometry(Initial_Geom.xyz[FinalAtomPosition-1], atoms= Initial_Geom.atoms.Z[InitialAtomPosition-1]) trace_atom_B_initial = sisl.Geometry(Initial_Geom.xyz[FinalAtomPosition-1], atoms= Initial_Geom.atoms.Z[FinalAtomPosition-1]) trace_atom_B_final = sisl.Geometry(Initial_Geom.xyz[InitialAtomPosition-1], atoms= Initial_Geom.atoms.Z[FinalAtomPosition-1]) InitialASEXYZ = InitialASEXYZ.remove(InitialAtomPosition-1) FinalASEXYZ = FinalASEXYZ.remove(FinalAtomPosition-1) if (FinalAtomPosition-1) > (InitialAtomPosition-1): print ("Order : Final Atom Position > Initial Atom Position") InitialASEXYZ = InitialASEXYZ.remove(FinalAtomPosition-2) FinalASEXYZ = FinalASEXYZ.remove(InitialAtomPosition-1) if (FinalAtomPosition-1) < (InitialAtomPosition-1): print ("Order : Initial Atom Position > Final Atom Position") InitialASEXYZ = InitialASEXYZ.remove(FinalAtomPosition-1) FinalASEXYZ = FinalASEXYZ.remove(InitialAtomPosition-2) InitialASEXYZ = InitialASEXYZ.add(TraceAtom_Initial) FinalASEXYZ = FinalASEXYZ.add(TraceAtom_Final) info_sisl = {'initial' : InitialASEXYZ, 'final' : FinalASEXYZ, 'trace_atom_initial' : TraceAtom_Initial, 'trace_atom_final' : TraceAtom_Final, 'Ghost_initial' : Ghost_initial, 'Ghost_final' : Ghost_final, } return info_sisl
def si_pdos_kgrid_geom(): return sisl.Geometry([[0, 0, 0], [1, 1, 1]], sisl.Atom('Si', R=np.arange(13) + 1))
def CAP(geometry, side, dz_CAP=30, write_xyz=True, zaxis=2): # Determine orientation if zaxis == 2: xaxis, yaxis = 0, 1 elif zaxis == 0: xaxis, yaxis = 1, 2 elif zaxis == 1: xaxis, yaxis = 0, 2 # Natural units (see "http://superstringtheory.com/unitsa.html") hbar = 1 m = 0.511e6 # eV c = 2.62 print('\nSetting up CAP regions: {}'.format(side)) print('Width of absorbing walls = {} Angstrom'.format(dz_CAP)) Wmax = 100 dH_CAP = si.Hamiltonian(geometry, dtype='complex128') CAP_list = [] ### EDGES if 'right' in side: print('Setting at right') z, y = geometry.xyz[:, xaxis], geometry.xyz[:, yaxis] z2 = np.max(geometry.xyz[:, xaxis]) + 1. z1 = z2 - dz_CAP idx = np.where(np.logical_and(z1 <= z, z < z2))[0] fz = (4 / (c**2)) * ((dz_CAP / (z2 - 2 * z1 + z[idx]))**2 + (dz_CAP / (z2 - z[idx]))**2 - 2) Wz = ((hbar**2) / (2 * m)) * (2 * np.pi / (dz_CAP / 2000))**2 * fz orbs = dH_CAP.geom.a2o( idx) # if you have just 1 orb per atom, then orb = ia for orb, wz in zip(orbs, Wz): dH_CAP[orb, orb] = complex(0, -wz) CAP_list.append(idx) #print(list2range_TBTblock(idx)) if 'left' in side: print('Setting at left') z, y = geometry.xyz[:, xaxis], geometry.xyz[:, yaxis] z2 = np.min(geometry.xyz[:, xaxis]) - 1. z1 = z2 + dz_CAP idx = np.where(np.logical_and(z2 < z, z <= z1))[0] fz = (4 / (c**2)) * ((dz_CAP / (z2 - 2 * z1 + z[idx]))**2 + (dz_CAP / (z2 - z[idx]))**2 - 2) Wz = ((hbar**2) / (2 * m)) * (2 * np.pi / (dz_CAP / 2000))**2 * fz orbs = dH_CAP.geom.a2o( idx) # if you have just 1 orb per atom, then orb = ia for orb, wz in zip(orbs, Wz): dH_CAP[orb, orb] = complex(0, -wz) CAP_list.append(idx) #print(list2range_TBTblock(idx)) if 'top' in side: print('Setting at top') z, y = geometry.xyz[:, xaxis], geometry.xyz[:, yaxis] y2 = np.max(geometry.xyz[:, yaxis]) + 1. y1 = y2 - dz_CAP idx = np.where(np.logical_and(y1 <= y, y < y2))[0] fz = (4 / (c**2)) * ((dz_CAP / (y2 - 2 * y1 + y[idx]))**2 + (dz_CAP / (y2 - y[idx]))**2 - 2) Wz = ((hbar**2) / (2 * m)) * (2 * np.pi / (dz_CAP / 2000))**2 * fz orbs = dH_CAP.geom.a2o( idx) # if you have just 1 orb per atom, then orb = ia for orb, wz in zip(orbs, Wz): dH_CAP[orb, orb] = complex(0, -wz) CAP_list.append(idx) #print(list2range_TBTblock(idx)) if 'bottom' in side: print('Setting at bottom') z, y = geometry.xyz[:, xaxis], geometry.xyz[:, yaxis] y2 = np.min(geometry.xyz[:, yaxis]) - 1. y1 = y2 + dz_CAP idx = np.where(np.logical_and(y2 < y, y <= y1))[0] fz = (4 / (c**2)) * ((dz_CAP / (y2 - 2 * y1 + y[idx]))**2 + (dz_CAP / (y2 - y[idx]))**2 - 2) Wz = ((hbar**2) / (2 * m)) * (2 * np.pi / (dz_CAP / 2000))**2 * fz orbs = dH_CAP.geom.a2o( idx) # if you have just 1 orb per atom, then orb = ia for orb, wz in zip(orbs, Wz): dH_CAP[orb, orb] = complex(0, -wz) CAP_list.append(idx) #print(list2range_TBTblock(idx)) CAP_list = np.concatenate(CAP_list).ravel().tolist() if write_xyz: # visualize CAP regions visualize = geometry.copy() visualize.atom[CAP_list] = si.Atom(8, R=[1.44]) visualize.write('CAP.xyz') return dH_CAP
H.write('inside_HS_DEV.nc') # Create CAP from lib_dft2tb import CAP dH_CAP = CAP(H.geom, 'left+right', dz_CAP=50, write_xyz=True, zaxis=2) dH_CAP_sile = si.get_sile('CAP.delta.nc', 'w') dH_CAP_sile.write_delta(dH_CAP) ############################ ############################ ############################ # Define atoms where Gamma exists a_tip = [3704] # Check tmp = H.geom.copy() tmp.atom[a_tip] = si.Atom(16, R=[1.44]) tmp.write('a_tip.xyz') # Setup TBTGF for tip injection # It is vital that you also write an electrode Hamiltonian, # i.e. the Hamiltonian object passed as "Htip", has to be written: # Build HS for tip HStip = H.sub(a_tip) HStip.write('TBTGF_H.nc') # Now generate a TBTGF file GF = si.io.TBTGFSileTBtrans('Gamma.TBTGF') # Below we load whatever TBT file from which we can take the energies and kpoints # In the next TBTrans run we MUST use the same energy points and kpoints we load here #TBT = si.get_sile("elist.TBT.nc") # Energy contour
def construct_modular(H0, TSHS, modules, positions): """ H0: Host TB model to be rearranged. Modules will be progressively appended at the end of atomic list TSHS: list of DFT Hamiltonians for each module; needed to recover the input coordinates to map modules: list of tuples (a_Delta, Delta, area_ext, area_R2) as those obtained from tbtncTools.Delta provide one tuple for each module positions: list of xyz object (ndarray with shape=(1,3)) in H0 corresponding to the center of mass of each module provide one xyz for each module EXAMPLE OF USAGE: from tbtncTools import Delta a_Delta, _, Delta, area_ext, area_R2 = Delta(TSHS, shape='Cuboid', z_area=TSHS.xyz[0, 2], thickness=10., ext_offset=tshs_0.cell[1,:].copy(), zaxis=2, atoms=C_list) frame_tip = (a_Delta, Delta, area_ext, area_R2) xyz_tsource = H0.center(what='xyz') +(0.4*H0.cell[1,:]-[0,5.31,0]) xyz_tdrain = H0.center(what='xyz') -(0.4*H0.cell[1,:]-[0,5.31,0]) -0.5*TSHS.cell[0,:] Hfinal, l_al, l_buf = construct_modular(H0=H0, TSHS=[TSHS, TSHS] modules=[frame_tip, frame_tip], positions=[xyz_tsource, xyz_tdrain]) WARNING: maybe in some situations it might overlap some buffer and module atoms. So, ALWAYS CHECK THAT FINAL XYZ IS WHAT YOU ACTUALLY EXPECT!!! """ Htmp = H0.copy() l_nal = [] for i, hs, mod, xyz in zip(range(len(TSHS)), TSHS, modules, positions): print('\nciaoooooooooo\n') H, al = rearrange_H(hs, mod[0], Htmp, pos_dSE=xyz, area=mod[1]) nal = len(al) print(nal) l_nal.insert(0, nal) Htmp = H.copy() l_nal = np.asarray(l_nal) # Find atoms in each frame, write xyz and info about modules l_al = [] for i in range(len(l_nal)): first = len(H) - l_nal[:len(l_nal) - i].sum() last = len(H) - l_nal[:len(l_nal) - (i + 1)].sum() al = np.arange(first, last) l_al.append(al) from tbtncTools import list2range_TBTblock print('After reordering: \n{}'.format(list2range_TBTblock(al))) v = H.geom.copy() v.atom[al] = si.Atom(8, R=[1.44]) #v.atom[l_buf[i]] = si.Atom(10, R=[1.44]) v.write('module_{}.xyz'.format(i + 1)) # Print elec-pos end for use in tbtrans print("< module_{}.xyz > \n elec-pos end {} (or {})".format( i + 1, al[-1] + 1, -1 - (l_nal[:len(l_nal) - (i + 1)].sum()))) l_al = np.asarray(l_al) # Find buffer # NB: that cuboids are always independent from the sorting in the host geometry l_buf = [] for mod, xyz, al in zip(modules, positions, l_al): area_B = mod[2].copy() area_B.set_center(xyz) almostbuffer = area_B.within_index(H.xyz) buffer_i = np.in1d(almostbuffer, al, assume_unique=True, invert=True) buf = almostbuffer[buffer_i] l_buf.append(buf) # Check final frames and buffers all_al = np.concatenate(l_al[:]) all_buf = np.concatenate(l_buf[:]) v = H.geom.copy() v.atom[all_al] = si.Atom(8, R=[1.44]) v.atom[all_buf] = si.Atom(10, R=[1.44]) v.write('framesbuffer.xyz') # Write buffer xyz and fdf block from tbtncTools import list2range_TBTblock with open('block_buffer.fdf', 'w') as fb: fb.write("%block TBT.Atoms.Buffer\n") fb.write(list2range_TBTblock(all_buf)) fb.write("\n%endblock\n") return H, l_al, l_buf
def from_block(cls, block): """ Return an `Atom` for a specified basis block Parameters ---------- block : list or str the PAO.basis block (as read from an fdf file). Should be a list of lines. """ if isinstance(block, str): block = block.splitlines() else: # store local list block = list(block) def blockline(): nonlocal block out = "" while len(out) == 0: out = block.pop(0).split('#')[0].strip() return out # define global opts opts = {} specie = blockline() specie = specie.split() if len(specie) == 4: # we have Symbol, nl, type, ionic_charge symbol, nl, opts["type"], opts["ion_charge"] = specie elif len(specie) == 3: # we have Symbol, nl, type # or # we have Symbol, nl, ionic_charge symbol, nl, opt = specie try: opts["ion_charge"] = float(opt) except: opts["type"] = opt elif len(specie) == 2: # we have Symbol, nl symbol, nl = specie type = None # now loop orbitals orbs = [] for _ in range(int(nl)): # we have 2 or 3 lines nl_line = blockline() rc_line = blockline() # check if we have contraction in the line # This is not perfect, but should grab # contration lines rather than next orbital line. # This is because the first n=<integer> should never # contain a ".", whereas the contraction *should*. if len(block) > 0: if '.' in block[0].split()[0]: contract_line = blockline() # remove n= nl_line = nl_line.replace("n=", "").split() # first 3 are n, l, Nzeta n = int(nl_line.pop(0)) l = int(nl_line.pop(0)) nzeta = int(nl_line.pop(0)) # assign defaults nlopts = {} while len(nl_line) > 0: opt = nl_line.pop(0) if opt == "P": try: npol = int(nl_line[0]) nl_line.pop(0) nlopts["pol"] = npol except: nlopts["pol"] = 1 elif opt == "S": nlopts["split"] = float(nl_line.pop(0)) elif opt == "F": nlopts["filter"] = float(nl_line.pop(0)) / _eV2Ry elif opt == "E": # 1 or 2 values V0 = float(nl_line.pop(0)) / _eV2Ry try: ri = float(nl_line[0]) / _Ang2Bohr nl_line.pop(0) except: # default to None (uses siesta default) ri = None nlopts["soft"] = [V0, ri] elif opt == "Q": # 1, 2 or 3 values charge = float(nl_line.pop(0)) try: # this is in Bohr-1 yukawa = float(nl_line[0]) * _Ang2Bohr nl_line.pop(0) except: # default to None (uses siesta default) yukawa = None try: width = float(nl_line[0]) / _Ang2Bohr nl_line.pop(0) except: # default to None (uses siesta default) width = None nlopts["charge"] = [charge, yukawa, width] # now we have everything to build the orbitals etc. for izeta, rc in enumerate(map(float, rc_line.split()), 1): if rc > 0: rc /= _Ang2Bohr elif rc < 0 and izeta > 1: rc *= -orbs[-1].R elif rc == 0 and izeta > 1: # this is ok, the split-norm will be used to # calculate the radius pass else: raise ValueError( f"Could not parse the PAO.Basis block for the zeta ranges {rc_line}." ) orb = si.AtomicOrbital(n=n, l=l, m=0, zeta=izeta, R=rc) nzeta -= 1 orbs.append(orb) # In case the final orbitals hasn't been defined. # They really should be defined in this one, but sometimes it may be # useful to leave the rc's definitions out. rc = orbs[-1].R for izeta in range(nzeta): orb = si.AtomicOrbital(n=n, l=l, m=0, zeta=orbs[-1].zeta + 1, R=rc) orbs.append(orb) opts[(n, l)] = nlopts # Now create the atom atom = si.Atom(symbol, orbs) return cls(atom, opts)
def from_input(cls, inp): """ Return atom object respecting the input Parameters ---------- inp : list or str create `AtomInput` from the content of `inp` """ def _get_content(f): if f.is_file(): return open(f, 'r').readlines() return None if isinstance(inp, (tuple, list)): # it is already in correct format pass elif isinstance(inp, (str, Path)): # convert to path inp = Path(inp) # Check if it is a path or an input content = _get_content(inp) if content is None: content = _get_content(inp / "INP") if content is None: raise ValueError( f"Could not find any input file in {str(inp)} or {str(inp / 'INP')}" ) inp = content else: raise ValueError(f"Unknown input format inp={inp}?") # Now read lines defines = [] opts = PropertyDict() def bypass_comments(inp): if inp[0].startswith("#"): inp.pop(0) bypass_comments(inp) def bypass(inp, defines): bypass_comments(inp) if inp[0].startswith("%define"): line = inp.pop(0) defines.append(line.split()[1].strip()) bypass(inp, defines) bypass(inp, defines) # Now prepare reading # First line has to contain the *type* of calculation # pg|pe|ae|pt <comment> line = inp.pop(0).strip() if line.startswith("pg"): opts.cc = False elif line.startswith("pe"): opts.cc = True # <flavor> logr? line = inp.pop(0).strip().split() opts.flavor = line[0] if len(line) >= 2: opts.logr = float(line[1]) / _Ang2Bohr # <element> <xc>' rs'? line = inp.pop(0) symbol = line.split()[0] # now get xc equation if len(line) >= 11: opts.equation = line[10:10] opts.xc = line[:10].split()[1] line = line.split() if len(line) >= 3: opts.libxc = int(line[2]) # currently not used line inp.pop(0) # core, valence core, valence = inp.pop(0).split() opts.core = int(core) valence = int(valence) orbs = [] for _ in range(valence): n, l, *occ = inp.pop(0).split() orb = PropertyDict() orb.n = int(n) orb.l = int(l) # currently we don't distinguish between up/down orb.q0 = sum(map(float, occ)) orbs.append(orb) # now we read the line with rc's and core-correction rcs = inp.pop(0).split() if len(rcs) >= 6: # core-correction opts.rcore = float(rcs[5]) / _Ang2Bohr for orb in orbs: orb.R = float(rcs[orb.l]) / _Ang2Bohr # Now create orbitals orbs = [si.AtomicOrbital(**orb, m=0, zeta=1) for orb in orbs] # now re-arrange ensuring we have correct order of l shells orbs = sorted(orbs, key=lambda orb: orb.l) atom = si.Atom(symbol, orbs) return cls(atom, defines, **opts)
def map_xyz(A, B, area_R_A, a_R_A=None, center_B=None, pos_B=None, area_for_buffer=None, tol=None): ### FRAME print('\nMapping from geometry A to geometry B') if a_R_A is None: # Recover atoms in R_A region of model A a_R_A = area_R_A.within_index(A.xyz) # Find the set R_B of unique corresponding atoms in model B area_R_B = area_R_A.copy() if pos_B is not None: vector = pos_B B_translated = B.translate(-vector) else: if center_B is None: center_B = B.center(what='xyz') vector = center_B - area_R_B.center B_translated = B.translate(-vector) a_R_B = area_R_B.within_index(B_translated.xyz) R_A = A.sub(a_R_A) R_B = B.sub(a_R_B) v1, v2 = np.amin(R_A.xyz, axis=0), np.amin(R_B.xyz, axis=0) xyz_B_shifted = R_B.xyz - v2[None, :] xyz_A_shifted = R_A.xyz - v1[None, :] if tol is None: tol = [0.01, 0.01, 0.01] tol = np.asarray(tol) print('Tolerance along x,y,z is set to {}'.format(tol)) # Check if a_R_A is included in a_R_B and if yes, try to solve the problem # Following `https://stackoverflow.com/questions/33513204/finding-intersection-of-two-matrices-in-python-within-a-tolerance` # Get absolute differences between xyz_B_shifted and xyz_A_shifted keeping their columns aligned diffs = np.abs( xyz_B_shifted.reshape(-1, 1, 3) - xyz_A_shifted.reshape(1, -1, 3)) # Compare each row with the triplet from `tol`. # Get mask of all matching rows and finally get the matching indices x1, x2 = np.nonzero((diffs < tol.reshape(1, 1, -1)).all(2)) idx_swap = np.argsort(x2[:len(x1)]) x1_reorder = x1[idx_swap] # CHECK if len(x1) == len(a_R_A): if len(a_R_B) > len(a_R_A): print('\nWARNING: len(a_R_A) = {} is not equal to len(a_R_B) = {}'. format(len(a_R_A), len(a_R_B))) print( 'But since a_R_A is entirely contained in a_R_B, I will fix it by removing the extra atoms' ) print( '\n OK! The coordinates of the mapped atoms in the two geometries match \ within the desired tolerance! ;)') else: print('\nWARNING: len(a_R_A) = {} is not equal to len(a_R_B) = {}'. format(len(a_R_A), len(a_R_B))) print('\n STOOOOOP: not all elements of a_R_A are in a_R_B') print(' Check `a_R_B_not_matching.xyz` vs `a_R_A.xyz` and \ try to set `pos_B` to `B.center(what=\'xyz\') + [dx,dy,dz]`. Or increase the tolerance' ) v = B.geom.copy() v.atom[a_R_B] = si.Atom(8, R=[1.44]) v.write('a_R_B_not_matching.xyz') exit(1) a_R_B, a_R_A = a_R_B[x1_reorder], a_R_A[x2] # Further CHECK, just to be sure if not np.allclose(xyz_B_shifted[x1], xyz_A_shifted[x2], rtol=np.amax(tol), atol=np.amax(tol)): print( '\n STOOOOOP: The coordinates of the mapped atoms in the two geometries don\'t match \ within the tolerance!!!!') print(' Check `a_R_B_not_matching.xyz` vs `a_R_A.xyz` and \ try to set `pos_B` to `B.center(what=\'xyz\') + [dx,dy,dz]`. Or increase the tolerance' ) v = B.geom.copy() v.atom[a_R_B] = si.Atom(8, R=[1.44]) v.write('a_R_B_not_matching.xyz') exit(1) print(' Max deviation (Ang) =', np.amax(xyz_A_shifted[x2] - xyz_B_shifted[x1])) # WARNING: we are about to rearrange the atoms in the host geometry!!! a_R_B_rearranged, new_B = rearrange(B, a_R_B, where='end') print("\nSelected atoms mapped into host geometry, after rearrangement\n\ at the end of the coordinates list (1-based): {}\n{}".format( len(a_R_B_rearranged), list2range_TBTblock(a_R_B_rearranged))) # Find and print buffer atoms if area_for_buffer is not None: # NB: that cuboids are always independent from the sorting in the host geometry area_B = area_for_buffer.copy() if center_B is not None: area_B.set_center(center_B) almostbuffer = area_B.within_index(new_B.xyz) buffer_i = np.in1d(almostbuffer, a_R_B_rearranged, assume_unique=True, invert=True) buffer = almostbuffer[buffer_i] return a_R_B_rearranged, new_B, buffer else: return a_R_B_rearranged, new_B
def makearea(TSHS, shape='Cuboid', z_area=None, ext_offset=None, center=None, thickness=None, zaxis=2, atoms=None, segment_dir=None): """ In frame cases, we are going to define an outer area 'area_ext' and an inner area 'area_R2', and we are going to subtract them. The resulting area is called 'Delta' TSHS: Hamiltonian or geometry object shape: shape (check sisl doc) ['Cube', 'Cuboid', 'Ellipsoid', 'Sphere', 'Segment'] z_area: coordinate at which the area should be defined [Angstrom] Default is along direction 2. Otherwise, change z_axis ext_offset: [3x1 array] Use this to set an offset along any direction in area_ext center: center of area plane in TSHS thickness: thickness of area [Angstrom] zaxis: [0,1,2] direction perpendicular to area plane atoms: list of atomic indices to filter the atoms inside Delta segment_dir: [0,1,2] direction along the smallest side of the segment Note: - it works for orthogonal cells! Needs some adjustments to be general... """ # z coordinate of area plane if z_area is None: print('\n\nPlease provide a value for z_area in makearea routine') exit(1) # Center of area plane in TSHS cellcenter = TSHS.center(atom=(TSHS.xyz[:, zaxis] == z_area).nonzero()[0]) if center is None: center = cellcenter center = np.asarray(center) # make sure it's an array # Thickness in Ang if thickness is None: thickness = 6. # Ang thickness = np.asarray(thickness, np.float64) # Cuboid or Ellissoid? if zaxis == 2: size = .5 * np.diagonal(TSHS.cell) + [ 0, 0, 300 ] # default radius is half the cell size elif zaxis == 0: size = .5 * np.diagonal(TSHS.cell) + [ 300, 0, 0 ] # default radius is half the cell size elif zaxis == 1: size = .5 * np.diagonal(TSHS.cell) + [ 0, 300, 0 ] # default radius is half the cell size if shape == 'Ellipsoid' or shape == 'Sphere': mkshape = si.shape.Ellipsoid elif shape == 'Cuboid' or shape == 'Cube': mkshape = si.shape.Cuboid # In this case it's the full perimeter so we double size *= 2 thickness *= 2 if ext_offset is not None: ext_offset = np.asarray(ext_offset, np.float64).copy() ext_offset *= 2 elif shape == 'Segment': mkshape = si.shape.Cuboid # In this case it's the full perimeter so we double size *= 2 size[segment_dir] = thickness if ext_offset is not None: ext_offset = np.asarray(ext_offset, np.float64).copy() else: print('\n shape = "{}" is not implemented...'.format(shape)) exit(1) if shape == 'Segment': # ADD COMPLEMENTARY AREA... # Areas Delta = mkshape(size, center=center) # Atoms within Delta and complementary area a_Delta = Delta.within_index(TSHS.xyz) if atoms is not None: a_Delta = a_Delta[np.in1d(a_Delta, atoms)] # Check v = TSHS.geom.copy() v.atom[a_Delta] = si.Atom(8, R=[1.43]) v.write('a_Delta.xyz') return a_Delta, Delta else: # External boundary area_ext = mkshape(size, center=center) # Adjust with ext_offset if necessary if ext_offset is not None: ext_offset = np.asarray(ext_offset, np.float64) area_ext = area_ext.expand(-ext_offset) # Force it to be Cube or Sphere (side = ext_offset) if necessary if shape == 'Sphere' or shape == 'Cube': if len(ext_offset.nonzero()[0]) > 1: print( 'Offset is in both axes. Please set "shape" to Cuboid or Ellipsoid' ) exit(1) axis = ext_offset.nonzero()[0][0] print( 'Offset is non-zero along axis: {}...complementary is {}'. format(axis, int(axis < 1))) new_ext_offset = np.zeros(3) new_ext_offset[int(axis < 1)] = ext_offset[axis] area_ext = area_ext.expand(-new_ext_offset) # Internal boundary area_R2 = area_ext.expand(-thickness) # Disjuction composite shape Delta = area_ext - area_R2 # Atoms within Delta and internal boundary a_Delta = Delta.within_index(TSHS.xyz) a_int = area_R2.within_index(TSHS.xyz) if atoms is not None: a_Delta = a_Delta[np.in1d(a_Delta, atoms)] # Check v = TSHS.geom.copy() v.atom[a_Delta] = si.Atom(8, R=[1.43]) v.write('a_Delta.xyz') return a_Delta, a_int, Delta, area_ext, area_R2
from hubbard import HubbardHamiltonian, sp2 import numpy as np import sisl for w in range(1, 25, 2): g = sisl.geom.agnr(w) H0 = sp2(g) H = HubbardHamiltonian(H0, U=0) zak = H.get_Zak_phase() print(f'width={w:3}, zak={zak:7.3f}') # SSH model, topological cell g = sisl.Geometry([[0, 0, 0], [0, 1.65, 0]], sisl.Atom(6, 1.001), sc=[10, 3, 10]) g.set_nsc([1, 3, 1]) H0 = sp2(g) H = HubbardHamiltonian(H0, U=0) zak = H.get_Zak_phase(axis=1) print(f'SSH topo : zak={zak:7.3f}') # SSH model, trivial cell g = sisl.Geometry([[0, 0, 0], [0, 1.42, 0]], sisl.Atom(6, 1.001), sc=[10, 3, 10]) g.set_nsc([1, 3, 1]) H0 = sp2(g) H = HubbardHamiltonian(H0, U=0) zak = H.get_Zak_phase(axis=1) print(f'SSH triv : zak={zak:7.3f}')
def pre_prepare_sisl (frac_or_cart_or_index,Initial_Geom,InitialAtomPosition,FinalAtomPosition,rtol,atol,Ghost): """ """ import sisl if frac_or_cart_or_index == 'cartesian': print ("Cartesian ...(BUGGGGGGGG! SHOULD FIX)") #Initial_Geom = Fdf.read_geometry() XYZ = Initial_Geom.xyz InitialASEXYZ = Initial_Geom FinalASEXYZ = Initial_Geom print ("Removing Vacancies Ang/Bohr") print ("Removing Index for Initial Atom:{}".format(AtomIndex(XYZ,InitialAtomPosition,rtol,atol))) print ("Removing Index for Final Atom:{}".format(AtomIndex(XYZ,FinalAtomPosition,rtol,atol))) if Ghost == True: Ghost_initial = sisl.Geometry(Initial_Geom.xyz[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)], atoms= -1* Initial_Geom.atoms.Z[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)] ) Ghost_final = sisl.Geometry(Initial_Geom.xyz[AtomIndex(XYZ,FinalAtomPosition,rtol,atol)], atoms= -1* Initial_Geom.atoms.Z[AtomIndex(XYZ,FinalAtomPosition,rtol,atol)] ) trace_atom_initial = sisl.Geometry(Initial_Geom.xyz[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)], atoms= Initial_Geom.atoms.Z[AtomIndex(XYZ,InitialAtomPosition,rtol,atol)] ) trace_atom_final = sisl.Geometry(Initial_Geom.xyz[AtomIndex(XYZ,FinalAtomPosition,rtol,atol)], atoms= Initial_Geom.atoms.Z[AtomIndex(XYZ,FinalAtomPosition,rtol,atol)] ) InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(XYZ,InitialAtomPosition,rtol,atol)) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(XYZ,FinalAtomPosition,rtol,atol)) if AtomIndex(XYZ,FinalAtomPosition,rtol,atol) > AtomIndex(XYZ,InitialAtomPosition,rtol,atol): print ("Order : Final Atom Position > Initial Atom Position") InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(XYZ,FinalAtomPosition,rtol,atol)-1) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(XYZ,InitialAtomPosition,rtol,atol)) if AtomIndex(XYZ,FinalAtomPosition,rtol,atol) < AtomIndex(XYZ,InitialAtomPosition,rtol,atol): print ("Order : Initial Atom Position > Final Atom Position") InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(XYZ,FinalAtomPosition,rtol,atol)) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(XYZ,InitialAtomPosition,rtol,atol)-1) elif frac_or_cart_or_index == 'frac': #print ("Removing Vacancies Fractional NOT Implemented") print ("Fractional ... (BUGGGGGGGG SHOULD FIX)") Frac = Initial_Geom.fxyz InitialASEXYZ = Initial_Geom FinalASEXYZ = Initial_Geom print ("Removing Vacancies Ang/Bohr") print ("Removing Index for Initial Atom:{}".format(AtomIndex(Frac,InitialAtomPosition,rtol,atol))) print ("Removing Index for Final Atom:{}".format(AtomIndex(Frac,FinalAtomPosition,rtol,atol))) if Ghost == True: Ghost_initial = sisl.Geometry(Initial_Geom.xyz[AtomIndex(Frac,InitialAtomPosition,rtol,atol)], atoms= -1* Initial_Geom.atoms.Z[AtomIndex(Frac,InitialAtomPosition,rtol,atol)] ) Ghost_final = sisl.Geometry(Initial_Geom.xyz[AtomIndex(Frac,FinalAtomPosition,rtol,atol)], atoms= -1* Initial_Geom.atoms.Z[AtomIndex(Frac,FinalAtomPosition,rtol,atol)] ) trace_atom_initial = sisl.Geometry(Initial_Geom.xyz[AtomIndex(Frac,InitialAtomPosition,rtol,atol)], atoms= Initial_Geom.atoms.Z[AtomIndex(Frac,InitialAtomPosition,rtol,atol)] ) trace_atom_final = sisl.Geometry(Initial_Geom.xyz[AtomIndex(Frac,FinalAtomPosition,rtol,atol)], atoms= Initial_Geom.atoms.Z[AtomIndex(Frac,FinalAtomPosition,rtol,atol)] ) InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(Frac,InitialAtomPosition,rtol,atol)) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(Frac,FinalAtomPosition,rtol,atol)) if AtomIndex(Frac,FinalAtomPosition,rtol,atol) > AtomIndex(Frac,InitialAtomPosition,rtol,atol): print ("Order : Final Atom Position > Initial Atom Position") InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(Frac,FinalAtomPosition,rtol,atol)-1) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(Frac,InitialAtomPosition,rtol,atol)) if AtomIndex(Frac,FinalAtomPosition,rtol,atol) < AtomIndex(Frac,InitialAtomPosition,rtol,atol): print ("Order : Initial Atom Position > Final Atom Position") InitialASEXYZ = InitialASEXYZ.remove(AtomIndex(Frac,FinalAtomPosition,rtol,atol)) FinalASEXYZ = FinalASEXYZ.remove(AtomIndex(Frac,InitialAtomPosition,rtol,atol)-1) else: print('index') #Frac = Initial_Geom.fxyz InitialASEXYZ = Initial_Geom FinalASEXYZ = Initial_Geom print ("Removing Vacancies Ang/Bohr") print ("Removing Index for Initial Atom:{}".format(InitialAtomPosition-1)) print ("Removing Index for Final Atom:{}".format(FinalAtomPosition-1)) if Ghost == True: #Ghost_initial = sisl.Geometry(Initial_Geom.xyz[InitialAtomPosition-1], # atoms= -1* Initial_Geom.atoms.Z[InitialAtomPosition-1] # ) #Ghost_final = sisl.Geometry(Initial_Geom.xyz[FinalAtomPosition-1], # atoms= -1* Initial_Geom.atoms.Z[FinalAtomPosition-1] # ) Ghost_initial_Info = sisl.Atom(Initial_Geom.atoms.Z[InitialAtomPosition-1]) Ghost_initial = sisl.Geometry(Initial_Geom.xyz[InitialAtomPosition-1], atoms= sisl.Atom( -1* Ghost_initial_Info.Z , tag=Ghost_initial_Info.symbol+"_ghost")) Ghost_final_Info = sisl.Atom(Initial_Geom.atoms.Z[FinalAtomPosition-1]) Ghost_final = sisl.Geometry(Initial_Geom.xyz[ FinalAtomPosition-1 ], atoms=sisl.Atom( -1* Ghost_final_Info.Z,tag = Ghost_final_Info.symbol+"_ghost" )) trace_atom_initial = sisl.Geometry(Initial_Geom.xyz[InitialAtomPosition-1], atoms= Initial_Geom.atoms.Z[InitialAtomPosition-1] ) trace_atom_final = sisl.Geometry(Initial_Geom.xyz[FinalAtomPosition-1], atoms= Initial_Geom.atoms.Z[FinalAtomPosition-1] ) InitialASEXYZ = InitialASEXYZ.remove(InitialAtomPosition-1) FinalASEXYZ = FinalASEXYZ.remove(FinalAtomPosition-1) if (FinalAtomPosition-1) > (InitialAtomPosition-1): print ("Order : Final Atom Position > Initial Atom Position") InitialASEXYZ = InitialASEXYZ.remove(FinalAtomPosition-2) FinalASEXYZ = FinalASEXYZ.remove(InitialAtomPosition-1) if (FinalAtomPosition-1) < (InitialAtomPosition-1): print ("Order : Initial Atom Position > Final Atom Position") InitialASEXYZ = InitialASEXYZ.remove(FinalAtomPosition-1) FinalASEXYZ = FinalASEXYZ.remove(InitialAtomPosition-2) InitialASEXYZ = InitialASEXYZ.add(trace_atom_final) FinalASEXYZ = FinalASEXYZ.add(trace_atom_initial) if Ghost == True: info_sisl = {'initial' : InitialASEXYZ, 'final' : FinalASEXYZ, 'trace_atom_initial' : trace_atom_initial, 'trace_atom_final' : trace_atom_final, 'Ghost_initial' : Ghost_initial, 'Ghost_final' : Ghost_final, } else: info_sisl = {'initial' : InitialASEXYZ, 'final' : FinalASEXYZ, 'trace_atom_initial' : trace_atom_initial, 'trace_atom_final' : trace_atom_final, } return info_sisl
import plotly.graph_objs as go import numpy as np import sisl r = np.linspace(0, 3.5, 50) f = np.exp(-r) orb = sisl.AtomicOrbital('2pzZ', (r, f)) geom = sisl.geom.graphene(orthogonal=True, atoms=sisl.Atom(6, orb)) geom = geom.move([0, 0, 5]) H = sisl.Hamiltonian(geom) H.construct([(0.1, 1.44), (0, -2.7)], ) def test_eigenstate_wf(): plot = H.eigenstate()[0].plot.wavefunction(geometry=H.geometry) assert len(plot.data) > 0 assert isinstance(plot.data[0], go.Isosurface) def test_hamiltonian_wf(): # Check if it works for 3D plots plot = H.plot.wavefunction(2) assert isinstance(plot.data[0], go.Isosurface) # Check that setting plot geom to True adds data traces plot.update_settings(plot_geom=False)
def test_1_graphene_all_content(sisl_files): """ This tests manifolds itself as: sisl.geom.graphene(orthogonal=True).tile(3, 0).tile(5, 1) All output is enabled: ### FDF ### # Transmission related quantities TBT.T.All T TBT.T.Out T TBT.T.Eig 2 # Density of states TBT.DOS.Elecs T TBT.DOS.Gf T TBT.DOS.A T TBT.DOS.A.All T # Orbital currents and Crystal-Orbital investigations. TBT.Symmetry.TimeReversal F TBT.Current.Orb T TBT.COOP.Gf T TBT.COOP.A T TBT.COHP.Gf T TBT.COHP.A T TBT.k [100 1 1] ### FDF ### """ tbt = sisl.get_sile(sisl_files(_dir, '1_graphene_all.TBT.nc')) assert tbt.E.min() > -2. assert tbt.E.max() < 2. # We have 400 energy-points ne = len(tbt.E) assert ne == 400 assert tbt.ne == ne # We have 100 k-points nk = len(tbt.kpt) assert nk == 100 assert tbt.nk == nk assert tbt.wk.sum() == pytest.approx(1.) for i in range(ne): assert tbt.Eindex(i) == i assert tbt.Eindex(tbt.E[i]) == i # Check raises with pytest.warns(sisl.SislWarning): tbt.Eindex(tbt.E.min() - 1.) with pytest.warns(sisl.SislInfo): tbt.Eindex(tbt.E.min() - 2e-3) with pytest.warns(sisl.SislWarning): tbt.kindex([0, 0, 0.5]) # Can't hit it #with pytest.warns(sisl.SislInfo): # tbt.kindex([0.0106, 0, 0]) for i in range(nk): assert tbt.kindex(i) == i assert tbt.kindex(tbt.kpt[i]) == i # Get geometry geom = tbt.geometry geom_c1 = tbt.read_geometry(atom=sisl.Atoms(sisl.Atom[6], geom.na)) geom_c2 = tbt.read_geometry(atom=sisl.Atoms(sisl.Atom(6, orbs=2), geom.na)) assert geom_c1 == geom_c2 # Check read is the same as the direct query assert tbt.na == geom.na assert tbt.no == geom.no assert tbt.no == geom.na assert tbt.na == 3 * 5 * 4 assert np.allclose(tbt.cell, geom.cell) # Check device atoms (1-orbital system) assert tbt.na_d == tbt.no_d assert tbt.na_d == 36 # 3 * 5 * 4 (and device is without electrodes, so 3 * 3 * 4) assert len( tbt.pivot() ) == 3 * 3 * 4 # 3 * 5 * 4 (and device is without electrodes, so 3 * 3 * 4) assert len(tbt.pivot(True)) == len(tbt.pivot()) assert np.all(tbt.pivot(True, True) == np.arange(tbt.no_d)) assert np.all(tbt.pivot(sort=True) == np.sort(tbt.pivot())) # Just check they are there assert tbt.n_btd() == len(tbt.btd()) # Check electrodes assert len(tbt.elecs) == 2 elecs = tbt.elecs[:] assert elecs == ['Left', 'Right'] for i, elec in enumerate(elecs): assert tbt._elec(i) == elec # Check the chemical potentials for elec in elecs: assert tbt.n_btd(elec) == len(tbt.btd(elec)) assert tbt.chemical_potential(elec) == pytest.approx(0.) assert tbt.electronic_temperature(elec) == pytest.approx(300., abs=1) assert tbt.eta(elec) == pytest.approx(1e-4, abs=1e-6) # Check electrode relevant stuff left = elecs[0] right = elecs[1] # Assert we have transmission symmetry assert np.allclose(tbt.transmission(left, right), tbt.transmission(right, left)) assert np.allclose(tbt.transmission_eig(left, right), tbt.transmission_eig(right, left)) # Check that the total transmission is larger than the sum of transmission eigenvalues assert np.all( tbt.transmission(left, right) + 1e-7 >= tbt.transmission_eig(left, right).sum(-1)) assert np.all( tbt.transmission(right, left) + 1e-7 >= tbt.transmission_eig(right, left).sum(-1)) # Check that we can't retrieve from same to same electrode with pytest.raises(ValueError): tbt.transmission(left, left) with pytest.raises(ValueError): tbt.transmission_eig(left, left) assert np.allclose(tbt.transmission(left, right, kavg=False), tbt.transmission(right, left, kavg=False)) # Also check for each k for ik in range(nk): assert np.allclose(tbt.transmission(left, right, ik), tbt.transmission(right, left, ik)) assert np.allclose(tbt.transmission_eig(left, right, ik), tbt.transmission_eig(right, left, ik)) assert np.all( tbt.transmission(left, right, ik) + 1e-7 >= tbt.transmission_eig(left, right, ik).sum(-1)) assert np.all( tbt.transmission(right, left, ik) + 1e-7 >= tbt.transmission_eig(right, left, ik).sum(-1)) assert np.allclose(tbt.DOS(kavg=ik), tbt.ADOS(left, kavg=ik) + tbt.ADOS(right, kavg=ik)) assert np.allclose( tbt.DOS(E=0.195, kavg=ik), tbt.ADOS(left, E=0.195, kavg=ik) + tbt.ADOS(right, E=0.195, kavg=ik)) kavg = list(range(10)) assert np.allclose(tbt.DOS(kavg=kavg), tbt.ADOS(left, kavg=kavg) + tbt.ADOS(right, kavg=kavg)) assert np.allclose( tbt.DOS(E=0.195, kavg=kavg), tbt.ADOS(left, E=0.195, kavg=kavg) + tbt.ADOS(right, E=0.195, kavg=kavg)) # Check that norm returns correct values assert tbt.norm() == 1 assert tbt.norm(norm='all') == tbt.no_d assert tbt.norm(norm='atom') == tbt.norm(norm='orbital') # Check atom is equivalent to orbital for norm in ['atom', 'orbital']: assert tbt.norm(0, norm=norm) == 0. assert tbt.norm(3 * 4, norm=norm) == 1 assert tbt.norm(range(3 * 4, 3 * 5), norm=norm) == 3 # Assert sum(ADOS) == DOS assert np.allclose(tbt.DOS(), tbt.ADOS(left) + tbt.ADOS(right)) assert np.allclose(tbt.DOS(sum=False), tbt.ADOS(left, sum=False) + tbt.ADOS(right, sum=False)) # Now check orbital resolved DOS assert np.allclose(tbt.DOS(sum=False), tbt.ADOS(left, sum=False) + tbt.ADOS(right, sum=False)) # Current must be 0 when the chemical potentials are equal assert tbt.current(left, right) == pytest.approx(0.) assert tbt.current(right, left) == pytest.approx(0.) high_low = tbt.current_parameter(left, 0.5, 0.0025, right, -0.5, 0.0025) low_high = tbt.current_parameter(left, -0.5, 0.0025, right, 0.5, 0.0025) assert high_low > 0. assert low_high < 0. assert -high_low == pytest.approx(low_high) with pytest.warns(sisl.SislWarning): tbt.current_parameter(left, -10., 0.0025, right, 10., 0.0025) # Since this is a perfect system there should be *no* QM shot-noise # Also, the shot-noise is related to the applied bias, so NO shot-noise assert np.allclose(tbt.shot_noise(left, right), 0.) assert np.allclose(tbt.shot_noise(right, left), 0.) # Since the data-file does not contain all T-eigs (only the first two) # we can't correctly calculate the fano factors assert np.all(tbt.fano(left, right) > 0.) assert np.all(tbt.fano(right, left) > 0.) # Check specific DOS queries DOS = tbt.DOS ADOS = tbt.ADOS atom = range(8, 40) # some in device, some not in device for o in ['atom', 'orbital']: opt = {o: atom} for E in [None, 2, 4]: assert np.allclose(DOS(E), ADOS(left, E) + ADOS(right, E)) assert np.allclose(DOS(E, **opt), ADOS(left, E, **opt) + ADOS(right, E, **opt)) opt['sum'] = False for E in [None, 2, 4]: assert np.allclose(DOS(E), ADOS(left, E) + ADOS(right, E)) assert np.allclose(DOS(E, **opt), ADOS(left, E, **opt) + ADOS(right, E, **opt)) opt['sum'] = True opt['norm'] = o for E in [None, 2, 4]: assert np.allclose(DOS(E), ADOS(left, E) + ADOS(right, E)) assert np.allclose(DOS(E, **opt), ADOS(left, E, **opt) + ADOS(right, E, **opt)) opt['sum'] = False for E in [None, 2, 4]: assert np.allclose(DOS(E), ADOS(left, E) + ADOS(right, E)) assert np.allclose(DOS(E, **opt), ADOS(left, E, **opt) + ADOS(right, E, **opt)) # Check orbital currents E = 201 # Sum of orbital current should be 0 (in == out) orb_left = tbt.orbital_current(left, E) orb_right = tbt.orbital_current(right, E) assert orb_left.sum() == pytest.approx(0., abs=1e-7) assert orb_right.sum() == pytest.approx(0., abs=1e-7) d1 = np.arange(12, 24).reshape(-1, 1) d2 = np.arange(24, 36).reshape(-1, 1) assert orb_left[d1, d2.T].sum() == pytest.approx( tbt.transmission(left, right)[E]) assert orb_left[d1, d2.T].sum() == pytest.approx(-orb_left[d2, d1.T].sum()) assert orb_right[d2, d1.T].sum() == pytest.approx( tbt.transmission(right, left)[E]) assert orb_right[d2, d1.T].sum() == pytest.approx(-orb_right[d1, d2.T].sum()) orb_left.sort_indices() atom_left = tbt.bond_current(left, E, only='all') atom_left.sort_indices() assert np.allclose(orb_left.data, atom_left.data) assert np.allclose( orb_left.data, tbt.bond_current_from_orbital(orb_left, only='all').data) orb_right.sort_indices() atom_right = tbt.bond_current(right, E, only='all') atom_right.sort_indices() assert np.allclose(orb_right.data, atom_right.data) assert np.allclose( orb_right.data, tbt.bond_current_from_orbital(orb_right, only='all').data) # Calculate the atom current # For 1-orbital systems the activity and non-activity are equivalent assert np.allclose(tbt.atom_current(left, E), tbt.atom_current(left, E, activity=False)) tbt.vector_current(left, E) assert np.allclose( tbt.vector_current_from_bond(atom_left) / 2, tbt.vector_current(left, E, only='all')) # Check COOP curves coop = tbt.orbital_COOP(E) coop_l = tbt.orbital_ACOOP(left, E) coop_r = tbt.orbital_ACOOP(right, E) assert np.allclose(coop.data, (coop_l + coop_r).data) coop = tbt.orbital_COOP(E, isc=[0, 0, 0]) coop_l = tbt.orbital_ACOOP(left, E, isc=[0, 0, 0]) coop_r = tbt.orbital_ACOOP(right, E, isc=[0, 0, 0]) assert np.allclose(coop.data, (coop_l + coop_r).data) coop = tbt.atom_COOP(E) coop_l = tbt.atom_ACOOP(left, E) coop_r = tbt.atom_ACOOP(right, E) assert np.allclose(coop.data, (coop_l + coop_r).data) coop = tbt.atom_COOP(E, isc=[0, 0, 0]) coop_l = tbt.atom_ACOOP(left, E, isc=[0, 0, 0]) coop_r = tbt.atom_ACOOP(right, E, isc=[0, 0, 0]) assert np.allclose(coop.data, (coop_l + coop_r).data) # Check COHP curves coop = tbt.orbital_COHP(E) coop_l = tbt.orbital_ACOHP(left, E) coop_r = tbt.orbital_ACOHP(right, E) assert np.allclose(coop.data, (coop_l + coop_r).data) coop = tbt.atom_COHP(E) coop_l = tbt.atom_ACOHP(left, E) coop_r = tbt.atom_ACOHP(right, E) assert np.allclose(coop.data, (coop_l + coop_r).data) # Simply print out information tbt.info() for elec in elecs: tbt.info(elec)
def si_pdos_kgrid_geom(with_orbs=True): if with_orbs: return sisl.geom.diamond(5.43, sisl.Atom('Si', R=np.arange(13) + 1)) return sisl.geom.diamond(5.43, sisl.Atom('Si'))
__slots__ = ('U', ) def __init__(self, *args, U=0., **kwargs): super().__init__(*args, **kwargs) self.U = U def copy(self, *args, **kwargs): copy = super().copy(*args, **kwargs) copy.U = self.U return copy # Multi-orbital tight-binding Hamiltonian, set U in the geometry pz = OrbitalU(1.42, q0=1.0, U=3.) s = OrbitalU(1.42, q0=0, U=0.) C = sisl.Atom(6, orbitals=[pz, s]) g = geom.zgnr(W, atoms=C) # Add another atom to have heterogeneous number of orbitals per atoms C2 = sisl.Atom(6, orbitals=[pz]) G_C2 = sisl.Geometry(g.xyz[0], atoms=C2) g = g.replace(0, G_C2) # Identify index for atoms idx = g.a2o(range(len(g))) # Build U for each orbital in each atom #U = np.zeros(g.no) #U[idx] = 3. # Build TB Hamiltonian, zeroes for non-pz orbitals
#!/usr/bin/env python # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # This example creates the tight-binding Hamiltonian # for graphene with on-site energy 0, and hopping energy # -2.7 eV. import sisl bond = 1.42 # Construct the atom with the appropriate orbital range # Note the 0.01 which is for numerical accuracy. C = sisl.Atom(6, R=bond + 0.01) # Create graphene unit-cell gr = sisl.geom.graphene(bond, C) # Create the tight-binding Hamiltonian H = sisl.Hamiltonian(gr) R = [0.1 * bond, bond + 0.01] for ia in gr: idx_a = gr.close(ia, R) # On-site H[ia, idx_a[0]] = 0. # Nearest neighbour hopping H[ia, idx_a[1]] = -2.7 # Calculate eigenvalues at K-point print(H.eigh([2. / 3, 1. / 3, 0.]))
def from_dict(cls, dic): """ Return an `AtomBasis` from a dictionary Parameters ---------- dic : dict """ from sisl_toolbox.siesta.atom._atom import _shell_order element = dic["element"] tag = dic.get("tag") mass = dic.get("mass", None) # get default options for pseudo opts = NotNonePropertyDict() basis = dic.get("basis", {}) opts["ion_charge"] = parse_variable(basis.get("ion-charge")).value opts["type"] = basis.get("type") def get_radius(orbs, zeta): for orb in orbs: if orb.zeta == zeta: return orb.R raise ValueError("Could parse the negative R value") orbs = [] for nl in dic: if nl not in _shell_order: continue n, l = int(nl[0]), 'spdfg'.index(nl[1]) # Now we are sure we are dealing with valence shells basis = dic[nl].get("basis", {}) opt_nl = NotNonePropertyDict() orbs_nl = [] # Now read through the entries for key, entry in basis.items(): if key in ("charge-confinement", "charge-conf"): opt_nl["charge"] = [ parse_variable(entry.get("charge")).value, parse_variable(entry.get("yukawa"), unit='1/Ang').value, parse_variable(entry.get("width"), unit='Ang').value ] elif key in ("soft-confinement", "soft-conf"): opt_nl["soft"] = [ parse_variable(entry.get("V0"), unit='eV').value, parse_variable(entry.get("ri"), unit='Ang').value ] elif key in ("filter", ): opt_nl["filter"] = parse_variable(entry, unit='eV').value elif key in ("split-norm", "split"): opt_nl["split"] = parse_variable(entry).value elif key in ("polarization", "pol"): opt_nl["pol"] = parse_variable(entry).value elif key.startswith("zeta"): # cutoff of zeta zeta = int(key[4:]) R = parse_variable(entry, unit='Ang').value if R < 0: R *= -get_radius(orbs_nl, zeta - 1) orbs_nl.append( si.AtomicOrbital(n=n, l=l, m=0, zeta=zeta, R=R)) if len(orbs_nl) > 0: opts[(n, l)] = opt_nl orbs.extend(orbs_nl) atom = si.Atom(element, orbs, mass=mass, tag=tag) return cls(atom, opts)
def in2out_frame_PBCoff(TSHS, a_R1, eta_value, energies, TBT, HS_host, orb_idx=None, pos_dSE=None, area_R1=None, area_R2=None, area_for_buffer=None, TBTSE=None, useCAP=None, spin=0, tol=None, EfromTBT=True): """ TSHS: TSHS from perturbed DFT system a_R1: idx atoms in sub-region A of perturbed DFT system (e.g. frame) \Sigma will live on these atoms eta_value: imaginary part in Green's function energies: energy in eV for which \Sigma should be computed (closest E in TBT will be used ) TBT: *.TBT.nc (or *.TBT.SE.nc) from a TBtrans calc. where TBT.HS = TSHS HS_host: host (H, S) model (e.g. a large TB model of unperturbed system). Coordinates of atoms "a_R1" in TSHS will be mapped into this new model. Atomic order will be adjusted so that mapped atoms will be consecutive and at the end of the list orb_idx (=None): idx of orbital per atom to be extracted from TSHS, in case HS_host has a reduced basis size pos_dSE (=0): center of region where \Sigma atoms should be placed in HS_host area_R1 (=None): si.shape.Cuboid object used to select "a_R1" atoms in TSHS area_R2 (=None): internal si.shape.Cuboid object used to construct area_R1 in TSHS area_for_buffer (=None): external si.shape.Cuboid object used to used to construct area_R1 in TSHS TBTSE (=None): *TBT.SE.nc file of self-energy enclosed by the atoms "a_R1" in TSHS (e.g., tip) useCAP (=None): use 'left+right+top+bottom' to set complex absorbing potential in all in-plane directions Important output files: "HS_DEV.nc": HS file for TBtrans (to be used with "TBT.HS" flag) this Hamiltonian is identical to HS_host, but it has no PBC and \Sigma projected atoms are moved to the end of the atom list "SE_i.delta.nc": \Delta \Sigma file for TBtrans (to be used as "TBT.dSE" flag) it will contain \Sigma from k-averaged Green's function from TSHS, projected on the atoms "a_R1" equivalent atoms of HS_host "SE_i.TBTGF": Green's function file for usage as electrode in TBtrans (to be used with "GF" flag in the electrode block for \Sigma) it will contain S^{noPBC}*e - H^{noPBC} - \Sigma from TSHS k-averaged Green's function, projected on the atoms "a_R1" equivalent atoms of HS_host "HS_SE_i.nc": electrode HS file for usage of TBTGF as electrode in TBtrans (to be used with "HS" flag in the electrode block for \Sigma) NOTES: - works for 2D carbon systems (as of now) """ """ Let's first find the orbitals inside R1 and R2 """ # Indices of atoms in device region a_dev = TBT.a_dev # a_dev from *TBT.nc and *TBT.SE.nc is not sorted correctly in older versions of tbtrans!!! # a_dev = np.sort(TBT.a_dev) # Indices of orbitals in device region o_dev = TSHS.a2o(a_dev, all=True) # Check it's only carbon in R1 for ia in a_R1: if TSHS.atom[ia].Z != 6: print('\nERROR: please select C atoms in region R1.') print('Atoms {} are not carbon \n'.format( (TSHS.atoms.Z != 6).nonzero()[0])) exit(1) # Define R1 region (indices are w.r.t. device device region! ) if orb_idx is not None: # Selected 'orb_idx' orbitals inside R1 region print( 'WARNING: you are selecting only orbital index \'{}\' in R1 region' .format(orb_idx)) o_R1 = TSHS.a2o( a_R1 ) + orb_idx # IMPORTANT: these are pz indices in the full L+D+R geometry else: # If no particular orbital is specified, then consider ALL orbitals inside R1 region o_R1 = TSHS.a2o( a_R1, all=True ) # With this we will basically calculate a Sigma DFT-->DFT instead of DFT-->TB # Now we find their indeces with respect to the device region o_R1 = np.in1d(o_dev, o_R1).nonzero( )[0] # np.in1d returns true/false. Nonzero turns it into the actual indices # In region 2 we will consider ALL orbitals of ALL atoms vv = TSHS.geom.sub(a_dev) o_R2_tmp = area_R2.within_index(vv.xyz) o_R2 = vv.a2o(o_R2_tmp, all=True) # these are ALL orbitals indices in region 2 # Check v = TSHS.geom.copy() v.atom[v.o2a(o_dev, unique=True)] = si.Atom(8, R=[1.44]) v.write('o_dev.xyz') # Check vv = TSHS.geom.sub(a_dev) vv.atom[vv.o2a(o_R1, unique=True)] = si.Atom(8, R=[1.44]) vv.write('o_R1.xyz') # Check vv = TSHS.geom.sub(a_dev) vv.atom[vv.o2a(o_R2, unique=True)] = si.Atom(8, R=[1.44]) vv.write('o_R2.xyz') """ ### Map a_R1 into host geometry (which includes electrodes!) We will now rearrange the atoms in the host geometry putting the mapped ones at the end of the coordinates list """ # sometimes this is useful to fix the mapping if area_for_buffer is None: area_for_buffer = area_R2.copy() print( "WARNING: You didn't provide 'area_for_buffer'. \n We are setting it to 'area_R2'. Please check that it is completely correct by comparing 'a_dSE_host.xyz' and 'buffer.xyz'" ) a_dSE_host, new_HS_host, a_buffer_host = map_xyz( A=TSHS, B=HS_host, center_B=pos_dSE, area_R_A=area_R1, a_R_A=a_R1, area_for_buffer=area_for_buffer, tol=tol) # Write dSE xyz v = new_HS_host.geom.copy() v.atom[a_dSE_host] = si.Atom(8, R=[1.44]) v.write('a_dSE_host.xyz') # Write buffer atoms fdf block v = new_HS_host.geom.copy() v.atom[a_buffer_host] = si.Atom(8, R=[1.44]) v.write('buffer.xyz') with open('block_buffer.fdf', 'w') as fb: fb.write("%block TBT.Atoms.Buffer\n") fb.write(list2range_TBTblock(a_buffer_host)) fb.write("\n%endblock\n") # Write final host large model, ready to be served to tbtrans new_HS_host.geom.write('HS_DEV.xyz') new_HS_host.geom.write('HS_DEV.fdf') new_HS_host.write('HS_DEV.nc') # Set CAP (optional) if useCAP: # Create dH | CAP dH_CAP = CAP(new_HS_host.geom, useCAP, dz_CAP=50, write_xyz=True) dH_CAP_sile = si.get_sile('CAP.delta.nc', 'w') dH_CAP_sile.write_delta(dH_CAP) # TBT.dH ############################# """ # Now we calculate and store the self-energy """ ### Initialize dSE (flag for tbtrans is --> TBT.dSE) print('Initializing dSE file...') o_dSE_host = new_HS_host.a2o(a_dSE_host, all=True).reshape( -1, 1) # this has to be wrt L+D+R host geometry dSE = si.get_sile('SE_i.delta.nc', 'w') ### Initialize TBTGF (flag for tbtrans is --> GF inside an electrode block) # For this we need a complex energy contour + the sub H, S and geom of R1 in the new_HS_host (large TB) # Energy grid if EfromTBT: Eindices = [TBT.Eindex(en) for en in energies] E = TBT.E[Eindices] + 1j * eta_value else: print( 'WARNING: energies will not be taken from TBT. Make sure you know what you are doing.' ) E = np.asarray(energies) + 1j * eta_value tbl = si.io.table.tableSile('contour.IN', 'w') tbl.write_data(E.real, np.zeros(len(E)), np.ones(len(E)), fmt='.8f') # Remove periodic boundary conditions from TSHS!!! TSHS_n = TSHS.copy() print('Removing periodic boundary conditions') TSHS_n.set_nsc( [1] * 3 ) # this is how you do it in sisl. Super easy. Removes all phase factors # Now we extract submatrices of this, pruning into o_R1 print('Initializing TBTGF files...') if TSHS_n.spin.is_polarized: H_tbtgf = TSHS_n.Hk(dtype=np.float64, spin=spin) else: H_tbtgf = TSHS_n.Hk(dtype=np.float64) S_tbtgf = TSHS_n.Sk(dtype=np.float64) print(' Hk and Sk: DONE') # Prune to dev region (again, this is because o_R1 is w.r.t. device) H_tbtgf_d = pruneMat(H_tbtgf, o_dev) S_tbtgf_d = pruneMat(S_tbtgf, o_dev) # Prune now these to o_R1 H_tbtgf_R1 = pruneMat(H_tbtgf_d, o_R1) S_tbtgf_R1 = pruneMat(S_tbtgf_d, o_R1) # Finally we need the geometry of R1 in new_HS_host geom_R1 = new_HS_host.geom.sub(a_dSE_host) # It is vital that you also write an electrode Hamiltonian Semi = si.Hamiltonian.fromsp(geom_R1, H_tbtgf_R1, S_tbtgf_R1) Semi.write('HS_SE_i.nc' ) # this is used as HS flag inside the TBTGF electrode block # We also need a formal Brillouin zone. # "Formal" because we will always use a Gamma-only TBTGF # (there is no periodicity once we plug DFT into TB!) BZ = si.BrillouinZone(TSHS_n) BZ._k = np.array([[0., 0., 0.]]) BZ._w = np.array([1.0]) # Now finally we initialize a TBTGF file. We # We will fill it further below with the matrix for the DFT-TB self-energy GF = si.io.TBTGFSileTBtrans('SE_i.TBTGF') GF.write_header( BZ, E, obj=Semi ) # Semi HAS to be a Hamiltonian object, E has to be complex (WITH eta) ############### # If there is a self energy enclosed by the frame (e.g. a semi-infinite tip), # we will need to add it later to H_R2 and S_R2 to generate G_R2 # For that we will need to know the indices of the orbitals of the device region (in_device=True) # on which the self-energy has been down-folded during the tbtrans BTD process (see tbtrans manual). # - When read with BTTSE.pivot, they will be sorted as after the BTD process # we can sort them back to the original indices by using sort=True if TBTSE: pv = TBTSE.pivot('tip', in_device=True, sort=True).reshape(-1, 1) pv_R2 = np.in1d(o_R2, pv.reshape(-1, )).nonzero()[0].reshape(-1, 1) """ Now we loop over requested energies and create the DFT-TB self-energy matrices at those energies. FINALLY some physics ;-) """ print('Computing and storing Sigma in TBTGF and dSE format...') for i, (ispin, HS4GF, _, e) in enumerate( GF ): # sisl specific. THe _ would be k. But we just have Gamma, so who cares print('Doing E # {} of {} ({} eV)'.format(i + 1, len(E), e.real)) print('Doing E # {} of {} ({} eV)'.format(i + 1, len(E), e.real), file=open('log', 'a+')) # Also log while running """ Calculate G_R2 """ # Read H and S from full TSHS (L+D+R) - no self-energies here! if TSHS_n.spin.is_polarized: Hfullk = TSHS_n.Hk(format='array', spin=spin) else: Hfullk = TSHS_n.Hk(format='array') Sfullk = TSHS_n.Sk(format='array') # Prune H, S to device region H_d = pruneMat(Hfullk, o_dev) S_d = pruneMat(Sfullk, o_dev) # Prune H, S matrices from device region to region 2 H_R2 = pruneMat(H_d, o_R2) S_R2 = pruneMat(S_d, o_R2) # Build inverse of G_R2 (no self-energies such as tip yet!) invG_R2 = S_R2 * e - H_R2 # if there's a self energy enclosed by the frame if TBTSE: # Read in the correct format SE_ext = TBTSE.self_energy('tip', E=e.real, k=[0., 0., 0.], sort=True) # Subtract from invG_R2 at the correct rows and columns (given by pv_R2) invG_R2[pv_R2, pv_R2.T] -= SE_ext # Now invert G_R2 = np.linalg.inv(invG_R2) """ Extract V_21 elements from H_d """ # Coupling matrix from R1 to R2 (len(o_R2) x len(o_R1)) V_21 = couplingMat(H_d, o_R2, o_R1) # CHECK: V_21 OR V_21 - z*S_21 S_21 = couplingMat(S_d, o_R2, o_R1) """ Compute the final self-energy """ # Self-energy is projected (lives) in R1, connecting R1 to R2 (len(o_R1) x len(o_R1)) SE_R1 = np.dot(np.dot(dagger(V_21), G_R2), V_21) #SE_R1 = np.dot(np.dot(dagger(V_21-e*S_21), G_R2), V_21-e*S_21) """ Now that we have it, we save it! """ # Write Sigma as a dSE file Sigma_in_HS_host = sp.sparse.csr_matrix( (len(new_HS_host), len(new_HS_host)), dtype=np.complex128) Sigma_in_HS_host[o_dSE_host, o_dSE_host.T] = SE_R1 delta_Sigma = si.Hamiltonian.fromsp(new_HS_host.geom, Sigma_in_HS_host) dSE.write_delta(delta_Sigma, E=e.real) # Write Sigma as TBTGF # tbtrans wants you to write the quantity S_R1*e - H_R1 - SE_R1 # So, prune H, S matrices from device region to region 1 H_R1 = pruneMat(H_d, o_R1) S_R1 = pruneMat(S_d, o_R1) if HS4GF: GF.write_hamiltonian(H_R1, S_R1) GF.write_self_energy(S_R1 * e - H_R1 - SE_R1)