@@ -531,6 +531,57 @@ def _mat_to_hr(R, mat):
531531 )
532532 return lines
533533
534+ @staticmethod
535+ def _read_tb (iterator , ignore_orbital_order = False ):
536+ """Reads the content of a seedname_tb.dat file"""
537+ next (iterator ) # skip comment line
538+
539+ lattice = np .zeros ((3 , 3 ))
540+ for i in range (3 ):
541+ lattice [i , :] = np .fromstring (next (iterator ), sep = ' ' )
542+
543+ num_wann = int (next (iterator ))
544+
545+ nrpts = int (next (iterator ))
546+
547+ # degeneracy of Wigner-Seitz grid points, 15 entries per line
548+ deg_pts = []
549+ # order in zip important because else the next data element is consumed
550+ for _ , line in zip (range (int (np .ceil (nrpts / 15 ))), iterator ):
551+ deg_pts .extend (int (x ) for x in line .split ())
552+ assert len (deg_pts ) == nrpts
553+
554+ # <0n|H|Rm>
555+ hop_list = []
556+ for ir in range (nrpts ):
557+ next (iterator ) # skip empty
558+ r_vec = [int (_ ) for _ in next (iterator ).strip ().split ()]
559+ for j in range (num_wann ):
560+ for i in range (num_wann ):
561+ line = next (iterator ).strip ().split ()
562+ iw , jw = [int (_ ) for _ in line [:2 ]]
563+ if not ignore_orbital_order and (iw != i + 1 or jw != j + 1 ):
564+ raise ValueError (f"Inconsistent orbital numbers in line '{ line } '" )
565+ ham = (float (line [2 ]) + 1j * float (line [3 ])) / deg_pts [ir ]
566+ hop_list .append ([ham , i , j , r_vec ])
567+
568+ # <0n|r|Rm>
569+ r_list = []
570+ for ir in range (nrpts ):
571+ next (iterator ) # skip empty
572+ r_vec = [int (_ ) for _ in next (iterator ).strip ().split ()]
573+ for j in range (num_wann ):
574+ for i in range (num_wann ):
575+ line = next (iterator ).strip ().split ()
576+ iw , jw = [int (_ ) for _ in line [:2 ]]
577+ if not ignore_orbital_order and (iw != i + 1 or jw != j + 1 ):
578+ raise ValueError (f"Inconsistent orbital numbers in line '{ line } '" )
579+ r = np .array ([float (_ ) for _ in line [2 :]])
580+ r = r [::2 ] + 1j * r [1 ::2 ]
581+ r_list .append ([r , i , j , r_vec ])
582+
583+ return lattice , num_wann , nrpts , deg_pts , hop_list , r_list
584+
534585 @classmethod
535586 def from_wannier_folder (
536587 cls , folder : str = "." , prefix : str = "wannier" , ** kwargs
@@ -717,6 +768,98 @@ def remap_hoppings(hop_entries):
717768
718769 return cls .from_hop_list (size = num_wann , hop_list = hop_entries , ** kwargs )
719770
771+ @classmethod # noqa: MC0001
772+ def from_wannier_tb_file ( # pylint: disable=too-many-locals
773+ cls ,
774+ * ,
775+ tb_file : str ,
776+ wsvec_file : str ,
777+ ** kwargs ,
778+ ) -> Model :
779+ """
780+ Create a :class:`.Model` instance from Wannier90 output files.
781+
782+ Parameters
783+ ----------
784+ tb_file :
785+ Path of the ``*_tb.dat`` file. Together with the
786+ ``*_wsvec.dat`` file, this determines the hopping terms.
787+ wsvec_file :
788+ Path of the ``*_wsvec.dat`` file. This file determines the
789+ remapping of hopping terms when ``use_ws_distance`` is used
790+ in the Wannier90 calculation.
791+ kwargs :
792+ :class:`.Model` keyword arguments.
793+ """
794+
795+ if "uc" in kwargs :
796+ raise ValueError (
797+ "Ambiguous unit cell: It can be given either via 'uc' or the 'tb_file' keywords, but not both."
798+ )
799+ if "pos" in kwargs :
800+ raise ValueError (
801+ "Ambiguous orbital positions: The positions can be given either via the 'pos' or the 'xyz_file' keywords, but not both."
802+ )
803+
804+ with open (tb_file ) as f :
805+ lattice , num_wann , nrpts , deg_pts , hop_list , r_list = cls ._read_tb (f )
806+
807+ kwargs ["uc" ] = lattice
808+
809+ def get_centers (r_list : list ) -> list :
810+ centers = [None for _ in range (num_wann )]
811+ for r , i , j , r_vec in r_list :
812+ if r_vec != [0 , 0 , 0 ]:
813+ continue
814+ if i != j :
815+ continue
816+ r = np .array (r )
817+ if not np .allclose (np .abs (r .imag ), 0 ):
818+ raise ValueError (f'Center should be real: WF { i + 1 } , center = { r } ' )
819+ centers [i ] = r .real
820+ return centers
821+
822+ pos_cartesian : ty .Union [
823+ ty .List [npt .NDArray [np .float_ ]], npt .NDArray [np .float_ ]
824+ ] = get_centers (r_list )
825+
826+ kwargs ["pos" ] = la .solve (kwargs ["uc" ].T , np .array (pos_cartesian ).T ).T
827+
828+ # hop_entries = (hop for hop in hop_entries if abs(hop[0]) > h_cutoff)
829+ hop_entries = hop_list
830+
831+ with open (wsvec_file ) as f :
832+ wsvec_generator = cls ._async_parse (
833+ cls ._read_wsvec (f ), chunksize = num_wann
834+ )
835+
836+ def remap_hoppings (hop_entries ):
837+ for t , orbital_1 , orbital_2 , R in hop_entries :
838+ # Step _async_parse to where it accepts
839+ # a new key.
840+ # The _async_parse does not raise StopIteration
841+ next ( # pylint: disable=stop-iteration-return
842+ wsvec_generator
843+ )
844+ T_list = wsvec_generator .send (
845+ (orbital_1 , orbital_2 , tuple (R ))
846+ )
847+ N = len (T_list )
848+ for T in T_list :
849+ # not using numpy here increases performance
850+ yield (
851+ t / N ,
852+ orbital_1 ,
853+ orbital_2 ,
854+ tuple (r + t for r , t in zip (R , T )),
855+ )
856+
857+ hop_entries = remap_hoppings (hop_entries )
858+
859+ return cls .from_hop_list (
860+ size = num_wann , hop_list = hop_entries , ** kwargs
861+ )
862+
720863 @staticmethod
721864 def _async_parse (iterator , chunksize = 1 ):
722865 """
0 commit comments