!-*- mode: F90 -*-!
!------------------------------------------------------------!
! Copyright (C) 2026 Wannier Developer Group                 !
!                                                            !
! This library is free software; you can redistribute it     !
! and/or modify it under the terms of the GNU Lesser General !
! Public License as published by the Free Software           !
! Foundation; either version 2.1 of the License, or (at your !
! option) any later version.                                 !
!                                                            !
! This library is distributed in the hope that it will be    !
! useful,but WITHOUT ANY WARRANTY; without even the implied  !
! warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR    !
! PURPOSE.  See the GNU Lesser General Public License for    !
! more details.                                              !
!                                                            !
! You should have received a copy of the GNU Lesser General  !
! Public License along with this library; if not, see        !
! <https://www.gnu.org/licenses/>.                           !
!                                                            !
! The webpage of the Wannier90 code is                       !
! <https://www.wannier.org>.                                 !
!                                                            !
! The Wannier90 code is hosted on GitHub                     !
! <https://github.com/wannier-developers/wannier90>          !
!------------------------------------------------------------!

!-----------------------------------------------------------------------!
!  Based on                                                             !
!  < dosqc_1.0 >                                                        !
!  Density Of States and Quantum Conductance - Version 1.0              !
!  Marco Buongiorno Nardelli, January 2000.                             !
!                                                                       !
!  Reference:                                                           !
!  - M. Buongiorno Nardelli, "Electronic transport in extended systems: !
!  application to carbon nanotubes", Phys. Rev. B, vol. 60(11), 7828    !
!  (1999)                                                               !
!-----------------------------------------------------------------------!

!=======================================================================!
! Definition of parameters used in w90_transport                        !
!=======================================================================!
!                                                                       !
! transport_mode       = 'bulk' or 'lcr'                                !
! tran_win_min         = minimum E                                      !
! tran_win_max         = maximum E                                      !
! tran_energy_step     = delta E                                        !
! tran_num_bb          = # of WFs in a principal layer of a perfectly   !
!                        periodic bulk system                           !
! tran_num_ll          = # of WFs in a principal layer of a left lead   !
! tran_num_rr          = # of WFs in a principal layer of a right lead  !
! tran_num_cc          = # of WFs in a disordered conducter cell        !
! tran_num_lc          = # of WFs in a disordered conducter cell that   !
!                        are used to calculate interaction with a       !
!                        left lead                                      !
! tran_num_cr          = # of WFs in a disordered conducter cell that   !
!                        are used to calculate interaction with a       !
!                        right lead                                     !
! tran_num_bandc       = width of band-diagonal hC matrix               !
! tran_read_ht         = .true. => read H matrix from h*.dat files      !
! tran_write_ht        = .true. => write H matrix from h*.dat files     !
! tran_use_same_lead   = .true. => in L-C-R construction, left and      !
!                                  right lead are the same kind         !
! tran_num_cell_ll     = # of unit cells in a PL of left lead           !
! tran_num_cell_rr     = # of unit cells in a PL of right lead          !
!                        (equal to tran_num_cell_ll for now)            !
! tran_group_threshold = distance defining the grouping of WFs          !
!=======================================================================!

module w90_transport_mod

  !! Module to handle ballistic transport.
  !! Based on
  !!  < dosqc_1.0 >
  !!  Density Of States and Quantum Conductance - Version 1.0
  !!  Marco Buongiorno Nardelli, January 2000.
  !!  Reference:
  !!  - M. Buongiorno Nardelli, "Electronic transport in extended systems:
  !!  application to carbon nanotubes", Phys. Rev. B, vol. 60(11), 7828
  !!  (1999)

  use w90_constants, only: dp
  use w90_error
  use w90_comms

  implicit none

  private

  complex(kind=dp), parameter :: eta = (0.0_dp, 0.0005_dp) !! small complex number

  integer, parameter :: nterx = 50 !! nterx  = # of maximum iteration to calculate transfer matrix

  public :: tran_main

contains

  !================================================!
  subroutine tran_main(atom_data, dis_manifold, fermi_energy_list, ham_logical, kpt_latt, &
                       output_file, real_space_ham, transport, print_output, wannier_data, &
                       ws_region, w90_calculation, ham_k, ham_r, u_matrix, u_matrix_opt, eigval, &
                       real_lattice, wannier_centres_translated, irvec, mp_grid, ndegen, &
                       shift_vec, nrpts, num_bands, num_kpts, num_wann, rpt_origin, &
                       bands_plot_mode, have_disentangled, lsitesymmetry, seedname, stdout, &
                       timer, error, comm)
    !================================================!
    !
    !! Main transport subroutine
    !
    !================================================!

    use w90_io, only: io_stopwatch_start, io_stopwatch_stop
    use w90_error, only: w90_error_type, set_error_dealloc
    use w90_hamiltonian, only: hamiltonian_get_hr, hamiltonian_write_hr, hamiltonian_setup
    use w90_types, only: wannier_data_type, print_output_type, ws_region_type, &
                         atom_data_type, dis_manifold_type, timer_list_type
    use w90_wannier90_types, only: w90_calculation_type, transport_type, output_file_type, &
                                   real_space_ham_type, ham_logical_type

    implicit none

    ! arguments
    type(transport_type), intent(inout)         :: transport
    type(real_space_ham_type), intent(inout)    :: real_space_ham
    type(ws_region_type), intent(inout)         :: ws_region
    type(print_output_type), intent(in)         :: print_output
    type(w90_calculation_type), intent(in)      :: w90_calculation
    type(output_file_type), intent(in)          :: output_file
    type(wannier_data_type), intent(in)         :: wannier_data
    type(atom_data_type), intent(in)            :: atom_data
    type(dis_manifold_type), intent(in)         :: dis_manifold
    real(kind=dp), intent(in)                   :: kpt_latt(:, :)
    real(kind=dp), allocatable, intent(in)      :: fermi_energy_list(:)
    type(ham_logical_type), intent(inout)       :: ham_logical
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(inout)              :: rpt_origin
    integer, intent(inout)              :: nrpts
    integer, intent(inout), allocatable :: ndegen(:)
    integer, intent(inout), allocatable :: shift_vec(:, :)
    integer, intent(inout), allocatable :: irvec(:, :)
    integer, intent(in)                 :: num_wann
    integer, intent(in)                 :: num_bands
    integer, intent(in)                 :: num_kpts
    integer, intent(in)                 :: stdout
    integer, intent(in)                 :: mp_grid(3)

    real(kind=dp), intent(inout), allocatable :: wannier_centres_translated(:, :)
    real(kind=dp), intent(in)                 :: real_lattice(3, 3)
    real(kind=dp), intent(in)                 :: eigval(:, :)

    complex(kind=dp), intent(in)                 :: u_matrix(:, :, :)
    complex(kind=dp), intent(in)                 :: u_matrix_opt(:, :, :)
    complex(kind=dp), allocatable, intent(inout) :: ham_k(:, :, :)
    complex(kind=dp), intent(inout), allocatable :: ham_r(:, :, :)

    character(len=*), intent(in) :: bands_plot_mode
    character(len=50), intent(in)  :: seedname
    logical, intent(in) :: have_disentangled
    logical, intent(in) :: lsitesymmetry  !YN:

    ! local variables
    integer              :: one_dim_vec
    !! cartesian axis to which real_lattice(:,one_dim_vec) is parallel
    integer              :: nrpts_one_dim
    integer              :: num_pl
    !! number of unit cell in a principal layer
    integer              :: coord(3)
    !! coord : coord(1) defines the conduction direction according to
    !1=x,2=y,3=z
    !! coord(2),coord(3) define the other directions during sorting routines
    integer, allocatable :: tran_sorted_idx(:)
    !! index of sorted WF centres to unsorted
    integer              :: num_G
    integer :: irvec_max ! size of hr_one_dim's last dimension (:, :, -irvec_max:irvec_max)
    integer :: ierr

    real(kind=dp), allocatable :: hr_one_dim(:, :, :)
    real(kind=dp), allocatable :: signatures(:, :)
    real(kind=dp), allocatable :: hB0(:, :)
    real(kind=dp), allocatable :: hB1(:, :)
    real(kind=dp), allocatable :: hC(:, :)
    real(kind=dp), allocatable :: hCR(:, :)
    real(kind=dp), allocatable :: hL0(:, :)
    real(kind=dp), allocatable :: hL1(:, :)
    real(kind=dp), allocatable :: hLC(:, :)
    real(kind=dp), allocatable :: hR0(:, :)
    real(kind=dp), allocatable :: hR1(:, :)

    logical :: pl_warning

    ! fixme printout guards as elsewhere please (even if not yet parallel)
    if (print_output%timing_level > 0) call io_stopwatch_start('tran: main', timer)

    write (stdout, '(/1x,a)') '*---------------------------------------------------------------------------*'
    write (stdout, '(1x,a)') '|                              TRANSPORT                                    |'
    write (stdout, '(1x,a)') '*---------------------------------------------------------------------------*'
    write (stdout, *)

    if (index(transport%mode, 'bulk') > 0) then
      write (stdout, '(/1x,a/)') 'Calculation of Quantum Conductance and DoS: bulk mode'
      if (.not. transport%read_ht) then
        call hamiltonian_setup(ham_logical, print_output, ws_region, w90_calculation, ham_k, &
                               ham_r, real_lattice, wannier_centres_translated, irvec, mp_grid, &
                               ndegen, num_kpts, num_wann, nrpts, rpt_origin, bands_plot_mode, &
                               stdout, timer, error, transport%mode, comm)
        if (allocated(error)) return

        call hamiltonian_get_hr(atom_data, dis_manifold, ham_logical, real_space_ham, &
                                print_output, ham_k, ham_r, u_matrix, u_matrix_opt, eigval, &
                                kpt_latt, real_lattice, wannier_data%centres, &
                                wannier_centres_translated, irvec, shift_vec, nrpts, num_bands, &
                                num_kpts, num_wann, have_disentangled, stdout, timer, error, &
                                lsitesymmetry, comm)
        if (allocated(error)) return

        if (output_file%write_hr) then
          ! is this redundant after the plot call?
          call hamiltonian_write_hr(ham_r, irvec, ndegen, nrpts, num_wann, &
                                    print_output%timing_level, seedname, timer, error, comm)
        end if

        if (allocated(error)) return

        call tran_reduce_hr(real_space_ham, ham_r, hr_one_dim, real_lattice, irvec, mp_grid, &
                            irvec_max, nrpts, nrpts_one_dim, num_wann, one_dim_vec, &
                            print_output%timing_level, stdout, timer, error, comm)
        if (allocated(error)) return

        call tran_cut_hr_one_dim(real_space_ham, transport, print_output, hr_one_dim, &
                                 real_lattice, wannier_centres_translated, mp_grid, irvec_max, &
                                 num_pl, num_wann, one_dim_vec, stdout, timer)

        call tran_get_ht(fermi_energy_list, transport, hB0, hB1, hr_one_dim, irvec_max, num_pl, &
                         num_wann, print_output%timing_level, seedname, timer, error, comm)
        if (allocated(error)) return

        if (output_file%write_xyz) then
          call tran_write_xyz(atom_data, transport, wannier_centres_translated, tran_sorted_idx, &
                              num_wann, seedname, stdout)
        end if
      end if
      call tran_bulk(transport, hB0, hB1, print_output%timing_level, stdout, seedname, timer, error, comm)
      if (allocated(error)) return
    end if

    if (index(transport%mode, 'lcr') > 0) then
      write (stdout, '(/1x,a/)') 'Calculation of Quantum Conductance and DoS: lead-conductor-lead mode'
      if (.not. transport%read_ht) then
        call hamiltonian_setup(ham_logical, print_output, ws_region, w90_calculation, ham_k, &
                               ham_r, real_lattice, wannier_centres_translated, irvec, mp_grid, &
                               ndegen, num_kpts, num_wann, nrpts, rpt_origin, bands_plot_mode, &
                               stdout, timer, error, transport%mode, comm)
        if (allocated(error)) return

        call hamiltonian_get_hr(atom_data, dis_manifold, ham_logical, real_space_ham, &
                                print_output, ham_k, ham_r, u_matrix, u_matrix_opt, eigval, &
                                kpt_latt, real_lattice, wannier_data%centres, &
                                wannier_centres_translated, irvec, shift_vec, nrpts, num_bands, &
                                num_kpts, num_wann, have_disentangled, stdout, timer, error, &
                                lsitesymmetry, comm)
        if (allocated(error)) return

        if (output_file%write_hr) then
          call hamiltonian_write_hr(ham_r, irvec, ndegen, nrpts, num_wann, &
                                    print_output%timing_level, seedname, timer, error, comm)
          if (allocated(error)) return
        end if

        call tran_reduce_hr(real_space_ham, ham_r, hr_one_dim, real_lattice, irvec, mp_grid, &
                            irvec_max, nrpts, nrpts_one_dim, num_wann, one_dim_vec, &
                            print_output%timing_level, stdout, timer, error, comm)
        if (allocated(error)) return

        call tran_cut_hr_one_dim(real_space_ham, transport, print_output, hr_one_dim, &
                                 real_lattice, wannier_centres_translated, mp_grid, irvec_max, &
                                 num_pl, num_wann, one_dim_vec, stdout, timer)

        write (stdout, *) '------------------------- 2c2 Calculation Type: ------------------------------'
        write (stdout, *) ' '
        call tran_find_integral_signatures(signatures, num_G, print_output, real_lattice, &
                                           u_matrix_opt, u_matrix, num_bands, num_wann, &
                                           have_disentangled, wannier_centres_translated, stdout, &
                                           seedname, timer, error, comm)
        if (allocated(error)) return

        call tran_lcr_2c2_sort(signatures, num_G, pl_warning, transport, atom_data, wannier_data, &
                               real_space_ham, print_output, real_lattice, num_wann, mp_grid, &
                               ham_r, irvec, nrpts, wannier_centres_translated, one_dim_vec, &
                               nrpts_one_dim, num_pl, coord, tran_sorted_idx, hr_one_dim, &
                               irvec_max, output_file%write_xyz, stdout, seedname, timer, error, comm)
        if (allocated(error)) return

        if (output_file%write_xyz) call tran_write_xyz(atom_data, transport, &
                                                       wannier_centres_translated, &
                                                       tran_sorted_idx, num_wann, seedname, stdout)

        call tran_parity_enforce(signatures, print_output, transport, num_wann, tran_sorted_idx, &
                                 hr_one_dim, irvec_max, stdout, timer)

        call tran_lcr_2c2_build_ham(pl_warning, real_space_ham, fermi_energy_list, kpt_latt, &
                                    num_wann, transport, print_output, real_lattice, mp_grid, &
                                    ham_r, irvec, nrpts, wannier_centres_translated, one_dim_vec, &
                                    nrpts_one_dim, num_pl, coord, tran_sorted_idx, hC, hCR, hL0, &
                                    hL1, hLC, hR0, hR1, hr_one_dim, irvec_max, stdout, seedname, &
                                    timer, error, comm)
        if (allocated(error)) return
      end if
      call tran_lcr(transport, hC, hCR, hL0, hL1, hLC, hR0, hR1, print_output%timing_level, &
                    stdout, seedname, timer, error, comm)
      if (allocated(error)) return

    end if

    if (print_output%timing_level > 0) call io_stopwatch_stop('tran: main', timer)

    if (allocated(hR1)) then
      deallocate (hR1, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating hR1 in tran_main', comm)
        return
      end if
    end if
    if (allocated(hR0)) then
      deallocate (hR0, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating hR0 in tran_main', comm)
        return
      end if
    end if
    if (allocated(hL1)) then
      deallocate (hL1, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating hL1 in tran_main', comm)
        return
      end if
    end if
    if (allocated(hB1)) then
      deallocate (hB1, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating hB1 in tran_main', comm)
        return
      end if
    end if
    if (allocated(hB0)) then
      deallocate (hB0, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating hB0 in tran_main', comm)
        return
      end if
    end if
    if (allocated(hr_one_dim)) then
      deallocate (hr_one_dim, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating hr_one_dim in tran_main', comm)
        return
      end if
    end if

  end subroutine tran_main

  !================================================!
  subroutine tran_reduce_hr(real_space_ham, ham_r, hr_one_dim, real_lattice, irvec, mp_grid, &
                            irvec_max, nrpts, nrpts_one_dim, num_wann, one_dim_vec, timing_level, &
                            stdout, timer, error, comm)
    !================================================!
    !
    ! reduce ham_r from 3-d to 1-d
    !
    !================================================!

    use w90_constants, only: dp, eps8
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop
    use w90_wannier90_types, only: real_space_ham_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_fatal
    use w90_types, only: timer_list_type

    implicit none

    ! passed vars
    type(real_space_ham_type), intent(in) :: real_space_ham
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: irvec(:, :)
    integer, intent(inout) :: irvec_max ! limits of hr_one_dim final dim
    integer, intent(in) :: mp_grid(3)
    integer, intent(in) :: nrpts
    integer, intent(in) :: num_wann
    integer, intent(inout) :: nrpts_one_dim
    integer, intent(inout) :: one_dim_vec
    integer, intent(in) :: timing_level
    integer, intent(in) :: stdout

    real(kind=dp), allocatable, intent(inout) :: hr_one_dim(:, :, :)
    real(kind=dp), intent(in) :: real_lattice(3, 3)

    complex(kind=dp), intent(in) :: ham_r(:, :, :)

    ! local variables
    integer :: ierr
    integer :: irvec_tmp(3), two_dim_vec(2)
    integer :: i, j
    integer :: i1, i2, i3, n1, nrpts_tmp, loop_rpt

    if (timing_level > 1) call io_stopwatch_start('tran: reduce_hr', timer)

    ! Find one_dim_vec which is parallel to one_dim_dir
    ! two_dim_vec - the other two lattice vectors
    j = 0
    do i = 1, 3
      if (abs(abs(real_lattice(real_space_ham%one_dim_dir, i)) &
              - sqrt(dot_product(real_lattice(:, i), real_lattice(:, i)))) .lt. eps8) then
        one_dim_vec = i
        j = j + 1
      end if
    end do
    if (j .ne. 1) then
      write (stdout, '(i3,a)') j, ' : 1-D LATTICE VECTOR NOT DEFINED'
      call set_error_fatal(error, 'Error: 1-d lattice vector not defined in tran_reduce_hr', comm)
      return
    end if

    j = 0
    do i = 1, 3
      if (i .ne. one_dim_vec) then
        j = j + 1
        two_dim_vec(j) = i
      end if
    end do

    ! starting H matrix should include all W-S supercell where
    ! the center of the cell spans the full space of the home cell
    ! adding one more buffer layer when mp_grid(one_dim_vec) is an odd number

    !irvec_max = (mp_grid(one_dim_vec)+1)/2
    irvec_tmp = maxval(irvec, DIM=2) + 1
    irvec_max = irvec_tmp(one_dim_vec)
    nrpts_one_dim = 2*irvec_max + 1

    allocate (hr_one_dim(num_wann, num_wann, -irvec_max:irvec_max), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hr_one_dim in tran_reduce_hr', comm)
      return
    end if
    hr_one_dim = 0.0_dp

    ! check imaginary part
    write (stdout, '(1x,a,F12.6)') 'Maximum imaginary part of the real-space Hamiltonian: ', &
      maxval(abs(aimag(ham_r)))

    ! select a subset of ham_r, where irvec is 0 along the two other lattice vectors

    nrpts_tmp = 0
    loop_n1: do n1 = -irvec_max, irvec_max
      do loop_rpt = 1, nrpts
        i1 = mod(n1 - irvec(one_dim_vec, loop_rpt), mp_grid(one_dim_vec))
        i2 = irvec(two_dim_vec(1), loop_rpt)
        i3 = irvec(two_dim_vec(2), loop_rpt)
        if (i1 .eq. 0 .and. i2 .eq. 0 .and. i3 .eq. 0) then
          nrpts_tmp = nrpts_tmp + 1
          hr_one_dim(:, :, n1) = real(ham_r(:, :, loop_rpt), dp)
          cycle loop_n1
        end if
      end do
    end do loop_n1

    if (nrpts_tmp .ne. nrpts_one_dim) then
      write (stdout, '(a)') 'FAILED TO EXTRACT 1-D HAMILTONIAN'
      call set_error_fatal(error, 'Error: cannot extract 1d hamiltonian in tran_reduce_hr', comm)
      return
    end if

    if (timing_level > 1) call io_stopwatch_stop('tran: reduce_hr', timer)

    return

  end subroutine tran_reduce_hr

  !================================================!
  subroutine tran_cut_hr_one_dim(real_space_ham, transport, print_output, hr_one_dim, &
                                 real_lattice, wannier_centres_translated, mp_grid, irvec_max, &
                                 num_pl, num_wann, one_dim_vec, stdout, timer)
    !================================================!

    use w90_constants, only: dp
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop
    use w90_types, only: print_output_type, timer_list_type
    use w90_wannier90_types, only: transport_type, real_space_ham_type

    implicit none

    ! arguments
    type(real_space_ham_type), intent(inout) :: real_space_ham
    type(print_output_type), intent(in) :: print_output
    type(transport_type), intent(inout) :: transport
    type(timer_list_type), intent(inout) :: timer

    integer, intent(in) :: mp_grid(3)
    integer, intent(in) :: irvec_max
    integer, intent(in) :: num_wann
    integer, intent(in) :: one_dim_vec
    integer, intent(inout) :: num_pl
    integer, intent(in) :: stdout

    real(kind=dp), intent(inout) :: hr_one_dim(:, :, -irvec_max:)
    real(kind=dp), intent(in) :: real_lattice(3, 3)
    real(kind=dp), intent(in) :: wannier_centres_translated(:, :)

    ! local variables
    integer :: i, j, n1
    real(kind=dp) :: hr_max
    real(kind=dp) :: dist
    real(kind=dp) :: dist_vec(3)
    real(kind=dp) :: dist_ij_vec(3)
    real(kind=dp) :: shift_vec(3, -irvec_max:irvec_max)
    real(kind=dp) :: hr_tmp(num_wann, num_wann)

    if (print_output%timing_level > 1) call io_stopwatch_start('tran: cut_hr_one_dim', timer)

    !irvec_max = nrpts_one_dim/2 ! now passed as arg
    ! maximum possible dist_cutoff
    dist = real(mp_grid(one_dim_vec), dp)*abs(real_lattice(real_space_ham%one_dim_dir, one_dim_vec)) &
           /2.0_dp
    if (real_space_ham%dist_cutoff .gt. dist) then
      write (stdout, '(1x,a,1x,F10.5,1x,a)') 'dist_cutoff', real_space_ham%dist_cutoff, &
        trim(print_output%length_unit), 'is too large'
      real_space_ham%dist_cutoff = dist
      ! aam_2012-04-13
      real_space_ham%dist_cutoff_hc = dist
      write (stdout, '(4x,a,1x,F10.5,1x,a)') 'reset to', real_space_ham%dist_cutoff, &
        trim(print_output%length_unit)
    end if

    do n1 = -irvec_max, irvec_max
      shift_vec(:, n1) = real(n1, dp)*(real_lattice(:, one_dim_vec))
!           write(stdout,'(a,3f10.6)') 'shift_vec', shift_vec(:,n1)
    end do

    ! apply dist_cutoff first
    if (index(real_space_ham%dist_cutoff_mode, 'one_dim') > 0) then
      do i = 1, num_wann
        do j = 1, num_wann
          dist_ij_vec(real_space_ham%one_dim_dir) = wannier_centres_translated(real_space_ham%one_dim_dir, i) &
                                                    - wannier_centres_translated(real_space_ham%one_dim_dir, j)
          do n1 = -irvec_max, irvec_max
            dist_vec(real_space_ham%one_dim_dir) = dist_ij_vec(real_space_ham%one_dim_dir) &
                                                   + shift_vec(real_space_ham%one_dim_dir, n1)

            !MS: Add special case for lcr: We must not cut the elements that are within
            !    dist_cutoff under PBC's (single kpt assumed) in order to build
            !    hamiltonians correctly in tran_2c2_build_hams

            if ((index(transport%mode, 'lcr') > 0) .and. &
                !~                    (transport%num_cell_ll .eq. 1)        .and. &
                (abs(dist_vec(real_space_ham%one_dim_dir)) .gt. real_space_ham%dist_cutoff)) then
              ! Move to right
              dist_vec(real_space_ham%one_dim_dir) = dist_ij_vec(real_space_ham%one_dim_dir) &
                                                     + real_lattice(real_space_ham%one_dim_dir, one_dim_vec)
              ! Move to left
              if (abs(dist_vec(real_space_ham%one_dim_dir)) .gt. real_space_ham%dist_cutoff) &
                dist_vec(real_space_ham%one_dim_dir) = dist_ij_vec(real_space_ham%one_dim_dir) &
                                                       - real_lattice(real_space_ham%one_dim_dir, one_dim_vec)
            end if

            !end MS

            dist = abs(dist_vec(real_space_ham%one_dim_dir))
            if (dist .gt. real_space_ham%dist_cutoff) hr_one_dim(j, i, n1) = 0.0_dp
          end do
        end do
      end do
    else
      do i = 1, num_wann
        do j = 1, num_wann
          dist_ij_vec(:) = wannier_centres_translated(:, i) - wannier_centres_translated(:, j)
          do n1 = -irvec_max, irvec_max
            dist_vec(:) = dist_ij_vec(:) + shift_vec(:, n1)
            dist = sqrt(dot_product(dist_vec, dist_vec))

            ! MS: Special case (as above) equivalent for alternate definition of cut off

            if ((index(transport%mode, 'lcr') > 0) .and. &
                !~                   (transport%num_cell_ll .eq. 1)         .and. &
                (dist .gt. real_space_ham%dist_cutoff)) then
              ! Move to right
              dist_vec(:) = dist_ij_vec(:) + real_lattice(:, one_dim_vec)
              dist = sqrt(dot_product(dist_vec, dist_vec))
              ! Move to left
              if (dist .gt. real_space_ham%dist_cutoff) then
                dist_vec(:) = dist_ij_vec(:) - real_lattice(:, one_dim_vec)
                dist = sqrt(dot_product(dist_vec, dist_vec))
              end if
            end if

            ! End MS

            if (dist .gt. real_space_ham%dist_cutoff) hr_one_dim(j, i, n1) = 0.0_dp
          end do
        end do
      end do
    end if

    ! output maximum to check a decay of H as a function of lattice vector R
    write (stdout, '(/1x,a78)') repeat('-', 78)
    write (stdout, '(1x,4x,a)') &
      'Maximum real part of the real-space Hamiltonian at each lattice point'
    write (stdout, '(1x,8x,a62)') repeat('-', 62)
    write (stdout, '(1x,11x,a,11x,a)') 'Lattice point R', 'Max |H_ij(R)|'
    ! calculate number of units inside a principal layer
    num_pl = 0
    do n1 = -irvec_max, irvec_max
      hr_tmp(:, :) = abs(hr_one_dim(:, :, n1))
      hr_max = maxval(hr_tmp)
      if (hr_max .gt. real_space_ham%hr_cutoff) then
        if (abs(n1) .gt. num_pl) num_pl = abs(n1)
      else
        hr_one_dim(:, :, n1) = 0.0_dp
      end if
      write (stdout, '(1x,9x,5x,I5,5x,12x,F12.6)') n1, hr_max
    end do
    write (stdout, '(1x,8x,a62)') repeat('-', 62)
    if (index(transport%mode, 'lcr') > 0) then
      write (stdout, '(/1x,a,I6)') 'Number of unit cells inside the principal layer:', &
        transport%num_cell_ll
      write (stdout, '(1x,a,I6)') 'Number of Wannier Functions inside the principal layer:', &
        transport%num_ll
    elseif (index(transport%mode, 'bulk') > 0) then
      write (stdout, '(/1x,a,I6)') 'Number of unit cells inside the principal layer:', num_pl
      write (stdout, '(1x,a,I6)') 'Number of Wannier Functions inside the principal layer:', &
        num_pl*num_wann
    end if
    ! apply hr_cutoff to each element inside the principal layer
    do n1 = -num_pl, num_pl
      do i = 1, num_wann
        do j = 1, num_wann
          if (abs(hr_one_dim(j, i, n1)) .lt. real_space_ham%hr_cutoff) hr_one_dim(j, i, n1) = 0.0_dp
        end do
      end do
    end do

    if (print_output%timing_level > 1) call io_stopwatch_stop('tran: cut_hr_one_dim', timer)

    return

  end subroutine tran_cut_hr_one_dim

  !================================================!
  subroutine tran_get_ht(fermi_energy_list, transport, hB0, hB1, hr_one_dim, irvec_max, num_pl, &
                         num_wann, timing_level, seedname, timer, error, comm)
    !================================================!
    !
    !!  Construct h00 and h01
    !
    !================================================!

    use w90_constants, only: dp
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop, io_date
    use w90_wannier90_types, only: transport_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_fatal
    use w90_types, only: timer_list_type

    implicit none

    ! arguments
    real(kind=dp), allocatable, intent(in) :: fermi_energy_list(:)
    type(transport_type), intent(inout) :: transport
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: num_pl
    integer, intent(in) :: num_wann
    integer, intent(in) :: irvec_max
    integer, intent(in) :: timing_level

    real(kind=dp), intent(in) :: hr_one_dim(:, :, -irvec_max:)
    real(kind=dp), allocatable, intent(inout) :: hB0(:, :)
    real(kind=dp), allocatable, intent(inout) :: hB1(:, :)

    character(len=50), intent(in)  :: seedname

    ! local variables
    integer :: ierr, file_unit
    integer :: i, j, n1, im, jm
    integer :: fermi_n
    character(len=9) :: cdate, ctime

    if (timing_level > 1) call io_stopwatch_start('tran: get_ht', timer)

    fermi_n = 0
    if (allocated(fermi_energy_list)) fermi_n = size(fermi_energy_list)
    if (fermi_n > 1) then
      call set_error_fatal(error, "Error in tran_get_ht: nfermi>1. " &
                           //"Set the fermi level using the input parameter 'fermi_evel'", comm)
      return
    end if

    transport%num_bb = num_pl*num_wann

    allocate (hB0(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hB0 in tran_get_ht', comm)
      return
    end if
    allocate (hB1(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hB1 in tran_get_ht', comm)
      return
    end if

    hB0 = 0.0_dp
    hB1 = 0.0_dp

    ! h00
    do j = 0, num_pl - 1
      do i = 0, num_pl - 1
        n1 = i - j
        im = i*num_wann
        jm = j*num_wann
        hB0(jm + 1:jm + num_wann, im + 1:im + num_wann) = hr_one_dim(:, :, n1)
      end do
    end do

    ! h01
    do j = 1, num_pl
      do i = 0, j - 1
        n1 = i - (j - 1) + num_pl
        im = i*num_wann
        jm = (j - 1)*num_wann
        hB1(jm + 1:jm + num_wann, im + 1:im + num_wann) = hr_one_dim(:, :, n1)
      end do
    end do

    ! shift by fermi_energy
    do i = 1, transport%num_bb
      hB0(i, i) = hB0(i, i) - fermi_energy_list(1)
    end do

    if (transport%write_ht) then

      open (newunit=file_unit, file=trim(seedname)//'_htB.dat', status='unknown', form='formatted', &
            action='write')

      call io_date(cdate, ctime)
      write (file_unit, *) 'written on '//cdate//' at '//ctime ! Date and time
      write (file_unit, '(I6)') transport%num_bb
      write (file_unit, '(6F12.6)') ((hB0(j, i), j=1, transport%num_bb), i=1, transport%num_bb)
      write (file_unit, '(I6)') transport%num_bb
      write (file_unit, '(6F12.6)') ((hB1(j, i), j=1, transport%num_bb), i=1, transport%num_bb)

      close (file_unit)

    end if

    if (timing_level > 1) call io_stopwatch_stop('tran: get_ht', timer)

    return

  end subroutine tran_get_ht

  !================================================!
  subroutine tran_bulk(transport, hB0, hB1, timing_level, stdout, seedname, timer, error, comm)
    !================================================!

    use w90_constants, only: dp, cmplx_0, cmplx_1, cmplx_i, pi
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop, io_date
    use w90_wannier90_types, only: transport_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc
    use w90_types, only: timer_list_type

    implicit none

    ! arguments
    integer, intent(in) :: stdout
    integer, intent(in) :: timing_level

    real(kind=dp), allocatable :: hB0(:, :)
    real(kind=dp), allocatable :: hB1(:, :)

    type(transport_type), intent(in) :: transport
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    character(len=50), intent(in)  :: seedname

    ! local variables
    integer :: qc_unit, dos_unit
    integer :: ierr
    integer :: n_e, n, i
    real(kind=dp) ::  qc, dos
    real(kind=dp) ::  e_scan
    complex(kind=dp) :: e_scan_cmp
    complex(kind=dp), allocatable, dimension(:, :) :: tot, tott
    complex(kind=dp), allocatable, dimension(:, :) :: g_B, gR, gL
    complex(kind=dp), allocatable, dimension(:, :) :: sLr, sRr
    complex(kind=dp), allocatable, dimension(:, :) :: s1, s2, c1
    character(len=50) :: filename
    character(len=9)  :: cdate, ctime

    if (timing_level > 1) call io_stopwatch_start('tran: bulk', timer)

    allocate (tot(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tot in tran_bulk', comm)
      return
    end if
    allocate (tott(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tott in tran_bulk', comm)
      return
    end if
    allocate (g_B(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating g_B in tran_bulk', comm)
      return
    end if
    allocate (gL(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating gL in tran_bulk', comm)
      return
    end if
    allocate (gR(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating gR in tran_bulk', comm)
      return
    end if
    allocate (sLr(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sLr in tran_bulk', comm)
      return
    end if
    allocate (sRr(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sRr in tran_bulk', comm)
      return
    end if
    allocate (s1(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s1 in tran_bulk', comm)
      return
    end if
    allocate (s2(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s2 in tran_bulk', comm)
      return
    end if
    allocate (c1(transport%num_bb, transport%num_bb), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating c1 in tran_bulk', comm)
      return
    end if

    call io_date(cdate, ctime)

    open (newunit=qc_unit, file=trim(seedname)//'_qc.dat', status='unknown', &
          form='formatted', action='write')
    write (qc_unit, *) '## written on '//cdate//' at '//ctime ! Date and time

    open (newunit=dos_unit, file=trim(seedname)//'_dos.dat', status='unknown', &
          form='formatted', action='write')
    write (dos_unit, *) '## written on '//cdate//' at '//ctime ! Date and time

    !   set up the layer hamiltonians

    if (transport%read_ht) then
      allocate (hB0(transport%num_bb, transport%num_bb), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating hB0 in tran_bulk', comm)
        return
      end if
      allocate (hB1(transport%num_bb, transport%num_bb), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating hB1 in tran_bulk', comm)
        return
      end if
      filename = trim(seedname)//'_htB.dat'
      call tran_read_htX(transport%num_bb, hB0, hB1, filename, stdout, error, comm)
      if (allocated(error)) return
    end if

    !   loop over the energies

    n_e = floor((transport%win_max - transport%win_min)/transport%energy_step) + 1

    write (stdout, '(/1x,a)', advance='no') 'Calculating quantum&
        & conductance and density of states...'

    do n = 1, n_e
      e_scan = transport%win_min + real(n - 1, dp)*transport%energy_step

!       if (mod(n,nint(0.1*n_e)).eq.0) write(stdout,'(a)',advance='no') '.'

      ! compute conductance according to Fisher and Lee
      ! retarded Green

      e_scan_cmp = e_scan + eta
      call tran_transfer(tot, tott, hB0, hB1, e_scan_cmp, transport%num_bb, stdout, error, comm)
      if (allocated(error)) return
      call tran_green(tot, tott, hB0, hB1, e_scan, g_B, 0, 1, transport%num_bb, stdout, error, comm)
      if (allocated(error)) return

      ! compute S_Lr and S_Rr

      c1(:, :) = cmplx(hB1(:, :), kind=dp)

      ! Self-energy (Sigma_L^r) : sLr = (hB1)^+ * tott
      ! Self-energy (Sigma_R^r) : sRr = (hB1)   * tot
      sLr = cmplx_0
      sRr = cmplx_0
      call ZGEMM('C', 'N', transport%num_bb, transport%num_bb, transport%num_bb, cmplx_1, c1, &
                 transport%num_bb, tott, transport%num_bb, cmplx_0, sLr, transport%num_bb)
      call ZGEMM('N', 'N', transport%num_bb, transport%num_bb, transport%num_bb, cmplx_1, c1, &
                 transport%num_bb, tot, transport%num_bb, cmplx_0, sRr, transport%num_bb)

      ! Gamma_L = i(Sigma_L^r-Sigma_L^a)
      gL = cmplx_i*(sLr - conjg(transpose(sLr)))
      ! Gamma_R = i(Sigma_R^r-Sigma_R^a)
      gR = cmplx_i*(sRr - conjg(transpose(sRr)))

      s1 = cmplx_0
      s2 = cmplx_0
      c1 = cmplx_0
      ! s1 = Gamma_L * g_B^r
      call ZGEMM('N', 'N', transport%num_bb, transport%num_bb, transport%num_bb, cmplx_1, gL, &
                 transport%num_bb, g_B, transport%num_bb, cmplx_0, s1, transport%num_bb)
      ! s2 = Gamma_L * g_B^r * Gamma_R
      call ZGEMM('N', 'N', transport%num_bb, transport%num_bb, transport%num_bb, cmplx_1, s1, &
                 transport%num_bb, gR, transport%num_bb, cmplx_0, s2, transport%num_bb)
      ! c1 = Gamma_L * g_B^r * Gamma_R * g_B^a
      call ZGEMM('N', 'C', transport%num_bb, transport%num_bb, transport%num_bb, cmplx_1, s2, &
                 transport%num_bb, g_B, transport%num_bb, cmplx_0, c1, transport%num_bb)

      qc = 0.0_dp
      do i = 1, transport%num_bb
        qc = qc + real(c1(i, i), dp)
      end do
      write (qc_unit, '(f15.9,f18.9)') e_scan, qc

      dos = 0.0_dp
      do i = 1, transport%num_bb
        dos = dos - aimag(g_B(i, i))
      end do
      dos = dos/pi
      write (dos_unit, '(f15.9,f18.9)') e_scan, dos

    end do

    write (stdout, '(a/)') ' done'

    close (qc_unit)
    close (dos_unit)

    deallocate (c1, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating c1 in tran_bulk', comm)
      return
    end if
    deallocate (s2, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s2 in tran_bulk', comm)
      return
    end if
    deallocate (s1, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s1 in tran_bulk', comm)
      return
    end if
    deallocate (sRr, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sRr in tran_bulk', comm)
      return
    end if
    deallocate (sLr, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sLr in tran_bulk', comm)
      return
    end if
    deallocate (gR, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating gR in tran_bulk', comm)
      return
    end if
    deallocate (gL, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating gL in tran_bulk', comm)
      return
    end if
    deallocate (g_B, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating g_B in tran_bulk', comm)
      return
    end if
    deallocate (tott, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating tott in tran_bulk', comm)
      return
    end if
    deallocate (tot, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating tot in tran_bulk', comm)
      return
    end if

    if (timing_level > 1) call io_stopwatch_stop('tran: bulk', timer)

    return

  end subroutine tran_bulk

  !================================================!
  subroutine tran_lcr(transport, hC, hCR, hL0, hL1, hLC, hR0, hR1, timing_level, stdout, seedname, &
                      timer, error, comm)
    !================================================!

    use w90_constants, only: dp, cmplx_0, cmplx_1, cmplx_i, pi
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop, io_date
    use w90_wannier90_types, only: transport_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc, set_error_fatal
    use w90_types, only: timer_list_type

    implicit none

    ! arguments
    integer, intent(in) :: stdout
    integer, intent(in) :: timing_level

    real(kind=dp), allocatable, intent(inout) :: hC(:, :)
    real(kind=dp), allocatable, intent(inout) :: hCR(:, :)
    real(kind=dp), allocatable, intent(inout) :: hL0(:, :)
    real(kind=dp), allocatable, intent(inout) :: hL1(:, :)
    real(kind=dp), allocatable, intent(inout) :: hLC(:, :)
    real(kind=dp), allocatable, intent(inout) :: hR0(:, :)
    real(kind=dp), allocatable, intent(inout) :: hR1(:, :)

    type(transport_type), intent(in) :: transport
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    character(len=50), intent(in)  :: seedname

    ! local variables
    integer :: qc_unit, dos_unit
    integer :: ierr
    integer :: KL, KU, KC
    integer :: n_e, n, i, j, k, info
    integer, allocatable :: ipiv(:)

    real(kind=dp) ::  qc, dos
    real(kind=dp) ::  e_scan
    real(kind=dp), allocatable :: hCband(:, :)

    complex(kind=dp) :: e_scan_cmp
    complex(kind=dp), allocatable :: hLC_cmp(:, :), hCR_cmp(:, :)
    complex(kind=dp), allocatable :: totL(:, :), tottL(:, :), totR(:, :), tottR(:, :)
    complex(kind=dp), allocatable :: g_surf_L(:, :), g_surf_R(:, :)
    complex(kind=dp), allocatable :: g_C(:, :), g_C_inv(:, :)
    complex(kind=dp), allocatable :: gR(:, :), gL(:, :)
    complex(kind=dp), allocatable :: sLr(:, :), sRr(:, :)
    complex(kind=dp), allocatable :: s1(:, :), s2(:, :)
    complex(kind=dp), allocatable :: c1(:, :), c2(:, :)

    character(len=50) :: filename
    character(len=9) :: cdate, ctime

    if (timing_level > 1) call io_stopwatch_start('tran: lcr', timer)

    call io_date(cdate, ctime)

    open (newunit=qc_unit, file=trim(seedname)//'_qc.dat', status='unknown', &
          form='formatted', action='write')
    write (qc_unit, *) '## written on '//cdate//' at '//ctime ! Date and time

    open (newunit=dos_unit, file=trim(seedname)//'_dos.dat', status='unknown', &
          form='formatted', action='write')
    write (dos_unit, *) '## written on '//cdate//' at '//ctime ! Date and time

    KL = max(transport%num_lc, transport%num_cr, transport%num_bandc) - 1
    KU = KL
    KC = max(transport%num_lc, transport%num_cr)

    allocate (hCband(2*KL + KU + 1, transport%num_cc), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hCband in tran_lcr', comm)
      return
    end if
    allocate (hLC_cmp(transport%num_ll, transport%num_lc), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hLC_cmp in tran_lcr', comm)
      return
    end if
    allocate (hCR_cmp(transport%num_cr, transport%num_rr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hCR_cmp in tran_lcr', comm)
      return
    end if

    !If construct used only when reading matrices from file
    if (transport%read_ht) then
      allocate (hL0(transport%num_ll, transport%num_ll), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating hL0 in tran_lcr', comm)
        return
      end if
      allocate (hL1(transport%num_ll, transport%num_ll), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating hL1 in tran_lcr', comm)
        return
      end if
      allocate (hC(transport%num_cc, transport%num_cc), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating hC in tran_lcr', comm)
        return
      end if
      allocate (hLC(transport%num_ll, transport%num_lc), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating hLC in tran_lcr', comm)
        return
      end if
      allocate (hCR(transport%num_cr, transport%num_rr), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating hCR in tran_lcr', comm)
        return
      end if

      filename = trim(seedname)//'_htL.dat'
      call tran_read_htX(transport%num_ll, hL0, hL1, filename, stdout, error, comm)
      if (allocated(error)) return

      if (.not. transport%use_same_lead) then
        allocate (hR0(transport%num_rr, transport%num_rr), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating hR0 in tran_lcr', comm)
          return
        end if
        allocate (hR1(transport%num_rr, transport%num_rr), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating hR1 in tran_lcr', comm)
          return
        end if
        filename = trim(seedname)//'_htR.dat'
        call tran_read_htX(transport%num_rr, hR0, hR1, filename, stdout, error, comm)
        if (allocated(error)) return
      end if

      filename = trim(seedname)//'_htC.dat'
      call tran_read_htC(transport%num_cc, hC, filename, stdout, error, comm)
      if (allocated(error)) return
      filename = trim(seedname)//'_htLC.dat'
      call tran_read_htXY(transport%num_ll, transport%num_lc, hLC, filename, stdout, error, comm)
      if (allocated(error)) return
      filename = trim(seedname)//'_htCR.dat'
      call tran_read_htXY(transport%num_cr, transport%num_rr, hCR, filename, stdout, error, comm)
      if (allocated(error)) return
    end if

    !  Banded matrix H_C  :  save memory !
    do j = 1, transport%num_cc
      do i = max(1, j - KU), min(transport%num_cc, j + KL)
        hCband(KL + KU + 1 + i - j, j) = hC(i, j)
      end do
    end do
    deallocate (hC, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating hC in tran_lcr', comm)
      return
    end if

    !  H_LC : to a complex matrix
    hLC_cmp(:, :) = cmplx(hLC(:, :), kind=dp)
    deallocate (hLC, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating hLC in tran_lcr', comm)
      return
    end if

    !  H_CR : to a complex matrix
    hCR_cmp(:, :) = cmplx(hCR(:, :), kind=dp)
    deallocate (hCR, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating hCR in tran_lcr', comm)
      return
    end if

    allocate (totL(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating totL in tran_lcr', comm)
      return
    end if
    allocate (tottL(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tottL in tran_lcr', comm)
      return
    end if
    if (.not. transport%use_same_lead) then
      allocate (totR(transport%num_rr, transport%num_rr), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating totR in tran_lcr', comm)
        return
      end if
      allocate (tottR(transport%num_rr, transport%num_rr), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating tottR in tran_lcr', comm)
        return
      end if
    end if
    allocate (g_surf_L(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating g_surf_L in tran_lcr', comm)
      return
    end if
    allocate (g_surf_R(transport%num_rr, transport%num_rr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating g_surf_R in tran_lcr', comm)
      return
    end if
    allocate (g_C_inv(2*KL + KU + 1, transport%num_cc), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating g_C_inv in tran_lcr', comm)
      return
    end if
    allocate (g_C(transport%num_cc, transport%num_cc), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating g_C in tran_lcr', comm)
      return
    end if
    allocate (sLr(transport%num_lc, transport%num_lc), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sLr in tran_lcr', comm)
      return
    end if
    allocate (sRr(transport%num_cr, transport%num_cr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sRr in tran_lcr', comm)
      return
    end if
    allocate (gL(transport%num_lc, transport%num_lc), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating gL in tran_lcr', comm)
      return
    end if
    allocate (gR(transport%num_cr, transport%num_cr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating gR in tran_lcr', comm)
      return
    end if
    allocate (c1(transport%num_lc, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating c1 in tran_lcr', comm)
      return
    end if
    allocate (c2(transport%num_cr, transport%num_rr), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating c2 in tran_lcr', comm)
      return
    end if
    allocate (s1(KC, KC), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s1 in tran_lcr', comm)
      return
    end if
    allocate (s2(KC, KC), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s2 in tran_lcr', comm)
      return
    end if
    allocate (ipiv(transport%num_cc), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating ipiv in tran_lcr', comm)
      return
    end if

    !  Loop over the energies
    n_e = floor((transport%win_max - transport%win_min)/transport%energy_step) + 1

    write (stdout, '(/1x,a)', advance='no') 'Calculating quantum conductance and &
      &density of states...'

    do n = 1, n_e

      e_scan = transport%win_min + real(n - 1, dp)*transport%energy_step

      !    compute conductance according to Fisher and Lee
      !    compute self-energies following Datta

      e_scan_cmp = e_scan + eta

      ! Surface green function for the left lead : g_surf_L
      call tran_transfer(totL, tottL, hL0, hL1, e_scan_cmp, transport%num_ll, stdout, error, comm)
      if (allocated(error)) return

      call tran_green(totL, tottL, hL0, hL1, e_scan, g_surf_L, -1, 1, transport%num_ll, stdout, &
                      error, comm)
      if (allocated(error)) return

      ! Self-energy (Sigma_L) : sLr = (hLC_cmp)^+ * g_surf_L * hLC_cmp
      c1 = cmplx_0
      sLr = cmplx_0
      call ZGEMM('C', 'N', transport%num_lc, transport%num_ll, transport%num_ll, cmplx_1, &
                 hLC_cmp, transport%num_ll, g_surf_L, transport%num_ll, cmplx_0, c1, transport%num_lc)
      call ZGEMM('N', 'N', transport%num_lc, transport%num_lc, transport%num_ll, cmplx_1, &
                 c1, transport%num_lc, hLC_cmp, transport%num_ll, cmplx_0, sLr, transport%num_lc)

      ! Surface green function for the right lead : g_surf_R
      if (transport%use_same_lead) then
        call tran_green(totL, tottL, hL0, hL1, e_scan, g_surf_R, 1, 1, transport%num_rr, stdout, &
                        error, comm)
        if (allocated(error)) return

      else
        call tran_transfer(totR, tottR, hR0, hR1, e_scan_cmp, transport%num_rr, stdout, error, comm)
        if (allocated(error)) return

        call tran_green(totR, tottR, hR0, hR1, e_scan, g_surf_R, 1, 1, transport%num_rr, stdout, &
                        error, comm)
        if (allocated(error)) return
      end if

      ! Self-energy (Sigma_R) : sRr = hCR_cmp * g_surf_R * (hCR_cmp)^+
      c2 = cmplx_0
      sRr = cmplx_0
      call ZGEMM('N', 'N', transport%num_cr, transport%num_rr, transport%num_rr, cmplx_1, &
                 hCR_cmp, transport%num_cr, g_surf_R, transport%num_rr, cmplx_0, c2, transport%num_cr)
      call ZGEMM('N', 'C', transport%num_cr, transport%num_cr, transport%num_rr, cmplx_1, &
                 c2, transport%num_cr, hCR_cmp, transport%num_cr, cmplx_0, sRr, transport%num_cr)

      ! g_C^-1 = -H
      g_C_inv(:, :) = cmplx(-hCband(:, :), kind=dp)

      ! g_C^-1 = -H - Sigma_L^r
      do j = 1, transport%num_lc
        do i = max(1, j - KU), min(transport%num_lc, j + KL)
          g_C_inv(KL + KU + 1 + i - j, j) = g_C_inv(KL + KU + 1 + i - j, j) - sLr(i, j)
        end do
      end do

      ! g_C^-1 = -H - Sigma_L^r - Sigma_R^r
      do j = (transport%num_cc - transport%num_cr) + 1, transport%num_cc
        do i = max((transport%num_cc - transport%num_cr) + 1, j - (transport%num_cr - 1)), &
          min(transport%num_cc, j + (transport%num_cr - 1))
          g_C_inv(KL + KU + 1 + i - j, j) = &
            g_C_inv(KL + KU + 1 + i - j, j) - &
            sRr(i - (transport%num_cc - transport%num_cr), j - (transport%num_cc - transport%num_cr))
        end do
      end do

      ! g_C^-1 = eI - H - Sigma_L^r - Sigma_R^r
      do i = 1, transport%num_cc
        g_C_inv(KL + KU + 1, i) = e_scan + g_C_inv(KL + KU + 1, i)
      end do

      ! invert g_C^-1 => g_C
      g_C = cmplx_0
      do i = 1, transport%num_cc
        g_C(i, i) = cmplx_1
      end do

      call zgbsv(transport%num_cc, KL, KU, transport%num_cc, g_C_inv, 2*KL + KU + 1, ipiv, g_C, &
                 transport%num_cc, info)
      if (info .ne. 0) then
        write (stdout, *) 'ERROR: IN ZGBSV IN tran_lcr, INFO=', info
        call set_error_fatal(error, 'tran_lcr: problem in ZGBSV', comm)
        return
      end if

      ! Gamma_L = i(Sigma_L^r-Sigma_L^a)
      gL = cmplx_i*(sLr - conjg(transpose(sLr)))

      ! s1 = Gamma_L * g_C^r
      s1 = cmplx_0
      do j = 1, KC
        do i = 1, transport%num_lc
          do k = 1, transport%num_lc
            s1(i, j) = s1(i, j) + gL(i, k)*g_C(k, j + (transport%num_cc - KC))
          end do
        end do
      end do

      ! Gamma_R = i(Sigma_R^r-Sigma_R^a)
      gR = cmplx_i*(sRr - conjg(transpose(sRr)))

      ! s2 = Gamma_R * g_C^a
      s2 = cmplx_0
      do j = 1, KC
        do i = 1, transport%num_cr
          do k = 1, transport%num_cr
            s2(i + (KC - transport%num_cr), j) = s2(i + (KC - transport%num_cr), j) &
                                                 + gR(i, k)*conjg(g_C(j, k &
                                                                      + (transport%num_cc - transport%num_cr)))
          end do
        end do
      end do

      qc = 0.0_dp
      do i = 1, KC
        do j = 1, KC
          qc = qc + real(s1(i, j)*s2(j, i), dp)
        end do
      end do
      write (qc_unit, '(f15.9,f18.9)') e_scan, qc

      ! compute density of states for the conductor layer

      dos = 0.0_dp
      do i = 1, transport%num_cc
        dos = dos - aimag(g_C(i, i))
      end do
      dos = dos/pi
      write (dos_unit, '(f15.9,f18.9)') e_scan, dos

    end do

    write (stdout, '(a)') ' done'

    close (qc_unit)
    close (dos_unit)

    deallocate (ipiv, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating ipiv in tran_lcr', comm)
      return
    end if
    deallocate (s2, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s2 in tran_lcr', comm)
      return
    end if
    deallocate (s1, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s1 in tran_lcr', comm)
      return
    end if
    deallocate (c2, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating c2 in tran_lcr', comm)
      return
    end if
    deallocate (c1, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating c1 in tran_lcr', comm)
      return
    end if
    deallocate (gR, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating gR in tran_lcr', comm)
      return
    end if
    deallocate (gL, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating gL in tran_lcr', comm)
      return
    end if
    deallocate (sRr, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sRr in tran_lcr', comm)
      return
    end if
    deallocate (sLr, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating sLr in tran_lcr', comm)
      return
    end if
    deallocate (g_C, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating g_C in tran_lcr', comm)
      return
    end if
    deallocate (g_C_inv, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating g_C_inv in tran_lcr', comm)
      return
    end if
    deallocate (g_surf_R, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating g_surf_R in tran_lcr', comm)
      return
    end if
    deallocate (g_surf_L, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating g_surf_L in tran_lcr', comm)
      return
    end if
    if (allocated(tottR)) then
      deallocate (tottR, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating tottR in tran_lcr', comm)
        return
      end if
    end if
    if (allocated(totR)) then
      deallocate (totR, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error in deallocating totR in tran_lcr', comm)
        return
      end if
    end if
    deallocate (tottL, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating tottL in tran_lcr', comm)
      return
    end if
    deallocate (totL, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating totL in tran_lcr', comm)
      return
    end if
    deallocate (hCR_cmp, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating hCR_cmp in tran_lcr', comm)
      return
    end if
    deallocate (hLC_cmp, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating hLC_cmp in tran_lcr', comm)
      return
    end if
    deallocate (hCband, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating hCband in tran_lcr', comm)
      return
    end if

    if (timing_level > 1) call io_stopwatch_stop('tran: lcr', timer)

    return

  end subroutine tran_lcr

  !================================================!
  subroutine tran_transfer(tot, tott, h_00, h_01, e_scan_cmp, nxx, stdout, error, comm)
    !================================================!
    !
    ! iterative construction of the transfer matrix
    ! as Lopez-Sancho^2&Rubio, J.Phys.F:Met.Phys., v.14, 1205 (1984)
    ! and ibid. v.15, 851 (1985)
    !
    !================================================

    use w90_constants, only: dp, cmplx_0, cmplx_1, eps7
    use w90_error, only: w90_error_type, set_error_alloc, set_error_fatal, set_error_unconv, &
                         set_error_dealloc

    implicit none

    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) :: nxx
    integer, intent(in) :: stdout
    complex(kind=dp), intent(in) ::  e_scan_cmp
    complex(kind=dp), intent(out) ::  tot(nxx, nxx)
    complex(kind=dp), intent(out) ::  tott(nxx, nxx)
    real(kind=dp), intent(in) :: h_00(nxx, nxx)
    real(kind=dp), intent(in) :: h_01(nxx, nxx)

    integer  :: ierr, info
    integer  :: i, j, n, nxx2
    integer, allocatable :: ipiv(:)
    real(kind=dp) :: conver, conver2
    complex(kind=dp), allocatable, dimension(:, :) :: tsum, tsumt
    complex(kind=dp), allocatable, dimension(:, :) :: t11, t12
    complex(kind=dp), allocatable, dimension(:, :) :: s1, s2
    complex(kind=dp), allocatable, dimension(:, :, :) :: tau, taut

    allocate (ipiv(nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating ipiv in tran_transfer', comm)
      return
    end if
    allocate (tsum(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tsum in tran_transfer', comm)
      return
    end if
    allocate (tsumt(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tsumt in tran_transfer', comm)
      return
    end if
    allocate (t11(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating t11 in tran_transfer', comm)
      return
    end if
    allocate (t12(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating t12 in tran_transfer', comm)
      return
    end if
    allocate (s1(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s1 in tran_transfer', comm)
      return
    end if
    allocate (s2(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s2 in tran_transfer', comm)
      return
    end if
    allocate (tau(nxx, nxx, 2), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tau in tran_transfer', comm)
      return
    end if
    allocate (taut(nxx, nxx, 2), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating taut in tran_transfer', comm)
      return
    end if

    nxx2 = nxx*nxx

    tot = cmplx_0
    tott = cmplx_0

    ! construction of the transfer matrix
    ! t12 = e - h_00
    t12(:, :) = cmplx(-h_00(:, :), kind=dp)
    do i = 1, nxx
      t12(i, i) = e_scan_cmp + t12(i, i)
    end do

    ! compute (e - h_00)^-1 and store it in t11
    t11 = cmplx_0
    do i = 1, nxx
      t11(i, i) = cmplx_1
    end do

    ! inverse of t12 -> t11
    call ZGESV(nxx, nxx, t12, nxx, ipiv, t11, nxx, info)
    if (info .ne. 0) then
      write (stdout, *) 'ERROR:  IN ZGESV IN tran_transfer, INFO=', info
      call set_error_fatal(error, 'tran_transfer: problem in ZGESV 1', comm)
      return
    end if

    ! compute intermediate t-matrices (defined as tau(nxx,nxx,niter)
    ! and taut(...)):
    tau = cmplx_0
    taut = cmplx_0

    ! t_0:
    t12(:, :) = cmplx(h_01(:, :), kind=dp)

    !  tau  = ( e - H_00 )^-1 * H_01^+
    call ZGEMM('N', 'C', nxx, nxx, nxx, cmplx_1, t11, nxx, t12, nxx, cmplx_0, tau(1, 1, 1), nxx)
    !  taut = ( e - H_00 )^-1 * H_01
    call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, t11, nxx, t12, nxx, cmplx_0, taut(1, 1, 1), nxx)

    !   initialize T:
    tot(:, :) = tau(:, :, 1)
    tsum(:, :) = taut(:, :, 1)

    !   initialize T^bar:
    tott(:, :) = taut(:, :, 1)
    tsumt(:, :) = tau(:, :, 1)

    !   main loop:
    do n = 1, nterx

      t11 = cmplx_0
      t12 = cmplx_0

      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, tau(1, 1, 1), nxx, taut(1, 1, 1), nxx, cmplx_0, &
                 t11, nxx)
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, taut(1, 1, 1), nxx, tau(1, 1, 1), nxx, cmplx_0, &
                 t12, nxx)

      s1(:, :) = -t11(:, :) - t12(:, :)
      do i = 1, nxx
        s1(i, i) = cmplx_1 + s1(i, i)
      end do

      s2 = cmplx_0
      do i = 1, nxx
        s2(i, i) = cmplx_1
      end do

      call ZGESV(nxx, nxx, s1, nxx, ipiv, s2, nxx, info)
      if (info .ne. 0) then
        write (stdout, *) 'ERROR:  IN ZGESV IN tran_transfer, INFO=', info
        call set_error_fatal(error, 'tran_transfer: problem in ZGESV 2', comm)
        return
      end if

      t11 = cmplx_0
      t12 = cmplx_0

      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, tau(1, 1, 1), nxx, tau(1, 1, 1), nxx, cmplx_0, &
                 t11, nxx)
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, taut(1, 1, 1), nxx, taut(1, 1, 1), nxx, &
                 cmplx_0, t12, nxx)
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, s2, nxx, t11, nxx, cmplx_0, tau(1, 1, 2), nxx)
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, s2, nxx, t12, nxx, cmplx_0, taut(1, 1, 2), nxx)

      !   put the transfer matrices together

      t11 = cmplx_0
      s1 = cmplx_0

      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, tsum, nxx, tau(1, 1, 2), nxx, cmplx_0, t11, nxx)
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, tsum, nxx, taut(1, 1, 2), nxx, cmplx_0, s1, nxx)
      call ZCOPY(nxx2, t11, 1, s2, 1)
      call ZAXPY(nxx2, cmplx_1, tot, 1, s2, 1)

      tot(:, :) = s2(:, :)
      tsum(:, :) = s1(:, :)

      t11 = cmplx_0
      s1 = cmplx_0

      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, tsumt, nxx, taut(1, 1, 2), nxx, cmplx_0, t11, &
                 nxx)
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, tsumt, nxx, tau(1, 1, 2), nxx, cmplx_0, s1, nxx)
      call ZCOPY(nxx2, t11, 1, s2, 1)
      call ZAXPY(nxx2, cmplx_1, tott, 1, s2, 1)

      tott(:, :) = s2(:, :)
      tsumt(:, :) = s1(:, :)

      tau(:, :, 1) = tau(:, :, 2)
      taut(:, :, 1) = taut(:, :, 2)

      ! convergence check on the t-matrices

      conver = 0.0_dp
      conver2 = 0.0_dp

      do j = 1, nxx
        do i = 1, nxx
          conver = conver + sqrt(real(tau(i, j, 2), dp)**2 + aimag(tau(i, j, 2))**2)
          conver2 = conver2 + sqrt(real(taut(i, j, 2), dp)**2 + aimag(taut(i, j, 2))**2)
        end do
      end do

      if (conver .lt. eps7 .and. conver2 .lt. eps7) return
    end do

    if (conver .gt. eps7 .or. conver2 .gt. eps7) then
      call set_error_unconv(error, 'Error in converging transfer matrix in tran_transfer', comm)
      return
    end if

    deallocate (taut, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating taut in tran_transfer', comm)
      return
    end if
    deallocate (tau, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating tau in tran_transfer', comm)
      return
    end if
    deallocate (s2, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s2 in tran_transfer', comm)
      return
    end if
    deallocate (s1, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s1 in tran_transfer', comm)
      return
    end if
    deallocate (t12, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating t12 in tran_transfer', comm)
      return
    end if
    deallocate (t11, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating t11 in tran_transfer', comm)
      return
    end if
    deallocate (tsumt, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating tsumt in tran_transfer', comm)
      return
    end if
    deallocate (tsum, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating tsum in tran_transfer', comm)
      return
    end if
    deallocate (ipiv, stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in deallocating ipiv in tran_transfer', comm)
      return
    end if

    return

  end subroutine tran_transfer

  !================================================!
  subroutine tran_green(tot, tott, h_00, h_01, e_scan, g, igreen, invert, nxx, stdout, error, comm)
    !================================================!
    !   construct green's functions
    !
    !   igreen = -1  left surface
    !   igreen =  1  right surface
    !   igreen =  0  bulk
    !
    !   invert = 0 computes g^-1
    !   invert = 1 computes g^-1 and g
    !================================================!

    use w90_constants, only: dp, cmplx_0, cmplx_1
    use w90_error, only: w90_error_type, set_error_alloc, set_error_fatal, set_error_dealloc

    implicit none

    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) :: nxx
    integer, intent(in) :: stdout
    integer, intent(in) :: igreen
    integer, intent(in) :: invert
    real(kind=dp), intent(in) :: e_scan
    real(kind=dp), intent(in) :: h_00(nxx, nxx), h_01(nxx, nxx)
    complex(kind=dp), intent(in) :: tot(nxx, nxx), tott(nxx, nxx)
    complex(kind=dp), intent(out) :: g(nxx, nxx)

    integer :: ierr, info
    integer :: i
    integer, allocatable :: ipiv(:)
    complex(kind=dp), allocatable, dimension(:, :) :: g_inv, eh_00
    complex(kind=dp), allocatable, dimension(:, :) :: s1, s2, c1

    allocate (ipiv(nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating ipiv in tran_green', comm)
      return
    end if
    allocate (g_inv(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating g_inv in tran_green', comm)
      return
    end if
    allocate (eh_00(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating eh_00 in tran_green', comm)
      return
    end if
    allocate (c1(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating c1 in tran_green', comm)
      return
    end if
    allocate (s1(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s1 in tran_green', comm)
      return
    end if
    allocate (s2(nxx, nxx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating s2 in tran_green', comm)
      return
    end if

    c1(:, :) = cmplx(h_01(:, :), kind=dp)

    select case (igreen)

    case (1)

      ! construct the surface green's function g00

      s1 = cmplx_0
      ! s1 = H_01 * T
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, c1, nxx, tot, nxx, cmplx_0, s1, nxx)

      ! eh_00 =  -H_00 - H_01*T
      eh_00(:, :) = cmplx(-h_00(:, :), kind=dp) - s1(:, :)
      ! eh_00 = e_scan -H_00 - H_01*T
      do i = 1, nxx
        eh_00(i, i) = cmplx(e_scan, kind=dp) + eh_00(i, i)
      end do

      g_inv(:, :) = eh_00(:, :)

      ! identity
      g = cmplx_0
      do i = 1, nxx
        g(i, i) = cmplx_1
      end do

      if (invert .eq. 1) then
        call ZGESV(nxx, nxx, eh_00, nxx, ipiv, g, nxx, info)
        if (info .ne. 0) then
          write (stdout, *) 'ERROR:  IN ZGESV IN tran_green, INFO=', info
          call set_error_fatal(error, 'tran_green: problem in ZGESV 1', comm)
          return
        end if
      end if

    case (-1)

      !  construct the dual surface green's function gbar00

      s1 = cmplx_0
      ! s1 = H_01^+ * T^bar
      call ZGEMM('C', 'N', nxx, nxx, nxx, cmplx_1, c1, nxx, tott, nxx, cmplx_0, s1, nxx)

      ! s1 = -H_00 - H_01^+ * T^bar
      eh_00(:, :) = cmplx(-h_00(:, :), kind=dp) - s1(:, :)
      ! s1 = e_scan - H_00 - H_01^+ * T^bar
      do i = 1, nxx
        eh_00(i, i) = cmplx(e_scan, kind=dp) + eh_00(i, i)
      end do

      g_inv(:, :) = eh_00(:, :)

      ! identity
      g = cmplx_0
      do i = 1, nxx
        g(i, i) = cmplx_1
      end do

      if (invert .eq. 1) then
        call ZGESV(nxx, nxx, eh_00, nxx, ipiv, g, nxx, info)
        if (info .ne. 0) then
          write (stdout, *) 'ERROR:  IN ZGESV IN tran_green, INFO=', info
          call set_error_fatal(error, 'tran_green: problem in ZGESV 2', comm)
          return
        end if
      end if

    case (0)

      !  construct the bulk green's function gnn or (if surface=.true.) the
      !  sub-surface green's function

      s1 = cmplx_0
      s2 = cmplx_0
      ! s1 = H_01 * T
      call ZGEMM('N', 'N', nxx, nxx, nxx, cmplx_1, c1, nxx, tot, nxx, cmplx_0, s1, nxx)
      ! s2 = H_01^+ * T^bar
      call ZGEMM('C', 'N', nxx, nxx, nxx, cmplx_1, c1, nxx, tott, nxx, cmplx_0, s2, nxx)

      eh_00(:, :) = cmplx(-h_00(:, :), kind=dp) - s1(:, :) - s2(:, :)
      do i = 1, nxx
        eh_00(i, i) = cmplx(e_scan, kind=dp) + eh_00(i, i)
      end do

      g_inv(:, :) = eh_00(:, :)

      ! identity
      g = cmplx_0
      do i = 1, nxx
        g(i, i) = cmplx_1
      end do

      if (invert .eq. 1) then
        call ZGESV(nxx, nxx, eh_00, nxx, ipiv, g, nxx, info)
        if (info .ne. 0) then
          write (stdout, *) 'ERROR:  IN ZGESV IN tran_green, INFO=', info
          call set_error_fatal(error, 'tran_green: problem in ZGESV 3', comm)
          return
        end if
      end if

    end select

    deallocate (s2, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s2 in tran_green', comm)
      return
    end if
    deallocate (s1, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating s1 in tran_green', comm)
      return
    end if
    deallocate (c1, stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in deallocating c1 in tran_green', comm)
      return
    end if
    deallocate (eh_00, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating eh_00 in tran_green', comm)
      return
    end if
    deallocate (g_inv, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating g_inv in tran_green', comm)
      return
    end if
    deallocate (ipiv, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error in deallocating ipiv in tran_green', comm)
      return
    end if

    return

  end subroutine tran_green

  !================================================!
  subroutine tran_read_htX(nxx, h_00, h_01, h_file, stdout, error, comm)
    !================================================!

    use w90_constants, only: dp, maxlen
    use w90_error, only: w90_error_type, set_error_file, set_error_file

    implicit none

    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) ::  nxx
    integer, intent(in) ::  stdout
    real(kind=dp), intent(out) :: h_00(nxx, nxx), h_01(nxx, nxx)
    character(len=50), intent(in) :: h_file

    integer :: i, j, nw, file_unit
    character(len=maxlen) :: dummy

    open (newunit=file_unit, file=h_file, form='formatted', &
          status='old', action='read', err=101)

    write (stdout, '(/a)', advance='no') ' Reading H matrix from '//h_file//'  : '

    read (file_unit, '(a)', err=102, end=102) dummy
    write (stdout, '(a)') trim(dummy)

    read (file_unit, *, err=102, end=102) nw
    if (nw .ne. nxx) then
      call set_error_file(error, 'wrong matrix size in transport: read_htX', comm)
      return
    end if
    read (file_unit, *) ((h_00(i, j), i=1, nxx), j=1, nxx)
    read (file_unit, *, err=102, end=102) nw
    if (nw .ne. nxx) then
      call set_error_file(error, 'wrong matrix size in transport: read_htX', comm)
      return
    end if
    read (file_unit, *, err=102, end=102) ((h_01(i, j), i=1, nxx), j=1, nxx)

    close (unit=file_unit)

    return

101 call set_error_file(error, 'Error: Problem opening input file '//h_file, comm)
    return
102 call set_error_file(error, 'Error: Problem reading input file '//h_file, comm)
    return

  end subroutine tran_read_htX

  !================================================!
  subroutine tran_read_htC(nxx, h_00, h_file, stdout, error, comm)
    !================================================!

    use w90_constants, only: dp, maxlen
    use w90_error, only: w90_error_type, set_error_file, set_error_file

    implicit none

    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) ::  nxx
    integer, intent(in) ::  stdout
    real(kind=dp), intent(out) :: h_00(nxx, nxx)
    character(len=50), intent(in) :: h_file

    integer :: i, j, nw, file_unit, ierr
    character(len=maxlen) :: dummy

    open (newunit=file_unit, file=h_file, form='formatted', status='old', action='read', iostat=ierr)
    if (ierr /= 0) then
      call set_error_file(error, 'Error: Problem opening input file '//h_file, comm)
      return
    end if

    write (stdout, '(/a)', advance='no') ' Reading H matrix from '//h_file//'  : '

    read (file_unit, '(a)', err=102, end=102) dummy
    write (stdout, '(a)') trim(dummy)

    read (file_unit, *, err=102, end=102) nw
    if (nw .ne. nxx) then
      call set_error_file(error, 'wrong matrix size in transport: read_htC', comm)
      return
    end if
    read (file_unit, *, err=102, end=102) ((h_00(i, j), i=1, nxx), j=1, nxx)

    close (unit=file_unit)

    return
102 call set_error_file(error, 'Error: Problem reading input file '//h_file, comm)

  end subroutine tran_read_htC

  !================================================!
  subroutine tran_read_htXY(nxx1, nxx2, h_01, h_file, stdout, error, comm)
    !================================================!

    use w90_constants, only: dp, maxlen
    use w90_error, only: w90_error_type, set_error_file, set_error_file

    implicit none

    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) ::  nxx1, nxx2
    integer, intent(in) ::  stdout
    real(kind=dp), intent(out) :: h_01(nxx1, nxx2)
    character(len=50), intent(in) :: h_file

    integer :: i, j, nw1, nw2, file_unit
    character(len=maxlen) :: dummy

    open (newunit=file_unit, file=h_file, form='formatted', &
          status='old', action='read', err=101)

    write (stdout, '(/a)', advance='no') ' Reading H matrix from '//h_file//'  : '

    read (file_unit, '(a)', err=102, end=102) dummy
    write (stdout, '(a)') trim(dummy)

    read (file_unit, *, err=102, end=102) nw1, nw2

    if (nw1 .ne. nxx1 .or. nw2 .ne. nxx2) then
      call set_error_file(error, 'wrong matrix size in transport: read_htXY', comm)
      return
    end if

    read (file_unit, *, err=102, end=102) ((h_01(i, j), i=1, nxx1), j=1, nxx2)

    close (unit=file_unit)

    return

101 call set_error_file(error, 'Error: Problem opening input file '//h_file, comm)
    return
102 call set_error_file(error, 'Error: Problem reading input file '//h_file, comm)
    return

  end subroutine tran_read_htXY

!================================================
  subroutine tran_find_integral_signatures(signatures, num_G, print_output, real_lattice, &
                                           u_matrix_opt, u_matrix, num_bands, num_wann, &
                                           have_disentangled, wannier_centres_translated, stdout, &
                                           seedname, timer, error, comm)
    !================================================!
    ! Reads <seedname>.unkg file that contains the u_nk(G) and calculate
    ! Fourier components of each wannier function. Linear combinations of
    ! these provide integral of different spatial dependence.
    ! The set of these integrals provide a signature for distinguishing the
    ! type and 'parity' of each wannier function.
    !================================================!
    use w90_constants, only: dp, cmplx_0, twopi, cmplx_i
    use w90_io, only: io_date, io_stopwatch_start, io_stopwatch_stop
    use w90_types, only: print_output_type, timer_list_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_file, set_error_file, &
                         set_error_dealloc

    implicit none

    type(print_output_type), intent(in) :: print_output
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: num_bands
    integer, intent(in) :: num_wann
    integer, intent(in) :: stdout
    integer, intent(out) :: num_G
    complex(kind=dp), intent(in) :: u_matrix(:, :, :)
    complex(kind=dp), intent(in) :: u_matrix_opt(:, :, :)
    real(kind=dp), allocatable, intent(out) :: signatures(:, :)
    real(kind=dp), intent(in) :: real_lattice(3, 3)
    real(kind=dp), intent(in) :: wannier_centres_translated(:, :)
    character(len=50), intent(in)  :: seedname
    logical, intent(in) :: have_disentangled

    integer, allocatable :: g_abc(:, :)
    integer :: i, ibnd, file_unit, ierr, p, p_max, n, m, ig, a, b, c, ig_idx(32)
    real(kind=dp) :: i_unkg, r_unkg, wf_frac(3), det_rl, inv_t_rl(3, 3), mag_signature_sq
    complex(kind=dp), allocatable :: unkg(:, :), tran_u_matrix(:, :)
    complex(kind=dp) :: phase_factor, signature_basis(32)
    logical :: have_file

    if (print_output%timing_level > 1) call io_stopwatch_start('tran: find_sigs_unkg_int', timer)

    inquire (file=trim(seedname)//'.unkg', exist=have_file)
    if (.not. have_file) then
      call set_error_file(error, 'tran_hr_parity_unkg: file '//trim(seedname)// &
                          '.unkg not found', comm)
      return
    end if
    open (newunit=file_unit, file=trim(seedname)//'.unkg', form='formatted', action='read')

    write (stdout, '(3a)') ' Reading '//trim(seedname)//'.unkg  file'
    read (file_unit, *) num_G

    allocate (signatures(20, num_bands), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating signatures in tran_find_integral_signatures', comm)
      return
    end if
    allocate (unkg(num_G, num_bands), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating unkg in tran_find_integral_signatures', comm)
      return
    end if
    allocate (g_abc(num_G, 3), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating g_abc in tran_find_integral_signatures', comm)
      return
    end if
    if (have_disentangled) then
      allocate (tran_u_matrix(num_bands, num_wann), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating tran_u_matrix in tran_find_integral_signatures', comm)
        return
      end if
    else
      allocate (tran_u_matrix(num_wann, num_wann), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating tran_u_matrix in tran_find_integral_signatures', comm)
        return
      end if
    end if

    unkg = cmplx_0
    do m = 1, num_bands
      do i = 1, num_G
        read (file_unit, *) ibnd, ig, a, b, c, r_unkg, i_unkg
        if ((ig .ne. i) .OR. (ibnd .ne. m)) then
          call set_error_file(error, 'tran_find_integral_signatures: Incorrect bands or g vectors', comm)
          return
        end if
        unkg(i, m) = cmplx(r_unkg, i_unkg, kind=dp)
        g_abc(i, :) = (/a, b, c/)
      end do
    end do

    close (file_unit)

    ! Computing inverse of transpose of real_lattice

    det_rl = real_lattice(1, 1)*(real_lattice(2, 2)*real_lattice(3, 3) &
                                 - real_lattice(2, 3)*real_lattice(3, 2)) &
             - real_lattice(2, 2)*(real_lattice(2, 1)*real_lattice(3, 3) &
                                   - real_lattice(2, 3)*real_lattice(3, 1)) &
             + real_lattice(3, 3)*(real_lattice(2, 1)*real_lattice(3, 2) &
                                   - real_lattice(2, 2)*real_lattice(3, 1))

    inv_t_rl(1, 1) = (real_lattice(2, 2)*real_lattice(3, 3) - real_lattice(3, 2)*real_lattice(2, 3))
    inv_t_rl(1, 2) = (real_lattice(2, 1)*real_lattice(3, 3) - real_lattice(3, 1)*real_lattice(2, 3))
    inv_t_rl(1, 3) = (real_lattice(2, 1)*real_lattice(3, 2) - real_lattice(3, 1)*real_lattice(2, 2))

    inv_t_rl(2, 1) = (real_lattice(1, 2)*real_lattice(3, 3) - real_lattice(3, 2)*real_lattice(1, 3))
    inv_t_rl(2, 2) = (real_lattice(1, 1)*real_lattice(3, 3) - real_lattice(3, 1)*real_lattice(1, 3))
    inv_t_rl(2, 3) = (real_lattice(1, 1)*real_lattice(3, 2) - real_lattice(3, 1)*real_lattice(1, 2))

    inv_t_rl(3, 1) = (real_lattice(1, 2)*real_lattice(2, 3) - real_lattice(2, 2)*real_lattice(1, 3))
    inv_t_rl(3, 2) = (real_lattice(1, 1)*real_lattice(2, 3) - real_lattice(2, 1)*real_lattice(1, 3))
    inv_t_rl(3, 3) = (real_lattice(1, 1)*real_lattice(2, 2) - real_lattice(2, 1)*real_lattice(1, 2))

    inv_t_rl = inv_t_rl/det_rl

    !Loop over wannier functions to compute generalised U matrix

    signatures = 0.0_dp
    tran_u_matrix = cmplx_0
    do n = 1, num_wann

      !Disentanglement step

      if (have_disentangled) then
        do p = 1, num_bands
          do m = 1, num_wann
            tran_u_matrix(p, n) = tran_u_matrix(p, n) + u_matrix(m, n, 1)*u_matrix_opt(p, m, 1)
          end do
        end do
        p_max = num_bands
      else
        tran_u_matrix = u_matrix(:, :, 1)
        p_max = num_wann
      end if
    end do

    if (print_output%iprint .ge. 5) write (stdout, *) 'Printing integral signatures for each &
      &wannier function:'

    ! Loop over all wannier functions

    do n = 1, num_wann

      ! Find fraction coordinate of wannier function in lattice vector basis
      ! wf_frac(:)=(transpose(real_lattice))^(-1) * wannier_centres_translated(:,n)

      wf_frac = 0.0_dp
      wf_frac = matmul(inv_t_rl, wannier_centres_translated(:, n))

      ! Loop over all g vectors, find a,b,c's required

      do ig = 1, num_G
        ! 0th Order
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(1) = ig   ! 1
        ! 1st Order
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(2) = ig   ! x
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(3) = ig   ! y
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 1)) ig_idx(4) = ig   ! z
        ! 2nd Order
        if ((g_abc(ig, 1) .eq. 2) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(5) = ig   ! x^2
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(6) = ig   ! xy
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. -1) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(7) = ig   ! xy
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 1)) ig_idx(8) = ig   ! xz
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. -1)) ig_idx(9) = ig   ! xz
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 2) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(10) = ig  ! y^2
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. 1)) ig_idx(11) = ig  ! yz
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. -1)) ig_idx(12) = ig  ! yz
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 2)) ig_idx(13) = ig  ! z^2
        ! 3rd Order
        if ((g_abc(ig, 1) .eq. 3) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(14) = ig  ! x^3
        if ((g_abc(ig, 1) .eq. 2) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(15) = ig  ! x^2y
        if ((g_abc(ig, 1) .eq. 2) .and. (g_abc(ig, 2) .eq. -1) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(16) = ig  ! x^2y
        if ((g_abc(ig, 1) .eq. 2) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 1)) ig_idx(17) = ig  ! x^2z
        if ((g_abc(ig, 1) .eq. 2) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. -1)) ig_idx(18) = ig  ! x^2z
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 2) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(19) = ig  ! xy^2
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. -2) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(20) = ig  ! xy^2
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. 1)) ig_idx(21) = ig  ! xyz
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. -1)) ig_idx(22) = ig  ! xyz
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. -1) .and. (g_abc(ig, 3) .eq. 1)) ig_idx(23) = ig  ! xyz
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. -1) .and. (g_abc(ig, 3) .eq. -1)) ig_idx(24) = ig  ! xyz
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 2)) ig_idx(25) = ig  ! xz^2
        if ((g_abc(ig, 1) .eq. 1) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. -2)) ig_idx(26) = ig  ! xz^2
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 3) .and. (g_abc(ig, 3) .eq. 0)) ig_idx(27) = ig  ! y^3
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 2) .and. (g_abc(ig, 3) .eq. 1)) ig_idx(28) = ig  ! y^2z
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 2) .and. (g_abc(ig, 3) .eq. -1)) ig_idx(29) = ig  ! y^2z
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. 2)) ig_idx(30) = ig  ! yz^2
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 1) .and. (g_abc(ig, 3) .eq. -2)) ig_idx(31) = ig  ! yz^2
        if ((g_abc(ig, 1) .eq. 0) .and. (g_abc(ig, 2) .eq. 0) .and. (g_abc(ig, 3) .eq. 3)) ig_idx(32) = ig  ! z^3
      end do

      ! Loop over the 32 required g-vectors

      signature_basis = cmplx_0
      do ig = 1, 32
        phase_factor = cmplx_0

        ! Compute the phase factor exp(-i*G*x_c)

        phase_factor = exp(-twopi*cmplx_i*(g_abc(ig_idx(ig), 1)*wf_frac(1) &
                                           + g_abc(ig_idx(ig), 2)*wf_frac(2) &
                                           + g_abc(ig_idx(ig), 3)*wf_frac(3)))

        ! Compute integrals that form the basis of the spatial integrals that form the signature
        do p = 1, p_max
          signature_basis(ig) = signature_basis(ig) + tran_u_matrix(p, n)*conjg(unkg(ig_idx(ig), p))
        end do
        signature_basis(ig) = signature_basis(ig)*phase_factor
      end do

      ! Definitions of the signature integrals

      ! 0th Order
      signatures(1, n) = real(signature_basis(1))                                                    ! 1
      ! 1st Order
      signatures(2, n) = aimag(signature_basis(2))                                                   ! x
      signatures(3, n) = aimag(signature_basis(3))                                                   ! y
      signatures(4, n) = aimag(signature_basis(4))                                                   ! z
      ! 2nd Orde r
      signatures(5, n) = real(signature_basis(1) - signature_basis(5))/2                             ! x^2
      signatures(6, n) = real(signature_basis(7) - signature_basis(6))/2                             ! xy
      signatures(7, n) = real(signature_basis(9) - signature_basis(8))/2                             ! xz
      signatures(8, n) = real(signature_basis(1) - signature_basis(10))/2                            ! y^2
      signatures(9, n) = real(signature_basis(12) - signature_basis(11))/2                           ! yz
      signatures(10, n) = real(signature_basis(1) - signature_basis(13))/2                           ! z^2
      ! 3rd Order
      signatures(11, n) = aimag(3*signature_basis(2) - signature_basis(14))/4                        ! x^3
      signatures(12, n) = aimag(2*signature_basis(3) + signature_basis(16) - signature_basis(15))/4  ! x^2y
      signatures(13, n) = aimag(2*signature_basis(4) + signature_basis(18) - signature_basis(17))/4  ! x^2z
      signatures(14, n) = aimag(2*signature_basis(2) - signature_basis(20) - signature_basis(19))/4  ! xy^2
      signatures(15, n) = aimag(signature_basis(23) + signature_basis(22) - signature_basis(21) &
                                - signature_basis(24))/4                                             ! xyz
      signatures(16, n) = aimag(2*signature_basis(2) - signature_basis(26) - signature_basis(25))/4  ! xz^2
      signatures(17, n) = aimag(3*signature_basis(3) - signature_basis(27))/4                        ! y^3
      signatures(18, n) = aimag(2*signature_basis(4) + signature_basis(29) - signature_basis(28))/4  ! y^2z
      signatures(19, n) = aimag(2*signature_basis(3) - signature_basis(31) - signature_basis(30))/4  ! yz^2
      signatures(20, n) = aimag(3*signature_basis(4) - signature_basis(32))/4                        ! z^3

      if (print_output%iprint .ge. 5) then
        write (stdout, *) ' '
        write (stdout, *) ' Wannier function: ', n
        do ig = 1, 20
          write (stdout, *) ig - 1, signatures(ig, n)
        end do
      end if

      !Normalise signature of each wannier function to a unit vector

      mag_signature_sq = 0.0_dp
      do ig = 1, 20
        mag_signature_sq = mag_signature_sq + abs(signatures(ig, n))**2
      end do
      signatures(:, n) = signatures(:, n)/sqrt(mag_signature_sq)

    end do ! Wannier Function loop

    ! Set num_G = 20 to ensure later subroutines work correctly

    num_G = 20

    deallocate (tran_u_matrix, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating tran_u_matrix in tran_find_integral_signatures', comm)
      return
    end if
    deallocate (g_abc, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating g_abc in tran_find_integral_signatures', comm)
      return
    end if
    deallocate (unkg, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating unkg in tran_find_integral_signatures', comm)
      return
    end if

    if (print_output%timing_level > 1) call io_stopwatch_stop('tran: find_sigs_unkg_int', timer)

    return

  end subroutine tran_find_integral_signatures

  !================================================!
  subroutine tran_lcr_2c2_sort(signatures, num_G, pl_warning, transport, atom_data, wannier_data, &
                               real_space_ham, print_output, real_lattice, num_wann, mp_grid, &
                               ham_r, irvec, nrpts, wannier_centres_translated, one_dim_vec, &
                               nrpts_one_dim, num_pl, coord, tran_sorted_idx, hr_one_dim, &
                               irvec_max, write_xyz, stdout, seedname, timer, error, comm)
    !================================================!
    ! This is the main subroutine controling the sorting
    ! for the 2c2 geometry. We first sort in the conduction
    ! direction, group, sort in 2nd direction, group and
    ! sort in 3rd direction. Rigourous checks are performed
    ! to ensure group and subgroup structure is consistent
    ! between principal layers (PLs), and unit cells. Once
    ! checks are passed we consider the possibility of
    ! multiple wannier functions are of similar centre, and
    ! sort those
    !================================================!

    use w90_constants, only: dp
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop
    use w90_types, only: wannier_data_type, atom_data_type, print_output_type, timer_list_type
    use w90_wannier90_types, only: transport_type, real_space_ham_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc, set_error_fatal

    implicit none

    ! arguments
    integer, intent(in) :: irvec(:, :)
    integer, intent(inout) :: irvec_max ! maybe modified
    integer, intent(in) :: mp_grid(3)
    integer, intent(in) :: num_G
    integer, intent(in) :: num_wann
    integer, intent(inout), allocatable :: tran_sorted_idx(:)
    integer, intent(inout) :: coord(3)
    integer, intent(inout) :: nrpts
    integer, intent(inout) :: nrpts_one_dim
    integer, intent(inout) :: num_pl
    integer, intent(inout) :: one_dim_vec
    integer, intent(in) :: stdout

    real(kind=dp), intent(in) :: signatures(:, :)
    real(kind=dp), allocatable, intent(inout) :: hr_one_dim(:, :, :)
    real(kind=dp), intent(inout) :: wannier_centres_translated(:, :)
    real(kind=dp), intent(in) :: real_lattice(3, 3)

    complex(kind=dp), intent(in) :: ham_r(:, :, :)

    type(atom_data_type), intent(in) :: atom_data
    type(real_space_ham_type), intent(inout) :: real_space_ham
    type(print_output_type), intent(in) :: print_output
    type(transport_type), intent(inout) :: transport
    type(wannier_data_type), intent(in) :: wannier_data
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    character(len=50), intent(in)  :: seedname

    logical, intent(in) :: write_xyz
    logical, intent(out) :: pl_warning

    ! local variables
    real(kind=dp), dimension(2, num_wann) :: centres_non_sorted, centres_initial_sorted
    real(kind=dp), dimension(2, transport%num_ll) :: PL1, PL2, PL3, PL4, PL
    real(kind=dp), dimension(2, num_wann - (4*transport%num_ll)) :: central_region
    real(kind=dp) :: reference_position, cell_length, distance, PL_max_val, PL_min_val

    integer :: i, j, k, PL_selector, sort_iterator, sort_iterator2
    integer :: ierr, temp_coord_2, temp_coord_3, n
    integer :: num_wann_cell_ll, num_wf_group1, num_wf_last_group
    integer, allocatable :: PL_groups(:), PL1_groups(:), PL2_groups(:), PL3_groups(:), &
                            PL4_groups(:), central_region_groups(:)
    integer, allocatable :: PL_subgroup_info(:, :), PL1_subgroup_info(:, :), &
                            PL2_subgroup_info(:, :), PL3_subgroup_info(:, :), PL4_subgroup_info(:, :), &
                            central_subgroup_info(:, :), temp_subgroup(:, :)

    character(30) :: fmt_1

    allocate (tran_sorted_idx(num_wann), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tran_sorted_idx in tran_lcr_2c2_sort', comm)
      return
    end if

    num_wann_cell_ll = transport%num_ll/transport%num_cell_ll

    if (print_output%timing_level > 1) call io_stopwatch_start('tran: lcr_2c2_sort', timer)

    sort_iterator = 0

    !Check translated centres have been found

    if (size(wannier_centres_translated) .eq. 0) then
      call set_error_fatal(error, 'Translated centres not known : required perform lcr transport, &
                          &try restart=plot', comm)
      return
    end if

    !read one_dim_dir and creates an array (coord) that correspond to the
    !conduction direction (coord(1)) and the two perpendicular directions
    !(coord(2),coord(3)), such that a right-handed set is formed

    if (real_space_ham%one_dim_dir .eq. 1) then
      coord(1) = 1
      coord(2) = 2
      coord(3) = 3
    elseif (real_space_ham%one_dim_dir .eq. 2) then
      coord(1) = 2
      coord(2) = 3
      coord(3) = 1
    elseif (real_space_ham%one_dim_dir .eq. 3) then
      coord(1) = 3
      coord(2) = 1
      coord(3) = 2
    end if

    !Check

    if (((real_lattice(coord(1), coord(2)) .ne. 0) .or. (real_lattice(coord(1), coord(3)) .ne. 0)) .or. &
        ((real_lattice(coord(2), coord(1)) .ne. 0) .or. (real_lattice(coord(3), coord(1)) .ne. 0))) then
      call set_error_fatal(error, &
      'Lattice vector in conduction direction must point along x,y or z &
      & direction and be orthogonal to the remaining lattice vectors.', comm)
      return
    end if

    !Check

    if (num_wann .le. 4*transport%num_ll) then
      call set_error_fatal(error, 'Principle layers are too big.', comm)
      return
    end if

100 continue

    !Extract a 2d array of the wannier_indices and their coord(1) from wannier_centers_translated

    do i = 1, num_wann
      centres_non_sorted(1, i) = i
      centres_non_sorted(2, i) = wannier_centres_translated(coord(1), i)
    end do
    write (stdout, '(/a)') ' Sorting WFs into principal layers'

    !Initial sorting according to coord(1).

    call sort(centres_non_sorted, centres_initial_sorted)

    !Extract principal layers. WARNING: This extraction implies the structure of the supercell is
    !2 principal layers of lead on the left and on the right of a central conductor.

    PL1 = centres_initial_sorted(:, 1:transport%num_ll)
    PL2 = centres_initial_sorted(:, transport%num_ll + 1:2*transport%num_ll)
    PL3 = centres_initial_sorted(:, num_wann - (2*transport%num_ll - 1):num_wann - (transport%num_ll))
    PL4 = centres_initial_sorted(:, num_wann - (transport%num_ll - 1):)

    if (sort_iterator .eq. 1) then
      temp_coord_2 = coord(2)
      temp_coord_3 = coord(3)
      coord(2) = temp_coord_3
      coord(3) = temp_coord_2
    end if

    if (print_output%iprint .ge. 4) then
      write (stdout, *) ' Group Breakdown of each principal layer'
    end if

    !Loop over principal layers

    do i = 1, 4

      !Creating a variable PL_selector which choose the appropriate PL

      PL_selector = i
      select case (PL_selector)
      case (1)
        PL = PL1
      case (2)
        PL = PL2
      case (3)
        PL = PL3
      case (4)
        PL = PL4
      end select

      !Grouping wannier functions with similar coord(1)

      call group(PL, PL_groups, transport%group_threshold, error, comm)
      if (allocated(error)) return

      if (print_output%iprint .ge. 4) then

        !Print group breakdown

        write (fmt_1, '(i5)') size(PL_groups)
        fmt_1 = adjustl(fmt_1)
        fmt_1 = '(a3,i1,a1,i5,a2,'//trim(fmt_1)//'i4,a1)'
        write (stdout, fmt_1) ' PL', i, ' ', size(PL_groups), ' (', (PL_groups(j), j=1, &
                                                                     size(PL_groups)), ')'
      end if

      !Returns the sorted PL and informations on this PL

      allocate (PL_subgroup_info(size(PL_groups), maxval(PL_groups)), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating PL_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      call master_sort_and_group(PL, PL_groups, PL_subgroup_info, transport%group_threshold, &
                                 print_output, wannier_centres_translated, coord, stdout, timer, &
                                 error, comm)
      if (allocated(error)) return

      select case (PL_selector)
      case (1)
        allocate (PL1_groups(size(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL1_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        allocate (PL1_subgroup_info(size(PL_groups), maxval(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL1_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if

        PL1 = PL
        PL1_groups = PL_groups
        PL1_subgroup_info = PL_subgroup_info

        deallocate (PL_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL1_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
      case (2)
        allocate (PL2_groups(size(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL2_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        allocate (PL2_subgroup_info(size(PL_groups), maxval(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL2_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if

        PL2 = PL
        PL2_groups = PL_groups
        PL2_subgroup_info = PL_subgroup_info

        deallocate (PL_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL2_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
      case (3)
        allocate (PL3_groups(size(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL3_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        allocate (PL3_subgroup_info(size(PL_groups), maxval(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL3_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if

        PL3 = PL
        PL3_groups = PL_groups
        PL3_subgroup_info = PL_subgroup_info

        deallocate (PL_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL3_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
      case (4)
        allocate (PL4_groups(size(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL4_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        allocate (PL4_subgroup_info(size(PL_groups), maxval(PL_groups)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating PL4_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if

        PL4 = PL
        PL4_groups = PL_groups
        PL4_subgroup_info = PL_subgroup_info

        deallocate (PL_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL4_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
      end select

      deallocate (PL_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL_groups in tran_lcr_2c2_sort', comm)
        return
      end if
    end do ! Principal layer loop

    !Grouping and sorting of central conductor region

    !Define central region

    central_region = centres_initial_sorted(:, 2*transport%num_ll + 1:num_wann - (2*transport%num_ll))

    !Group central region

    call group(central_region, central_region_groups, transport%group_threshold, error, comm)
    if (allocated(error)) return

    !Print central region group breakdown

    if (print_output%iprint .ge. 4) then
      write (stdout, *) ' Group Breakdown of central region'
      write (fmt_1, '(i5)') size(central_region_groups)
      fmt_1 = adjustl(fmt_1)
      fmt_1 = '(a5,i5,a2,'//trim(fmt_1)//'i4,a1)'
      write (stdout, fmt_1) '     ', size(central_region_groups), ' (', &
        (central_region_groups(j), j=1, size(central_region_groups)), ')'
    end if

    !Returns sorted central group region

    allocate (central_subgroup_info(size(central_region_groups), maxval(central_region_groups)), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating central_group_info in tran_lcr_2c2_sort', comm)
      return
    end if
    call master_sort_and_group(central_region, central_region_groups, central_subgroup_info, &
                               transport%group_threshold, print_output, &
                               wannier_centres_translated, coord, stdout, timer, error, comm)
    if (allocated(error)) return
    deallocate (central_subgroup_info, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating central_group_info in tran_lcr_2c2_sort', comm)
      return
    end if
    write (stdout, *) ' '

    !Build the sorted index array

    tran_sorted_idx = nint(centres_initial_sorted(1, :))
    tran_sorted_idx(1:transport%num_ll) = nint(PL1(1, :))
    tran_sorted_idx(transport%num_ll + 1:2*transport%num_ll) = nint(PL2(1, :))
    tran_sorted_idx(2*transport%num_ll + 1:num_wann - (2*transport%num_ll)) = nint(central_region(1, :))
    tran_sorted_idx(num_wann - (2*transport%num_ll - 1):num_wann - (transport%num_ll)) = nint(PL3(1, :))
    tran_sorted_idx(num_wann - (transport%num_ll - 1):) = nint(PL4(1, :))

    sort_iterator = sort_iterator + 1

    !Checks:

    if ((size(PL1_groups) .ne. size(PL2_groups)) .or. &
        (size(PL2_groups) .ne. size(PL3_groups)) .or. &
        (size(PL3_groups) .ne. size(PL4_groups))) then
      if (sort_iterator .ge. 2) then
        if (write_xyz) call tran_write_xyz(atom_data, transport, wannier_centres_translated, &
                                           tran_sorted_idx, num_wann, seedname, stdout)
        call set_error_fatal(error, 'Sorting techniques exhausted:&
          & Inconsistent number of groups among principal layers', comm)
        return
      end if
      if (print_output%iprint > 0) write (stdout, *) 'Inconsistent number of groups among principal layers: restarting sorting...'
      deallocate (PL1_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL1_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL2_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL2_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL3_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL3_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL4_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL4_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL1_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL1_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL2_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL2_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL3_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL3_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL4_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL4_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      goto 100
    end if

    do i = 1, size(PL1_groups)
      if ((PL1_groups(i) .ne. PL2_groups(i)) .or. &
          (PL2_groups(i) .ne. PL3_groups(i)) .or. &
          (PL3_groups(i) .ne. PL4_groups(i))) then
        if (sort_iterator .ge. 2) then
          if (write_xyz) call tran_write_xyz(atom_data, transport, wannier_centres_translated, &
                                             tran_sorted_idx, num_wann, seedname, stdout)
          call set_error_fatal(error, &
           'Sorting techniques exhausted: Inconsitent number of wannier function among &
             & similar groups within principal layers', comm)
          return
        end if
        write (stdout, *) 'Inconsitent number of wannier function among &
          &similar groups within& principal layers: restarting sorting...'

        deallocate (PL1_groups, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL1_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        deallocate (PL2_groups, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL2_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        deallocate (PL3_groups, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL3_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        deallocate (PL4_groups, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL4_groups in tran_lcr_2c2_sort', comm)
          return
        end if
        deallocate (PL1_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL1_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
        deallocate (PL2_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL2_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
        deallocate (PL3_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL3_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
        deallocate (PL4_subgroup_info, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating PL4_subgroup_info in tran_lcr_2c2_sort', comm)
          return
        end if
        goto 100
      end if
    end do

    !Now we check that the leftmost group and the rightmost group aren't
    !supposed to be the same group

    reference_position = wannier_centres_translated(coord(1), tran_sorted_idx(1))
    cell_length = real_lattice(coord(1), coord(1))
    sort_iterator2 = 1
    do i = 1, transport%num_ll
      distance = abs(abs(reference_position - wannier_centres_translated(coord(1), tran_sorted_idx(num_wann - i + 1))) &
                     - cell_length)
      if (distance .lt. transport%group_threshold) then
        wannier_centres_translated(coord(1), tran_sorted_idx(num_wann - i + 1)) = &
          wannier_centres_translated(coord(1), tran_sorted_idx(num_wann - i + 1)) - cell_length
        sort_iterator2 = sort_iterator2 + 1
      end if
    end do

    if (sort_iterator2 .gt. 1) then
      write (stdout, *) ' Grouping inconsistency found between first and last unit cells: '
      write (stdout, *) ' suspect Wannier functions have been translated. '
      write (stdout, *) ' '
      write (stdout, *) ' Rebuilding Hamiltonian...'
      write (stdout, *) ' '
      deallocate (hr_one_dim, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating hr_one_dim in tran_lcr_2c2_sort', comm)
        return
      end if
      call tran_reduce_hr(real_space_ham, ham_r, hr_one_dim, real_lattice, irvec, mp_grid, &
                          irvec_max, nrpts, nrpts_one_dim, num_wann, one_dim_vec, &
                          print_output%timing_level, stdout, timer, error, comm)
      if (allocated(error)) return

      call tran_cut_hr_one_dim(real_space_ham, transport, print_output, hr_one_dim, real_lattice, &
                               wannier_centres_translated, mp_grid, irvec_max, num_pl, num_wann, &
                               one_dim_vec, stdout, timer)

      write (stdout, *) ' '
      write (stdout, *) ' Restarting sorting...'
      write (stdout, *) ' '
      sort_iterator = sort_iterator - 1
      deallocate (PL1_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL1_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL2_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL2_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL3_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL3_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL4_groups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL4_groups in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL1_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL1_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL2_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL2_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL3_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL3_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      deallocate (PL4_subgroup_info, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating PL4_subgroup_info in tran_lcr_2c2_sort', comm)
        return
      end if
      goto 100
    end if

    ! if we reach this point, we don't have any left/right problems anymore. So we now
    ! check for inconsistencies in subgroups

    allocate (temp_subgroup(size(PL1_subgroup_info, 1), size(PL1_subgroup_info, 2)), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tmp_subgroup in tran_lcr_2c2_sort', comm)
      return
    end if
    do i = 2, 4
      select case (i)
      case (2)
        temp_subgroup = PL1_subgroup_info - PL2_subgroup_info
      case (3)
        temp_subgroup = PL1_subgroup_info - PL3_subgroup_info
      case (4)
        temp_subgroup = PL1_subgroup_info - PL4_subgroup_info
      end select
      do j = 1, size(temp_subgroup, 1)
        do k = 1, size(temp_subgroup, 2)
          if (temp_subgroup(j, k) .ne. 0) then
            if (sort_iterator .ge. 2) then
              if (write_xyz) call tran_write_xyz(atom_data, transport, &
                                                 wannier_centres_translated, &
                                                 tran_sorted_idx, num_wann, seedname, &
                                                 stdout)
              call set_error_fatal(error, &
                                   'Sorting techniques exhausted: Inconsitent subgroup structures among principal layers', comm)
              return
            end if
            if (print_output%iprint > 0) &
              write (stdout, *) 'Inconsistent subgroup structure among principal layers: restarting sorting...'
            deallocate (temp_subgroup, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating tmp_subgroup in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL1_groups, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL1_groups in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL2_groups, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL2_groups in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL3_groups, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL3_groups in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL4_groups, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL4_groups in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL1_subgroup_info, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL1_subgroup_info in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL2_subgroup_info, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL2_subgroup_info in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL3_subgroup_info, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL3_subgroup_info in tran_lcr_2c2_sort', comm)
              return
            end if
            deallocate (PL4_subgroup_info, stat=ierr)
            if (ierr /= 0) then
              call set_error_dealloc(error, 'Error deallocating PL4_subgroup_info in tran_lcr_2c2_sort', comm)
              return
            end if
            goto 100
          end if
        end do
      end do
    end do

    ! At this point, every check has been cleared, and we need to use
    ! the parity signatures of the WFs for the possibility of equal centres

    call check_and_sort_similar_centres(signatures, num_G, atom_data, transport, print_output, &
                                        num_wann, wannier_centres_translated, coord, &
                                        tran_sorted_idx, write_xyz, stdout, seedname, timer, error, comm)
    if (allocated(error)) return

    write (stdout, *) ' '
    write (stdout, *) '------------------------- Sorted Wannier Centres -----------------------------'
    write (stdout, *) 'Sorted index   Unsorted index           x           y           z     Spread  '
    write (stdout, *) '==================================PL1========================================='
    n = 0
    do k = 1, 4
      if (k .eq. 2) write (stdout, *) '==================================PL2========================================='
      if (k .eq. 3) then
        write (stdout, *) '===========================Central Region==================================='
        do i = 1, num_wann - 4*transport%num_ll
          n = n + 1
          write (stdout, FMT='(2x,i6,10x,i6,6x,4F12.6)') n, tran_sorted_idx(n), &
            wannier_centres_translated(1, tran_sorted_idx(n)), &
            wannier_centres_translated(2, tran_sorted_idx(n)), &
            wannier_centres_translated(3, tran_sorted_idx(n)), &
            wannier_data%spreads(tran_sorted_idx(n))*print_output%lenconfac**2
        end do
        write (stdout, *) '==================================PL3========================================='
      end if
      if (k .eq. 4) write (stdout, *) '==================================PL4========================================='
      do i = 1, transport%num_cell_ll
        do j = 1, num_wann_cell_ll
          n = n + 1
          write (stdout, FMT='(2x,i6,10x,i6,6x,4F12.6)') n, tran_sorted_idx(n), &
            wannier_centres_translated(1, tran_sorted_idx(n)), &
            wannier_centres_translated(2, tran_sorted_idx(n)), &
            wannier_centres_translated(3, tran_sorted_idx(n)), &
            wannier_data%spreads(tran_sorted_idx(n))*print_output%lenconfac**2
        end do
        if (i .ne. transport%num_cell_ll) write (stdout, *) '---------------------&
          &---------------------------------------------------------'

      end do
    end do

    write (stdout, *) '=============================================================================='
    write (stdout, *) ' '

    ! MS: Use sorting to assess whether dist_cutoff is suitable for correct PL cut
    !     by using limits of coord(1) values in 1st and last groups of PL1, & 1st group of PL2

    pl_warning = .false.
    num_wf_group1 = size(PL1_subgroup_info(1, :))
    if (size(PL1_groups) .ge. 1) then
      num_wf_last_group = size(PL1_subgroup_info(size(PL1_groups), :))
    else

      !Exception for 1 group in unit cell.
      num_wf_last_group = num_wann_cell_ll
    end if
    PL_min_val = maxval(wannier_centres_translated(coord(1), tran_sorted_idx(transport%num_ll &
                                                                             - num_wf_last_group + 1:transport%num_ll))) &
                 - minval(wannier_centres_translated(coord(1), tran_sorted_idx(1:num_wf_group1)))
    PL_max_val = minval(wannier_centres_translated(coord(1), tran_sorted_idx(transport%num_ll + &
                                                                             1:transport%num_ll + num_wf_group1))) &
                 - minval(wannier_centres_translated(coord(1), tran_sorted_idx(1:num_wf_group1)))
    if ((real_space_ham%dist_cutoff .lt. PL_min_val) .or. (real_space_ham%dist_cutoff .gt. PL_max_val)) then
      write (stdout, '(a)') ' WARNING: Expected dist_cutoff to be a PL length, I think this'
      write (stdout, '(2(a,f10.6),a)') ' WARNING: is somewhere between ', PL_min_val, ' and ', PL_max_val, ' Ang'
      pl_warning = .true.
    end if
    ! End MS.

    if (print_output%timing_level > 1) call io_stopwatch_stop('tran: lcr_2c2_sort', timer)

    return

  end subroutine tran_lcr_2c2_sort

  !================================================!
  subroutine master_sort_and_group(Array, Array_groups, subgroup_info, tran_group_threshold, &
                                   print_output, wannier_centres_translated, coord, stdout, timer, &
                                   error, comm)
    !================================================!
    ! General sorting and grouping subroutine which takes Array,
    ! an ordered in conduction direction array of wannier function
    ! indexes and positions, and returns the ordered (and grouped)
    ! indexes and positions after considering the other two
    ! directions. Sub group info is also return for later checks.
    !================================================!

    use w90_constants, only: dp
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop
    use w90_types, only: print_output_type, timer_list_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc

    implicit none

    ! arguments
    type(print_output_type), intent(in) :: print_output
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    integer, intent(in) :: stdout !, Array_size => size(Array_groups) so not needed
    integer, intent(in) :: Array_groups(:), coord(3)
    integer, intent(out), allocatable :: subgroup_info(:, :)

    real(kind=dp), intent(inout) :: Array(:, :) !(2, Array_size)
    real(kind=dp), intent(in) :: tran_group_threshold, wannier_centres_translated(:, :)

    ! local variables
    integer :: i, j, k, Array_num_groups, increment, ierr, subgroup_increment, group_num_subgroups
    integer, allocatable :: group_subgroups(:)
    real(kind=dp), allocatable :: group_array(:, :), sorted_group_array(:, :)
    real(kind=dp), allocatable :: subgroup_array(:, :), sorted_subgroup_array(:, :)
    character(30) :: fmt_2

    if (print_output%timing_level > 2) call io_stopwatch_start('tran: lcr_2c2_sort: master_sort', timer)

    allocate (subgroup_info(size(Array_groups), maxval(Array_groups)), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating subgroup_info in master_sort_and_group', comm)
      return
    end if
    subgroup_info = 0

    !Number of groups inside the principal layer

    Array_num_groups = size(Array_groups)

    !Convenient variable which will be amended later. Used to appropriately extract the group array from the Array

    increment = 1

    !Loop over groups inside Array

    do j = 1, Array_num_groups
      allocate (group_array(2, Array_groups(j)), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating group_array in master_sort_and_group', comm)
        return
      end if
      allocate (sorted_group_array(2, Array_groups(j)), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating sorted_group_array in master_sort_and_group', comm)
        return
      end if

      !Extract the group from the Array

      group_array = Array(:, increment:increment + Array_groups(j) - 1)

      !Updating group_array to contain coord(2)

      do k = 1, Array_groups(j)
        group_array(2, k) = wannier_centres_translated(coord(2), int(group_array(1, k)))
      end do

      call sort(group_array, sorted_group_array)
      call group(sorted_group_array, group_subgroups, tran_group_threshold, error, comm)
      if (allocated(error)) return

      group_num_subgroups = size(group_subgroups)

      if (print_output%iprint .ge. 4) then

        !Printing subgroup breakdown

        write (fmt_2, '(i5)') group_num_subgroups
        fmt_2 = adjustl(fmt_2)
        fmt_2 = '(a7,i3,a1,i5,a2,'//trim(fmt_2)//'i4,a1)'
        write (stdout, fmt_2) ' Group ', j, ' ', group_num_subgroups, ' (', (group_subgroups(i), i=1, group_num_subgroups), ')'
      end if

      ! filling up subgroup_info

      do k = 1, group_num_subgroups
        subgroup_info(j, k) = group_subgroups(k)
      end do

      !Convenient variable which will be amended later. Used to appropriately extract the subgroup array from the group_array

      subgroup_increment = 1

      !Loop over subgroups inside group

      do k = 1, group_num_subgroups
        allocate (subgroup_array(2, group_subgroups(k)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating subgroup_array in master_sort_and_group', comm)
          return
        end if
        allocate (sorted_subgroup_array(2, group_subgroups(k)), stat=ierr)
        if (ierr /= 0) then
          call set_error_alloc(error, 'Error in allocating sorted_subgroup_array in master_sort_and_group', comm)
          return
        end if

        !Extract the subgroup from the group

        subgroup_array = sorted_group_array(:, subgroup_increment:subgroup_increment + group_subgroups(k) - 1)

        !Updating subgroup_array to contain coord(3)

        do i = 1, group_subgroups(k)
          subgroup_array(2, i) = wannier_centres_translated(coord(3), int(subgroup_array(1, i)))
        end do

        call sort(subgroup_array, sorted_subgroup_array)

        !Update sorted_group array with the sorted subgroup array

        sorted_group_array(:, subgroup_increment:subgroup_increment + group_subgroups(k) - 1) = sorted_subgroup_array

        !Update the subgroup_increment

        subgroup_increment = subgroup_increment + group_subgroups(k)
        deallocate (sorted_subgroup_array, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating sorted_subgroup_array in master_sort_and_group', comm)
          return
        end if
        deallocate (subgroup_array, stat=ierr)
        if (ierr /= 0) then
          call set_error_dealloc(error, 'Error deallocating subgroup_array in master_sort_and_group', comm)
          return
        end if
      end do

      !Update Array with the sorted group array

      Array(:, increment:increment + Array_groups(j) - 1) = sorted_group_array

      !Update the group increment

      increment = increment + Array_groups(j)
      deallocate (group_array, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating group_array in master_sort_and_group', comm)
        return
      end if
      deallocate (sorted_group_array, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating sorted_group_array in master_sort_and_group', comm)
        return
      end if
      deallocate (group_subgroups, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating group_subgroups in master_sort_and_group', comm)
        return
      end if
    end do

    if (print_output%timing_level > 2) call io_stopwatch_stop('tran: lcr_2c2_sort: master_sort', timer)

    return

  end subroutine master_sort_and_group

  !================================================!
  subroutine sort(non_sorted, sorted)
    !================================================!

    use w90_constants, only: dp

    implicit none

    real(kind=dp), intent(inout) :: non_sorted(:, :)
    real(kind=dp), intent(out) :: sorted(:, :)

    integer, dimension(1) :: min_loc
    integer :: num_col, i

    num_col = size(non_sorted, 2)

    do i = 1, num_col

      !look for the location of the minimum value of the coordinates in non_sorted

      min_loc = minloc(non_sorted(2, :))

      !now the index in the first row of sorted is the index non_sorted(1,min_loc)

      sorted(1, i) = non_sorted(1, min_loc(1))

      !here is the corresponding coordinate

      sorted(2, i) = non_sorted(2, min_loc(1))

      !here one replaces the minimum coordinate with 10**10 such that this value
      !will not be picked-up again by minloc

      non_sorted(2, min_loc(1)) = 10.0**10
    end do

    return

  end subroutine sort

  !================================================!
  subroutine group(array, array_groups, tran_group_threshold, error, comm)
    !================================================!

    use w90_constants, only: dp
    use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc

    implicit none

    ! arguments
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    real(kind=dp), intent(in) :: tran_group_threshold
    real(kind=dp), intent(in) :: array(:, :)
    integer, intent(out), allocatable :: array_groups(:)

    ! local variables
    integer, allocatable :: dummy_array(:)
    integer :: array_idx, i, j, group_number, array_size, ierr
    logical, allocatable :: logic(:)

    array_size = size(array, 2)

    allocate (dummy_array(array_size), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating dummy_array in group', comm)
      return
    end if
    allocate (logic(array_size), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating logic in group', comm)
      return
    end if

    !Initialise dummy array
    dummy_array = 0

    !Initialise logic to false
    logic = .false.

    !Define counter of number of groups
    array_idx = 1

    !Loop over columns of array (ie array_size)
    do i = 1, array_size

      !If an element of logic is true then it means the wannier function has already been grouped
      if (logic(i) .eqv. .false.) then

        !Create a group for the wannier function
        logic(i) = .true.

        !Initialise the number of wannier functions in this group to be 1
        group_number = 1

        !Loop over the rest of wannier functions in array
        do j = min(i + 1, array_size), array_size

          !Special termination cases
          if ((j .eq. 1) .or. (i .eq. array_size)) then
            dummy_array(array_idx) = group_number
            exit
          end if
          if (j .eq. array_size .and. (abs(array(2, j) - array(2, i)) .le. tran_group_threshold)) then
            group_number = group_number + 1
            dummy_array(array_idx) = group_number
            logic(j) = .true.
            exit
          end if

          !Check distance between wannier function_i and wannier function_j
          if (abs(array(2, j) - array(2, i)) .le. tran_group_threshold) then

            !Increment number of wannier functions in group

            group_number = group_number + 1

            !Assigns wannier function to the group

            logic(j) = .true.
          else

            !Group is finished and store number of wanniers in the group to dummy_array
            dummy_array(array_idx) = group_number

            !Increment number of groups
            array_idx = array_idx + 1
            exit
          end if
        end do
      end if
    end do

    !Copy elements of dummy_array to array_groups

    allocate (array_groups(array_idx), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating array_groups in group', comm)
      return
    end if
    array_groups = dummy_array(:array_idx)

    deallocate (dummy_array, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating dummy_array in group', comm)
      return
    end if
    deallocate (logic, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating logic in group', comm)
      return
    end if

    return

  end subroutine group

  !================================================
  subroutine check_and_sort_similar_centres(signatures, num_G, atom_data, transport, print_output, &
                                            num_wann, wannier_centres_translated, coord, &
                                            tran_sorted_idx, write_xyz, stdout, seedname, timer, &
                                            error, comm)
    !================================================!
    ! Here, we consider the possiblity of wannier functions
    ! with similar centres, such as a set of d-orbitals
    ! centred on an atom. We use tran_group_threshold to
    ! identify them, if they exist, then use the signatures
    ! to dishinguish and sort then consistently from unit
    ! cell to unit cell.
    !
    ! MS: For 2two-shot and beyond, some parameters,
    ! eg, first_group_element will need changing to consider
    ! the geometry of the new systems.
    !================================================!

    use w90_constants, only: dp
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop
    use w90_types, only: atom_data_type, print_output_type, timer_list_type
    use w90_wannier90_types, only: transport_type
    use w90_error, only: w90_error_type, set_error_alloc, set_error_dealloc, set_error_fatal

    implicit none

    ! arguments
    type(atom_data_type), intent(in) :: atom_data
    type(print_output_type), intent(in) :: print_output
    type(transport_type), intent(inout) :: transport
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm
    integer, intent(in) :: coord(3)
    integer, intent(in) :: num_G
    integer, intent(in) :: num_wann
    integer, intent(inout) :: tran_sorted_idx(:)
    integer, intent(in) :: stdout
    real(kind=dp), intent(in) :: wannier_centres_translated(:, :)
    real(kind=dp), intent(in) :: signatures(:, :)
    character(len=50), intent(in)  :: seedname
    logical, intent(in) :: write_xyz

    ! local variables
    integer, allocatable :: idx_similar_wf(:), group_verifier(:), sorted_idx(:), centre_id(:)
    integer, allocatable :: tmp_wf_verifier(:, :), wf_verifier(:, :), first_group_element(:, :)
    integer, allocatable :: ref_similar_centres(:, :), unsorted_similar_centres(:, :)
    integer, allocatable :: wf_similar_centres(:, :, :)
    logical, allocatable :: has_similar_centres(:)
    integer :: i, j, k, l, ierr, group_iterator, coord_iterator, num_wf_iterator, num_wann_cell_ll
    integer :: iterator, max_position(1), p, num_wf_cell_iter
    real(kind=dp), allocatable :: dot_p(:)

    if (print_output%timing_level > 2) call io_stopwatch_start('tran: lcr_2c2_sort: similar_centres', timer)

    num_wann_cell_ll = transport%num_ll/transport%num_cell_ll

    allocate (wf_similar_centres(transport%num_cell_ll*4, num_wann_cell_ll, num_wann_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating wf_similar_centre in check_and_sort_similar_centres', comm)
      return
    end if
    allocate (idx_similar_wf(num_wann_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating idx_similar_wf in check_and_sort_similar_centres', comm)
      return
    end if
    allocate (has_similar_centres(num_wann_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating has_similar_centres in check_and_sort_similar_centres', comm)
      return
    end if
    allocate (tmp_wf_verifier(4*transport%num_cell_ll, num_wann_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating tmp_wf_verifier in check_and_sort_similar_centres', comm)
      return
    end if
    allocate (group_verifier(4*transport%num_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating group_verifier in check_and_sort_similar_centres', comm)
      return
    end if
    allocate (first_group_element(4*transport%num_cell_ll, num_wann_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating first_group_element in check_and_sort_similar_centres', comm)
      return
    end if
    allocate (centre_id(num_wann_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating centre_id in check_and_sort_similar_centres', comm)
      return
    end if

    ! First find WFs with similar centres: store in wf_similar_centres(cell#,group#,WF#)

    group_verifier = 0
    tmp_wf_verifier = 0
    first_group_element = 0
    centre_id = 0

    ! Loop over unit cells in PL1,PL2,PL3 and PL4

    do i = 1, 4*transport%num_cell_ll
      group_iterator = 0
      has_similar_centres = .false.

      ! Loops over wannier functions in present unit cell

      num_wf_cell_iter = 0
      do j = 1, num_wann_cell_ll
        num_wf_iterator = 0

        ! 2nd Loop over wannier functions in the present unit cell

        do k = 1, num_wann_cell_ll
          if ((.not. has_similar_centres(k)) .and. (j .ne. k)) then
            coord_iterator = 0

            ! Loop over x,y,z to find similar centres

            do l = 1, 3
              if (i .le. 2*transport%num_cell_ll) then
                if (abs(wannier_centres_translated(coord(l), tran_sorted_idx(j + (i - 1)*num_wann_cell_ll)) - &
                        wannier_centres_translated(coord(l), tran_sorted_idx(k + (i - 1)*num_wann_cell_ll))) &
                    .le. transport%group_threshold) then
                  coord_iterator = coord_iterator + 1
                else
                  exit
                end if
              else
                if (abs(wannier_centres_translated(coord(l), &
                                                   tran_sorted_idx(num_wann - 2*transport%num_ll &
                                                                   + j + (i - 2*transport%num_cell_ll - 1)*num_wann_cell_ll)) &
                        - wannier_centres_translated(coord(l), &
                                                     tran_sorted_idx(num_wann - 2*transport%num_ll &
                                                                     + k + (i - 2*transport%num_cell_ll - 1)*num_wann_cell_ll))) &
                    .le. transport%group_threshold) then
                  coord_iterator = coord_iterator + 1
                else
                  exit
                end if
              end if
            end do
            if (coord_iterator .eq. 3) then
              if (.not. has_similar_centres(j)) then
                num_wf_iterator = num_wf_iterator + 1
                if (i .le. 2*transport%num_cell_ll) then
                  idx_similar_wf(num_wf_iterator) = tran_sorted_idx(j + (i - 1)*num_wann_cell_ll)
                else
                  idx_similar_wf(num_wf_iterator) = tran_sorted_idx(j + num_wann - 2*transport%num_ll + &
                                                                    (i - 2*transport%num_cell_ll - 1)*num_wann_cell_ll)
                end if
                if (i .le. 2*transport%num_cell_ll) then
                  first_group_element(i, j) = j + (i - 1)*num_wann_cell_ll
                else
                  first_group_element(i, j) = num_wann - 2*transport%num_ll + &
                                              j + (i - 2*transport%num_cell_ll - 1)*num_wann_cell_ll
                end if
                num_wf_cell_iter = num_wf_cell_iter + 1
                centre_id(num_wf_cell_iter) = j
              end if
              has_similar_centres(k) = .true.
              has_similar_centres(j) = .true.
              num_wf_iterator = num_wf_iterator + 1
              if (i .le. 2*transport%num_cell_ll) then
                idx_similar_wf(num_wf_iterator) = tran_sorted_idx(k + (i - 1)*num_wann_cell_ll)
              else
                idx_similar_wf(num_wf_iterator) = tran_sorted_idx(k + num_wann - 2*transport%num_ll + &
                                                                  (i - 2*transport%num_cell_ll - 1)*num_wann_cell_ll)
              end if
            end if
          end if
        end do ! loop over k
        if (num_wf_iterator .gt. 0) then
          group_iterator = group_iterator + 1
          wf_similar_centres(i, group_iterator, :) = idx_similar_wf(:)

          !Save number of WFs in each group

          tmp_wf_verifier(i, group_iterator) = num_wf_iterator
        end if
      end do
      if ((count(has_similar_centres) .eq. 0) .and. (i .eq. 1)) then
        write (stdout, '(a)') ' No wannier functions found with similar centres: sorting completed'
        exit
      elseif (i .eq. 1) then
        write (stdout, *) ' Wannier functions found with similar centres: '
        write (stdout, *) '  -> using signatures to complete sorting '
      end if

      !Save number of group of WFs in each unit cell and compare to previous unit cell

      group_verifier(i) = group_iterator
      if (print_output%iprint .ge. 4) write (stdout, '(a11,i4,a13,i4)') ' Unit cell:', i, '  Num groups:', group_verifier(i)
      if (i .ne. 1) then
        if (group_verifier(i) .ne. group_verifier(i - 1)) then
          if (write_xyz) call tran_write_xyz(atom_data, transport, wannier_centres_translated, &
                                             tran_sorted_idx, num_wann, seedname, stdout)
          call set_error_fatal(error, 'Inconsistent number of groups of similar centred wannier functions between unit cells', comm)
          return
        elseif (i .eq. 4*transport%num_cell_ll) then
          write (stdout, *) ' Consistent groups of similar centred wannier functions between '
          write (stdout, *) ' unit cells found'
          write (stdout, *) ' '
        end if
      end if
    end do  !Loop over all unit cells in PL1,PL2,PL3,PL4

    ! Perform check to ensure consistent number of WFs between equivalent groups in different unit cells

    if (any(has_similar_centres)) then

      allocate (wf_verifier(4*transport%num_cell_ll, group_verifier(1)), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error in allocating wf_verifier in check_and_sort_similar_centres', comm)
        return
      end if

      if (print_output%iprint .ge. 4) write (stdout, *) 'Unit cell   Group number   Num WFs'
      wf_verifier = 0
      wf_verifier = tmp_wf_verifier(:, 1:group_verifier(1))
      do i = 1, 4*transport%num_cell_ll
        do j = 1, group_verifier(1)
          if (print_output%iprint .ge. 4) write (stdout, '(a3,i4,a9,i4,a7,i4)') '   ', i, '         ', &
            j, '       ', wf_verifier(i, j)
          if (i .ne. 1) then
            if (wf_verifier(i, j) .ne. wf_verifier(i - 1, j)) then
              call set_error_fatal(error, 'Inconsistent number of wannier &
                  &functions between equivalent groups of similar &
                  &centred wannier functions', comm)
              return
            end if
          end if
        end do
      end do
      write (stdout, *) ' Consistent number of wannier functions between equivalent groups of similar'
      write (stdout, *) ' centred wannier functions'
      write (stdout, *) ' '

      write (stdout, *) ' Fixing order of similar centred wannier functions using parity signatures'

      do i = 2, 4*transport%num_cell_ll
        do j = 1, group_verifier(1)

          ! Make array of WF numbers which act as a reference to sort against
          ! and an array which need sorting

          allocate (ref_similar_centres(group_verifier(1), wf_verifier(1, j)), stat=ierr)
          if (ierr /= 0) then
            call set_error_alloc(error, 'Error in allocating ref_similar_centres in check_and_sort_similar_centres', comm)
            return
          end if
          allocate (unsorted_similar_centres(group_verifier(1), wf_verifier(1, j)), stat=ierr)
          if (ierr /= 0) then
            call set_error_alloc(error, 'Error in allocating unsorted_similar_centres in check_and_sort_similar_centres', comm)
            return
          end if
          allocate (sorted_idx(wf_verifier(1, j)), stat=ierr)
          if (ierr /= 0) then
            call set_error_alloc(error, 'Error in allocating sorted_idx in check_and_sort_similar_centres', comm)
            return
          end if
          allocate (dot_p(wf_verifier(1, j)), stat=ierr)
          if (ierr /= 0) then
            call set_error_alloc(error, 'Error in allocating dot_p in check_and_sort_similar_centres', comm)
            return
          end if

          do k = 1, wf_verifier(1, j)
            ref_similar_centres(j, k) = wf_similar_centres(1, j, k)
            unsorted_similar_centres(j, k) = wf_similar_centres(i, j, k)
          end do

          sorted_idx = 0
          do k = 1, wf_verifier(1, j)
            dot_p = 0.0_dp

            ! building the array of positive dot products of signatures between unsorted_similar_centres(j,k)
            ! and all the ref_similar_centres(j,:)

            do l = 1, wf_verifier(1, j)
              do p = 1, num_G
                dot_p(l) = dot_p(l) + abs(signatures(p, unsorted_similar_centres(j, k)))* &
                           abs(signatures(p, ref_similar_centres(j, l)))
              end do
            end do

            max_position = maxloc(dot_p)

            sorted_idx(max_position(1)) = unsorted_similar_centres(j, k)
          end do

          ! we have the properly ordered indexes for group j in unit cell i, now we need
          ! to overwrite the tran_sorted_idx array at the proper position

          tran_sorted_idx(first_group_element(i, centre_id(j)):first_group_element(i, centre_id(j)) +&
               &wf_verifier(i, j) - 1) = sorted_idx(:)

          deallocate (dot_p, stat=ierr)
          if (ierr /= 0) then
            call set_error_dealloc(error, 'Error in deallocating dot_p in check_and_sort_similar_centres', comm)
            return
          end if
          deallocate (sorted_idx, stat=ierr)
          if (ierr /= 0) then
            call set_error_dealloc(error, 'Error in deallocating sorted_idx in check_and_sort_similar_centres', comm)
            return
          end if
          deallocate (unsorted_similar_centres, stat=ierr)
          if (ierr /= 0) then
            call set_error_dealloc(error, 'Error in deallocating unsorted_similar_centres in check_and_sort_similar_centres', comm)
            return
          end if
          deallocate (ref_similar_centres, stat=ierr)
          if (ierr /= 0) then
            call set_error_dealloc(error, 'Error in deallocating ref_similar_centres in check_and_sort_similar_centres', comm)
            return
          end if
        end do
      end do

      ! checking that all the indices of WFs in the new tran_sorted_idx are distinct
      ! Remark: physically, no two WFs with similar centres can have the same type so we should expect
      ! this check to always pass unless the signatures/wf are very weird !!

      do k = 1, num_wann
        iterator = 0
        do l = 1, num_wann
          if (tran_sorted_idx(l) .eq. k) then
            iterator = iterator + 1
          end if
        end do

        if ((iterator .ge. 2) .or. (iterator .eq. 0)) then
          call set_error_fatal(error, &
              'A Wannier Function appears either zero times or twice after sorting, this may be due to a &
              &poor wannierisation and/or disentanglement', comm)
          return
        end if
        !write(stdout,*) ' WF : ',k,' appears ',iterator,' time(s)'
      end do
      deallocate (wf_verifier, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating wf_verifier in check_and_sort_similar_centres', comm)
        return
      end if
    end if

    deallocate (centre_id, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating centre_id in check_and_sort_similar_centres', comm)
      return
    end if
    deallocate (first_group_element, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating first_group_element in check_and_sort_similar_centres', comm)
      return
    end if
    deallocate (group_verifier, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating group_verifier in check_and_sort_similar_centres', comm)
      return
    end if
    deallocate (tmp_wf_verifier, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating tmp_wf_verifier in check_and_sort_similar_centres', comm)
      return
    end if
    deallocate (has_similar_centres, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating has_similar_centres in check_and_sort_similar_centres', comm)
      return
    end if
    deallocate (idx_similar_wf, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating idx_similar_wf in check_and_sort_similar_centres', comm)
      return
    end if
    deallocate (wf_similar_centres, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating wf_similar_centre in check_and_sort_similar_centres', comm)
      return
    end if

    if (print_output%timing_level > 2) call io_stopwatch_stop('tran: lcr_2c2_sort: similar_centres', timer)

    return

  end subroutine check_and_sort_similar_centres

  !================================================!
  subroutine tran_write_xyz(atom_data, transport, wannier_centres_translated, tran_sorted_idx, &
                            num_wann, seedname, stdout)
    !================================================!
    !
    ! Write xyz file with Wannier centres
    ! and atomic positions
    !
    !================================================!

    use w90_io, only: io_date
    use w90_types, only: atom_data_type
    use w90_wannier90_types, only: transport_type

    implicit none

    ! arguments
    type(transport_type), intent(inout) :: transport
    type(atom_data_type), intent(in) :: atom_data

    integer, intent(in) :: tran_sorted_idx(:)
    integer, intent(in) :: num_wann
    integer, intent(in) :: stdout

    real(kind=dp), intent(in) :: wannier_centres_translated(:, :)

    character(len=50), intent(in)  :: seedname

    ! local variables
    integer          :: iw, ind, xyz_unit, nat, nsp
    character(len=9) :: cdate, ctime
    real(kind=dp)    :: wc(3, num_wann)

    if (index(transport%mode, 'bulk') > 0) wc = wannier_centres_translated
    if (index(transport%mode, 'lcr') > 0) then
      do iw = 1, num_wann
        wc(:, iw) = wannier_centres_translated(:, tran_sorted_idx(iw))
      end do
    end if

    open (newunit=xyz_unit, file=trim(seedname)//'_centres.xyz', form='formatted')
    write (xyz_unit, '(i6)') num_wann + atom_data%num_atoms
    call io_date(cdate, ctime)
    write (xyz_unit, '(a84)') 'Wannier centres and atomic positions, written by Wannier90 on '//cdate//' at '//ctime

    do iw = 1, num_wann
      write (xyz_unit, '("X",6x,3(f14.8,3x))') (wc(ind, iw), ind=1, 3)
    end do
    do nsp = 1, atom_data%num_species
      do nat = 1, atom_data%species_num(nsp)
        write (xyz_unit, '(a2,5x,3(f14.8,3x))') atom_data%symbol(nsp), atom_data%pos_cart(:, nat, nsp)
      end do
    end do

    write (stdout, *) ' Wannier centres written to file '//trim(seedname)//'_centres.xyz'

    return

  end subroutine tran_write_xyz

  !================================================!
  subroutine tran_parity_enforce(signatures, print_output, transport, num_wann, tran_sorted_idx, &
                                 hr_one_dim, irvec_max, stdout, timer)
    !================================================!
    ! Here, the signatures of the each wannier fucntion (stored in
    ! signatures) is used to determine its relavite parity
    ! with respect to the first unit cell. The parity pattern of
    ! first unit cell is then enforced.
    !================================================!

    use w90_constants, only: dp
    use w90_io, only: io_stopwatch_start, io_stopwatch_stop
    use w90_types, only: print_output_type, timer_list_type
    use w90_wannier90_types, only: transport_type

    implicit none

    ! arguments
    integer, intent(in) :: irvec_max
    integer, intent(in) :: num_wann
    integer, intent(in) :: stdout
    integer, intent(in) :: tran_sorted_idx(:)

    real(kind=dp), intent(inout) :: signatures(:, :)
    real(kind=dp), intent(inout) :: hr_one_dim(:, :, -irvec_max:)

    type(print_output_type), intent(in) :: print_output
    type(transport_type), intent(in) :: transport
    type(timer_list_type), intent(inout) :: timer

    ! local variables
    integer :: i, j, k, wf_idx, num_wann_cell_ll
    real(kind=dp) :: signature_dot_p

    if (print_output%timing_level > 1) call io_stopwatch_start('tran: parity_enforce', timer)

    ! NP: special "easy" fix of the parities by switching the sign
    ! of the Wannier Functions if the first element of the signature
    ! is found negative. Then updating the signature and the Hamiltonian
    ! matrix element for the corresponding line and column

    if (transport%easy_fix) then
      do i = 1, num_wann
        if (real(signatures(1, i)) .lt. 0.0_dp) then
          signatures(:, i) = -signatures(:, i)
          do k = 1, num_wann
            hr_one_dim(k, i, 0) = -hr_one_dim(k, i, 0)
            hr_one_dim(i, k, 0) = -hr_one_dim(i, k, 0)
          end do
        end if
      end do
    end if

    num_wann_cell_ll = transport%num_ll/transport%num_cell_ll
    if (print_output%iprint .eq. 5) write (stdout, '(a101)') 'Unit cell    Sorted WF index    Unsort WF index  &
         &Unsorted WF Equiv       Signature Dot Product'

    ! Loop over unit cell in principal layers

    do i = 2, 4*transport%num_cell_ll

      ! Loop over wannier functions in unit cell

      do j = 1, num_wann_cell_ll
        if (i .le. 2*transport%num_cell_ll) then
          wf_idx = j + (i - 1)*num_wann_cell_ll
        else
          wf_idx = num_wann - 2*transport%num_ll + j + (i - 1 - 2*transport%num_cell_ll)*num_wann_cell_ll
        end if
        signature_dot_p = dot_product(signatures(:, tran_sorted_idx(j)), signatures(:, tran_sorted_idx(wf_idx)))
        if (print_output%iprint .eq. 5) then
          write (stdout, '(2x,i4,3(13x,i5),12x,f20.17)') &
            i, wf_idx, tran_sorted_idx(wf_idx), tran_sorted_idx(j), signature_dot_p
        end if
        if (abs(signature_dot_p) .le. 0.8_dp) then
          write (stdout, '(a28,i4,a64,i4,a20)') ' WARNING: Wannier function (', tran_sorted_idx(wf_idx), &
            ') seems to has poor resemblance to equivalent wannier function (', tran_sorted_idx(j), ') in first unit cell'
          if (print_output%iprint .lt. 5) write (stdout, *) 'Dot product of signatures: ', signature_dot_p
        end if
        if (signature_dot_p .lt. 0.0_dp) then
          do k = 1, num_wann
            hr_one_dim(k, tran_sorted_idx(wf_idx), 0) = -hr_one_dim(k, tran_sorted_idx(wf_idx), 0)
            hr_one_dim(tran_sorted_idx(wf_idx), k, 0) = -hr_one_dim(tran_sorted_idx(wf_idx), k, 0)
          end do
        end if
      end do
    end do

    if (print_output%timing_level > 1) call io_stopwatch_stop('tran: parity_enforce', timer)

    return

  end subroutine tran_parity_enforce

  !================================================!
  subroutine tran_lcr_2c2_build_ham(pl_warning, real_space_ham, fermi_energy_list, kpt_latt, &
                                    num_wann, transport, print_output, real_lattice, mp_grid, &
                                    ham_r, irvec, nrpts, wannier_centres_translated, one_dim_vec, &
                                    nrpts_one_dim, num_pl, coord, tran_sorted_idx, hC, hCR, hL0, &
                                    hL1, hLC, hR0, hR1, hr_one_dim, irvec_max, stdout, seedname, &
                                    timer, error, comm)
    !================================================!
    ! Builds hamiltonians blocks required for the
    ! Greens function caclulations of the quantum
    ! conductance according to the 2c2 geometry.
    ! Leads are also symmetrised, in that unit cell
    ! sub-blocks are copied to create truely ideal
    ! leads.
    !================================================!

    use w90_constants, only: dp, eps5
    use w90_io, only: io_date, io_stopwatch_start, io_stopwatch_stop
    use w90_types, only: print_output_type, timer_list_type
    use w90_wannier90_types, only: transport_type, real_space_ham_type
    use w90_error, only: w90_error_type, set_error_fatal, set_error_alloc, set_error_dealloc

    implicit none

    ! arguments
    integer, intent(in) :: coord(3)
    integer, intent(in) :: irvec(:, :)
    integer, intent(inout) :: irvec_max
    integer, intent(in) :: mp_grid(3)
    integer, intent(in) :: num_wann
    integer, intent(inout) :: nrpts
    integer, intent(inout) :: nrpts_one_dim
    integer, intent(inout) :: num_pl
    integer, intent(inout) :: one_dim_vec
    integer, intent(in) :: stdout
    integer, intent(in) :: tran_sorted_idx(:)

    real(kind=dp), allocatable, intent(inout) :: hr_one_dim(:, :, :) ! de/realloc'd below
    real(kind=dp), allocatable, intent(inout) :: hC(:, :)
    real(kind=dp), allocatable, intent(inout) :: hCR(:, :)
    real(kind=dp), allocatable, intent(inout) :: hL0(:, :)
    real(kind=dp), allocatable, intent(inout) :: hL1(:, :)
    real(kind=dp), allocatable, intent(inout) :: hLC(:, :)
    real(kind=dp), allocatable, intent(inout) :: hR0(:, :)
    real(kind=dp), allocatable, intent(inout) :: hR1(:, :)
    real(kind=dp), intent(in) :: real_lattice(3, 3)
    real(kind=dp), intent(in) :: wannier_centres_translated(:, :)

    complex(kind=dp), intent(in) :: ham_r(:, :, :)

    real(kind=dp), allocatable, intent(in) :: fermi_energy_list(:)
    real(kind=dp), intent(in) :: kpt_latt(:, :)

    type(print_output_type), intent(in) :: print_output
    type(real_space_ham_type), intent(inout) :: real_space_ham
    type(transport_type), intent(inout) :: transport
    type(timer_list_type), intent(inout) :: timer
    type(w90_error_type), allocatable, intent(out) :: error
    type(w90_comm_type), intent(in) :: comm

    character(len=50), intent(in)  :: seedname

    logical, intent(in) :: pl_warning

    ! local variables
    integer                :: i, j, k, num_wann_cell_ll, file_unit, ierr, band_size
    integer                :: fermi_n
    real(kind=dp), allocatable  :: sub_block(:, :)
    real(kind=dp)          :: PL_length, dist, dist_vec(3)
    character(len=9)       :: cdate, ctime

    if (print_output%timing_level > 1) call io_stopwatch_start('tran: lcr_2c2_build_ham', timer)

    fermi_n = 0
    if (allocated(fermi_energy_list)) fermi_n = size(fermi_energy_list)
    if (fermi_n > 1) then
      call set_error_fatal(error, "Error in tran_lcr_2c2_build_ham: nfermi>1. " &
                           //"Set the fermi level using the input parameter 'fermi_evel'", comm)
      return
    end if

    allocate (hL0(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hL0 in tran_lcr_2c2_build_ham', comm)
      return
    end if
    allocate (hL1(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hL1 in tran_lcr_2c2_build_ham', comm)
      return
    end if
    allocate (hR0(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hR0 in tran_lcr_2c2_build_ham', comm)
      return
    end if
    allocate (hR1(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hR1 in tran_lcr_2c2_build_ham', comm)
      return
    end if
    allocate (hLC(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hLC in tran_lcr_2c2_build_ham', comm)
      return
    end if
    allocate (hCR(transport%num_ll, transport%num_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hCR in tran_lcr_2c2_build_ham', comm)
      return
    end if
    allocate (hC(num_wann - (2*transport%num_ll), num_wann - (2*transport%num_ll)), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating hC in tran_lcr_2c2_build_ham', comm)
      return
    end if

    !This checks that only the gamma point is used in wannierisation
    !This is necessary since this calculation only makes sense if we
    !have periodicity over the supercell.
    !BGS, I think (0, 0, 0) in kpt_latt should work as well as in kpt_cart
    if ((size(kpt_latt, 2) .ne. 1) .and. (kpt_latt(1, 1) .eq. 0.0_dp) &
        .and. (kpt_latt(2, 1) .eq. 0.0_dp) .and. (kpt_latt(3, 1) .eq. 0.0_dp)) then
      call set_error_fatal(error, 'Calculation must be performed at gamma only', comm)
      return
    end if

    num_wann_cell_ll = transport%num_ll/transport%num_cell_ll

    allocate (sub_block(num_wann_cell_ll, num_wann_cell_ll), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating sub_block in tran_lcr_2c2_build_ham', comm)
      return
    end if

    !Build hL0 & hL1

    hL0 = 0.0_dp
    hL1 = 0.0_dp

    !Loop over the sub_blocks corresponding to distinct unit cells inside the principal layer

    do i = 1, transport%num_cell_ll

      !Each sub_block will be duplicated along the corresponding diagonal. This ensures the correct symmetry for the leads.

      sub_block = 0.0_dp

      !Extract matrix elements from hr_one_dim needed for hL0 (and lower triangular sub_blocks of hL1)

      do j = 1, num_wann_cell_ll
        do k = 1, num_wann_cell_ll
          sub_block(j, k) = hr_one_dim(tran_sorted_idx(j), tran_sorted_idx((i - 1)*num_wann_cell_ll + k), 0)
        end do
      end do

      !Filling up hL0 sub_block by sub_block

      do j = 1, transport%num_cell_ll - i + 1

        !Fill diagonal and upper diagonal sub_blocks

        hL0((j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll, &
            (j - 1)*num_wann_cell_ll + 1 + (i - 1)*num_wann_cell_ll:j*num_wann_cell_ll + (i - 1)*num_wann_cell_ll) = sub_block

        !Fill lower diagonal sub_blocks

        if (i .gt. 1) then
          hL0((j - 1)*num_wann_cell_ll + 1 + (i - 1)*num_wann_cell_ll:j*num_wann_cell_ll + (i - 1)*num_wann_cell_ll, &
              (j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll) = transpose(sub_block)
        end if
      end do

      !Filling up non-diagonal hL1 sub_blocks (nothing need be done for i=1)

      if (i .gt. 1) then
        do j = 1, i - 1
          hL1((transport%num_cell_ll - (i - j))*num_wann_cell_ll + 1:(transport%num_cell_ll - (i - 1 - j))*num_wann_cell_ll, &
              (j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll) = sub_block
        end do
      end if

      ! MS: Get diagonal and upper triangular sublocks for hL1 - use periodic image of PL4

      sub_block = 0.0_dp

      if (i == 1) then !Do diagonal only
        do j = 1, num_wann_cell_ll
          do k = 1, num_wann_cell_ll
            sub_block(j, k) = hr_one_dim( &
                              tran_sorted_idx(num_wann - transport%num_ll + j), &
                              tran_sorted_idx((i - 1)*num_wann_cell_ll + k), 0)
          end do
        end do

        ! MS: Now fill subblocks of hL1

        do j = 1, transport%num_cell_ll - i + 1
          hL1((j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll, &
              (j - 1)*num_wann_cell_ll + 1 + (i - 1)*num_wann_cell_ll:j*num_wann_cell_ll + (i - 1)* &
              num_wann_cell_ll) = sub_block
        end do
      end if
    end do

    !Special case tran_num_cell_ll=1, the diagonal sub-block of hL1 is hL1, so cannot be left as zero

    if (transport%num_cell_ll .eq. 1) then
      do j = num_wann - num_wann_cell_ll + 1, num_wann
        do k = 1, num_wann_cell_ll
          hL1(j - num_wann + num_wann_cell_ll, k) = hr_one_dim(tran_sorted_idx(j), tran_sorted_idx(k), 0)
        end do
      end do
    end if

    !Build hR0 & hR1

    hR0 = 0.0_dp
    hR1 = 0.0_dp

    !Loop over the sub_blocks corresponding to distinct unit cells inside the principal layer

    do i = 1, transport%num_cell_ll

      !Each sub_block will be duplicated along the corresponding diagonal. This ensures the correct symmetry for the leads.

      sub_block = 0.0_dp

      !Extract matrix elements from hr_one_dim needed for hR0 (and lower triangular sub_blocks of hR1)

      do j = 1, num_wann_cell_ll
        do k = 1, num_wann_cell_ll
          sub_block(j, k) = hr_one_dim(tran_sorted_idx(num_wann - i*(num_wann_cell_ll) + j), &
                                       tran_sorted_idx(num_wann - num_wann_cell_ll + k), 0)
        end do
      end do

      !Filling up hR0 sub_block by sub_block

      do j = 1, transport%num_cell_ll - i + 1

        !Fill diagonal and upper diagonal sub_blocks

        hR0((j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll, &
            (j - 1)*num_wann_cell_ll + 1 + (i - 1)*num_wann_cell_ll:j*num_wann_cell_ll + (i - 1)*num_wann_cell_ll) = sub_block

        !Fill lower diagonal sub_blocks

        if (i .gt. 1) then
          hR0((j - 1)*num_wann_cell_ll + 1 + (i - 1)*num_wann_cell_ll:j*num_wann_cell_ll + (i - 1)*num_wann_cell_ll, &
              (j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll) = transpose(sub_block)
        end if
      end do

      !Filling up non-diagonal hR1 sub_blocks (nothing need be done for i=1)

      if (i .gt. 1) then
        do j = 1, i - 1
          hR1((transport%num_cell_ll - (i - j))*num_wann_cell_ll + 1:(transport%num_cell_ll - (i - 1 - j))*num_wann_cell_ll, &
              (j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll) = sub_block
        end do
      end if

      ! MS: Get diagonal and upper triangular sublocks for hR1 - use periodic image of PL1

      sub_block = 0.0_dp

      if (i == 1) then  !Do diagonal only
        do j = 1, num_wann_cell_ll
          do k = 1, num_wann_cell_ll
            sub_block(j, k) = hr_one_dim(tran_sorted_idx((i - 1)*num_wann_cell_ll + k), &
                                         tran_sorted_idx(num_wann - transport%num_ll + j), 0)
          end do
        end do

        ! MS: Now fill subblocks of hR1

        do j = 1, transport%num_cell_ll - i + 1
          hR1((j - 1)*num_wann_cell_ll + 1:j*num_wann_cell_ll, &
              (j - 1)*num_wann_cell_ll + 1 + (i - 1)*num_wann_cell_ll:j*num_wann_cell_ll + (i - 1)*num_wann_cell_ll) = sub_block
        end do
      end if
    end do

    !Special case tran_num_cell_ll=1, the diagonal sub-block of hR1 is hR1, so cannot be left as zero

    if (transport%num_cell_ll .eq. 1) then
      do j = 1, num_wann_cell_ll
        do k = num_wann - num_wann_cell_ll + 1, num_wann
          hR1(k - num_wann + num_wann_cell_ll, j) = hr_one_dim(tran_sorted_idx(j), tran_sorted_idx(k), 0)
        end do
      end do
    end if

    !Building hLC

    hLC = 0.0_dp
    do i = 1, transport%num_ll
      do j = transport%num_ll + 1, 2*transport%num_ll
        hLC(i, j - transport%num_ll) = hr_one_dim(tran_sorted_idx(i), tran_sorted_idx(j), 0)
      end do
    end do
!----!
! MS ! Rely on dist_cutoff doing the work here, as it cuts element-wise, not block wise (incorrect)
!----!
!    if (transport%num_cell_ll .gt. 1) then
!        do j=1,transport%num_cell_ll
!            do k=1,transport%num_cell_ll
!                if (k .ge. j) then
!                    hLC((j-1)*num_wann_cell_ll+1:j*num_wann_cell_ll,(k-1)*num_wann_cell_ll+1:k*num_wann_cell_ll)=0.0_dp
!                endif
!            enddo
!        enddo
!    endif
!---!
!end!
!---!

    !Building hC

    hC = 0.0_dp

    band_size = 0
    if (real_space_ham%dist_cutoff_hc .ne. real_space_ham%dist_cutoff) then
      real_space_ham%dist_cutoff = real_space_ham%dist_cutoff_hc
      write (stdout, *) 'Applying dist_cutoff_hc to Hamiltonian for construction of hC'
      deallocate (hr_one_dim, stat=ierr)
      if (ierr /= 0) then
        call set_error_dealloc(error, 'Error deallocating hr_one_dim in tran_lcr_2c2_build_ham', comm)
        return
      end if
      call tran_reduce_hr(real_space_ham, ham_r, hr_one_dim, real_lattice, irvec, mp_grid, &
                          irvec_max, nrpts, nrpts_one_dim, num_wann, one_dim_vec, &
                          print_output%timing_level, stdout, timer, error, comm)
      if (allocated(error)) return

      call tran_cut_hr_one_dim(real_space_ham, transport, print_output, hr_one_dim, real_lattice, &
                               wannier_centres_translated, mp_grid, irvec_max, num_pl, num_wann, &
                               one_dim_vec, stdout, timer)

    end if

    do i = transport%num_ll + 1, num_wann - transport%num_ll
      do j = transport%num_ll + 1, num_wann - transport%num_ll
        hC(i - transport%num_ll, j - transport%num_ll) = hr_one_dim(tran_sorted_idx(i), tran_sorted_idx(j), 0)

        ! Impose a ham_cutoff of 1e-4 eV to reduce tran_num_bandc (and in turn hCband, and speed up transport)

        if (abs(hC(i - transport%num_ll, j - transport%num_ll)) .lt. 10.0_dp*eps5) then
          hC(i - transport%num_ll, j - transport%num_ll) = 0.0_dp
          band_size = max(band_size, abs(i - j))
        end if
      end do
    end do

    !Building hCR

    hCR = 0.0_dp
    do i = num_wann - 2*transport%num_ll + 1, num_wann - transport%num_ll
      do j = num_wann - transport%num_ll + 1, num_wann
        hCR(i - (num_wann - 2*transport%num_ll), j - (num_wann - transport%num_ll)) = &
          hr_one_dim(tran_sorted_idx(i), tran_sorted_idx(j), 0)
      end do
    end do
!----!
! MS ! Rely on dist_cutoff doing the work here, as it cuts element-wise, not block wise (incorrect)
!----!
!    if (transport%num_cell_ll .gt. 1) then
!        do j=1,transport%num_cell_ll
!            do k=1,transport%num_cell_ll
!                if (k .ge. j) then
!                    hCR((j-1)*num_wann_cell_ll+1:j*num_wann_cell_ll,(k-1)*num_wann_cell_ll+1:k*num_wann_cell_ll)=0.0_dp
!                endif
!            enddo
!        enddo
!    endif
!---!
!end!
!---!

    !Subtract the Fermi energy from the diagonal elements of hC,hL0,hR0

    do i = 1, transport%num_ll
      hL0(i, i) = hL0(i, i) - fermi_energy_list(1)
      hR0(i, i) = hR0(i, i) - fermi_energy_list(1)
    end do
    do i = 1, num_wann - (2*transport%num_ll)
      hC(i, i) = hC(i, i) - fermi_energy_list(1)
    end do

    !Define tran_num_** parameters that are used later in tran_lcr

    transport%num_rr = transport%num_ll
    transport%num_lc = transport%num_ll
    transport%num_cr = transport%num_ll
    transport%num_cc = num_wann - (2*transport%num_ll)

    ! Set appropriate tran_num_bandc if has not been set (0.0_dp is default value)

    if (transport%num_bandc .eq. 0.0_dp) then
      transport%num_bandc = min(band_size + 1, (transport%num_cc + 1)/2 + 1)
    end if

    ! MS: Find and print effective PL length

    if (.not. pl_warning) then
      PL_length = 0.0_dp
      do i = 1, transport%num_ll
        do j = 1, transport%num_ll
          if (abs(hL1(i, j)) .gt. 0.0_dp) then
            if (index(real_space_ham%dist_cutoff_mode, 'one_dim') .gt. 0) then
              dist = abs(wannier_centres_translated(coord(1), tran_sorted_idx(i)) &
                         - wannier_centres_translated(coord(1), tran_sorted_idx(j + transport%num_ll)))
            else
              dist_vec(:) = wannier_centres_translated(:, tran_sorted_idx(i)) &
                            - wannier_centres_translated(:, tran_sorted_idx(j + transport%num_ll))
              dist = sqrt(dot_product(dist_vec, dist_vec))
            end if
            PL_length = max(PL_length, dist)
          end if
          if (abs(hR1(i, j)) .gt. 0.0_dp) then
            if (index(real_space_ham%dist_cutoff_mode, 'one_dim') .gt. 0) then
              dist = abs(wannier_centres_translated(coord(1), tran_sorted_idx(num_wann - 2*transport%num_ll + i)) &
                         - wannier_centres_translated(coord(1), tran_sorted_idx(num_wann - transport%num_ll + j)))
            else
              dist_vec(:) = wannier_centres_translated(:, tran_sorted_idx(num_wann - 2*transport%num_ll + i)) &
                            - wannier_centres_translated(:, tran_sorted_idx(num_wann - transport%num_ll + j))
              dist = sqrt(dot_product(dist_vec, dist_vec))
            end if
            PL_length = max(PL_length, dist)
          end if
        end do
      end do
      write (stdout, '(1x,a,f12.6,a)') 'Approximate effective principal layer length is: ', PL_length, ' Ang.'
    end if

    !Writing to file:

    if (transport%write_ht) then
      write (stdout, *) '------------------------------- Writing ht files  ----------------------------'

      open (newunit=file_unit, file=trim(seedname)//'_htL.dat', status='unknown', form='formatted', action='write')

      call io_date(cdate, ctime)
      write (file_unit, *) 'written on '//cdate//' at '//ctime ! Date and time
      write (file_unit, '(I6)') transport%num_ll
      write (file_unit, '(6F12.6)') ((hL0(j, i), j=1, transport%num_ll), i=1, transport%num_ll)
      write (file_unit, '(I6)') transport%num_ll
      write (file_unit, '(6F12.6)') ((hL1(j, i), j=1, transport%num_ll), i=1, transport%num_ll)

      close (file_unit)
      write (stdout, *) ' '//trim(seedname)//'_htL.dat  written'

      !hR
      open (newunit=file_unit, file=trim(seedname)//'_htR.dat', status='unknown', form='formatted', action='write')

      call io_date(cdate, ctime)
      write (file_unit, *) 'written on '//cdate//' at '//ctime ! Date and time
      write (file_unit, '(I6)') transport%num_rr
      write (file_unit, '(6F12.6)') ((hR0(j, i), j=1, transport%num_rr), i=1, transport%num_rr)
      write (file_unit, '(I6)') transport%num_rr
      write (file_unit, '(6F12.6)') ((hR1(j, i), j=1, transport%num_rr), i=1, transport%num_rr)

      close (file_unit)
      write (stdout, *) ' '//trim(seedname)//'_htR.dat  written'

      !hLC
      open (newunit=file_unit, file=trim(seedname)//'_htLC.dat', status='unknown', form='formatted', action='write')

      call io_date(cdate, ctime)
      write (file_unit, *) 'written on '//cdate//' at '//ctime ! Date and time
      write (file_unit, '(2I6)') transport%num_ll, transport%num_lc
      write (file_unit, '(6F12.6)') ((hLC(j, i), j=1, transport%num_lc), i=1, transport%num_lc)

      close (file_unit)
      write (stdout, *) ' '//trim(seedname)//'_htLC.dat written'

      !hCR
      open (newunit=file_unit, file=trim(seedname)//'_htCR.dat', status='unknown', form='formatted', action='write')

      call io_date(cdate, ctime)
      write (file_unit, *) 'written on '//cdate//' at '//ctime ! Date and time
      write (file_unit, '(2I6)') transport%num_cr, transport%num_rr
      write (file_unit, '(6F12.6)') ((hCR(j, i), j=1, transport%num_cr), i=1, transport%num_cr)

      close (file_unit)
      write (stdout, *) ' '//trim(seedname)//'_htCR.dat written'

      !hC
      open (newunit=file_unit, file=trim(seedname)//'_htC.dat', status='unknown', form='formatted', action='write')

      call io_date(cdate, ctime)
      write (file_unit, *) 'written on '//cdate//' at '//ctime ! Date and time
      write (file_unit, '(I6)') transport%num_cc
      write (file_unit, '(6F12.6)') ((hC(j, i), j=1, transport%num_cc), i=1, transport%num_cc)

      close (file_unit)
      write (stdout, *) ' '//trim(seedname)//'_htC.dat  written'

      write (stdout, *) '------------------------------------------------------------------------------'
    end if

    deallocate (sub_block, stat=ierr)
    if (ierr /= 0) then
      call set_error_dealloc(error, 'Error deallocating sub_block in tran_lcr_2c2_build_ham', comm)
      return
    end if

    if (print_output%timing_level > 1) call io_stopwatch_stop('tran: lcr_2c2_build_ham', timer)

    return

  end subroutine tran_lcr_2c2_build_ham

end module w90_transport_mod
