def __init__(self, spgeom, infinite, eta=1e-6, bloch=None): """ Create a `SelfEnergy` object from any `SparseGeometry` """ self.eta = eta if bloch is None: self.bloch = _a.onesi([3]) else: self.bloch = _a.arrayi(bloch) # Determine whether we are in plus/minus direction if infinite.startswith('+'): self.semi_inf_dir = 1 elif infinite.startswith('-'): self.semi_inf_dir = -1 else: raise ValueError(self.__class__.__name__ + ": infinite keyword does not start with `+` or `-`.") # Determine the direction INF = infinite.upper() if INF.endswith('A'): self.semi_inf = 0 elif INF.endswith('B'): self.semi_inf = 1 elif INF.endswith('C'): self.semi_inf = 2 # Check that the Hamiltonian does have a non-zero V along the semi-infinite direction if spgeom.geometry.sc.nsc[self.semi_inf] == 1: warn('Creating a semi-infinite self-energy with no couplings along the semi-infinite direction') # Finalize the setup by calling the class specific routine self._setup(spgeom)
def pivot(self, elec=None, in_device=False, sort=False): """ Return the pivoting indices for a specific electrode Parameters ---------- elec : str or int the corresponding electrode to return the self-energy from in_device : bool, optional If ``True`` the pivoting table will be translated to the device region orbitals sort : bool, optional Whether the returned indices are sorted. Mostly useful if the self-energies are returned sorted as well. Examples -------- >>> se = tbtsencSileTBtrans(...) # doctest: +SKIP >>> se.pivot() # doctest: +SKIP [3, 4, 6, 5, 2] >>> se.pivot(sort=True) # doctest: +SKIP [2, 3, 4, 5, 6] >>> se.pivot(0) # doctest: +SKIP [2, 3] >>> se.pivot(0, in_device=True) # doctest: +SKIP [4, 0] >>> se.pivot(0, in_device=True, sort=True) # doctest: +SKIP [0, 1] >>> se.pivot(0, sort=True) # doctest: +SKIP [2, 3] """ if elec is None: if in_device and sort: return _a.arangei(self.no_d) pvt = self._value('pivot') - 1 if in_device: # Count number of elements that we need to subtract from each orbital subn = _a.onesi(self.no) subn[pvt] = 0 pvt -= _a.cumsumi(subn)[pvt] elif sort: pvt = np.sort(pvt) return pvt if in_device: pvt = self._value('pivot') - 1 if sort: pvt = np.sort(pvt) # Get electrode pivoting elements se_pvt = self._value('pivot', tree=self._elec(elec)) - 1 if sort: # Sort pivoting indices # Since we know that pvt is also sorted, then # the resulting in_device would also return sorted # indices se_pvt = np.sort(se_pvt) if in_device: # translate to the device indices se_pvt = in1d(pvt, se_pvt, assume_unique=True).nonzero()[0] return se_pvt
def write_header(self, bz, E, mu=0., obj=None): """ Write to the binary file the header of the file Parameters ---------- bz : BrillouinZone contains the k-points, the weights and possibly the parent Hamiltonian (if `obj` is None)s E : array_like of cmplx or float the energy points. If `obj` is an instance of `SelfEnergy` where an associated ``eta`` is defined then `E` may be float, otherwise it *has* to be a complex array. mu : float, optional chemical potential in the file obj : ..., optional an object that contains the Hamiltonian definitions, defaults to ``bz.parent`` """ if obj is None: obj = bz.parent nspin = len(obj.spin) cell = obj.geometry.sc.cell * Ang2Bohr na_u = obj.geometry.na no_u = obj.geometry.no xa = obj.geometry.xyz * Ang2Bohr # The lasto in siesta requires lasto(0) == 0 # and secondly, the Python index to fortran # index makes firsto behave like fortran lasto lasto = obj.geometry.firsto bloch = _a.onesi(3) mu = mu * eV2Ry NE = len(E) if E.dtype not in [np.complex64, np.complex128]: E = E + 1j * obj.eta Nk = len(bz) k = bz.k w = bz.weight sizes = { 'na_used': na_u, 'nkpt': Nk, 'ne': NE, } self._nspin = nspin self._E = E * eV2Ry self._k = np.copy(k) if self._nspin > 2: self._no_u = no_u * 2 else: self._no_u = no_u # Ensure it is open (in write mode) self._close_gf() self._open_gf('w') # Now write to it... _siesta.write_gf_header(self._iu, nspin, cell.T, na_u, no_u, no_u, xa.T, lasto, bloch, 0, mu, k.T, w, self._E, **sizes) _bin_check(self, 'write_header', 'could not write header information.')
def __init__(self, spgeom, infinite, eta=1e-6, bloch=None): """ Create a `SelfEnergy` object from any `SparseGeometry` This enables the calculation of the self-energy for a semi-infinite chain. Parameters ---------- spgeom : SparseGeometry any sparse geometry matrix which may return matrices infinite : str axis specification for the semi-infinite direction (`+A`/`-A`/`+B`/`-B`/`+C`/`-C`) eta : float, optional the default imaginary part of the self-energy calculation bloch : array_like, optional Bloch-expansion for each of the lattice vectors (`1` for no expansion) The resulting self-energy will have dimension equal to `len(obj) * np.product(bloch)`. """ self.eta = eta if bloch is None: self.bloch = _a.onesi([3]) else: self.bloch = _a.arrayi(bloch) # Determine whether we are in plus/minus direction if infinite.startswith('+'): self.semi_inf_dir = 1 elif infinite.startswith('-'): self.semi_inf_dir = -1 else: raise ValueError( self.__class__.__name__ + ": infinite keyword does not start with `+` or `-`.") # Determine the direction INF = infinite.upper() if INF.endswith('A'): self.semi_inf = 0 elif INF.endswith('B'): self.semi_inf = 1 elif INF.endswith('C'): self.semi_inf = 2 # Check that the Hamiltonian does have a non-zero V along the semi-infinite direction if spgeom.geometry.sc.nsc[self.semi_inf] == 1: warn( 'Creating a semi-infinite self-energy with no couplings along the semi-infinite direction' ) # Finalize the setup by calling the class specific routine self._setup(spgeom)
def write_header(self, E, bz, obj, mu=0.): """ Write to the binary file the header of the file Parameters ---------- E : array_like of cmplx or float the energy points. If `obj` is an instance of `SelfEnergy` where an associated ``eta`` is defined then `E` may be float, otherwise it *has* to be a complex array. bz : BrillouinZone contains the k-points and their weights obj : ... an object that contains the Hamiltonian definitions """ nspin = len(obj.spin) cell = obj.geom.sc.cell * Ang2Bohr na_u = obj.geom.na no_u = obj.geom.no xa = obj.geom.xyz * Ang2Bohr # The lasto in siesta requires lasto(0) == 0 # and secondly, the Python index to fortran # index makes firsto behave like fortran lasto lasto = obj.geom.firsto bloch = _a.onesi(3) mu = mu * eV2Ry NE = len(E) if E.dtype not in [np.complex64, np.complex128]: E = E + 1j * obj.eta Nk = len(bz) k = bz.k w = bz.weight sizes = { 'na_used': na_u, 'nkpt': Nk, 'ne': NE, } self._E = np.copy(E) * eV2Ry self._k = np.copy(k) # Ensure it is open self._close_gf() self._open_gf() # Now write to it... _siesta.write_gf_header(self._iu, nspin, cell.T, na_u, no_u, no_u, xa.T, lasto, bloch, 0, mu, k.T, w, self._E, **sizes)
def read_supercell_nsc(self, *args, **kwargs): """ Read supercell size using any method available Raises ------ SislWarning if none of the files can be read """ order = kwargs.pop('order', ['nc', 'ORB_INDX']) for f in order: v = getattr(self, '_r_supercell_nsc_{}'.format(f.lower()))(*args, **kwargs) if v is not None: return v warn( 'number of supercells could not be read from output files. Assuming molecule cell ' '(no supercell connections)') return _a.onesi(3)
def pivot(self, in_device=False, sort=False): """ Pivoting orbitals for the full system Parameters ---------- in_device : bool, optional whether the pivoting elements are with respect to the device region sort : bool, optional whether the pivoting elements are sorted """ if in_device and sort: return _a.arangei(self.no_d) pvt = self._value('pivot') - 1 if in_device: subn = _a.onesi(self.no) subn[pvt] = 0 pvt -= _a.cumsumi(subn)[pvt] elif sort: pvt = np.sort(pvt) return pvt
def __init__(self, cell, nsc=None, origo=None): if nsc is None: nsc = [1, 1, 1] # If the length of cell is 6 it must be cell-parameters, not # actual cell coordinates self.cell = self.tocell(cell) if origo is None: self._origo = _a.zerosd(3) else: self._origo = _a.arrayd(origo) if self._origo.size != 3: raise ValueError("Origo *must* be 3 numbers.") # Set the volume self._update_vol() self.nsc = _a.onesi(3) # Set the super-cell self.set_nsc(nsc=nsc)
def pivot(self, elec=None, in_device=False, sort=False): """ Return the pivoting indices for a specific electrode (in the device region) or the device Parameters ---------- elec : str or int the corresponding electrode to return the pivoting indices from in_device : bool, optional If ``True`` the pivoting table will be translated to the device region orbitals. If `sort` is also true, this would correspond to the orbitals directly translated to the geometry ``self.geometry.sub(self.a_dev)``. sort : bool, optional Whether the returned indices are sorted. Mostly useful if you want to handle the device in a non-pivoted order. Examples -------- >>> se = tbtncSileTBtrans(...) >>> se.pivot() [3, 4, 6, 5, 2] >>> se.pivot(sort=True) [2, 3, 4, 5, 6] >>> se.pivot(0) [2, 3] >>> se.pivot(0, in_device=True) [4, 0] >>> se.pivot(0, in_device=True, sort=True) [0, 1] >>> se.pivot(0, sort=True) [2, 3] See Also -------- pivot_down : for the pivot table for electrodes down-folding regions """ if elec is None: if in_device and sort: return _a.arangei(self.no_d) pvt = self._value('pivot') - 1 if in_device: # Count number of elements that we need to subtract from each orbital subn = _a.onesi(self.no) subn[pvt] = 0 pvt -= _a.cumsumi(subn)[pvt] elif sort: pvt = npsort(pvt) return pvt # Get electrode pivoting elements se_pvt = self._value('pivot', tree=self._elec(elec)) - 1 if sort: # Sort pivoting indices # Since we know that pvt is also sorted, then # the resulting in_device would also return sorted # indices se_pvt = npsort(se_pvt) if in_device: pvt = self._value('pivot') - 1 if sort: pvt = npsort(pvt) # translate to the device indices se_pvt = indices(pvt, se_pvt, 0) return se_pvt