library_interface.F90 Source File


This file depends on

sourcefile~~library_interface.f90~~EfferentGraph sourcefile~library_interface.f90 library_interface.F90 sourcefile~comms.f90 comms.F90 sourcefile~library_interface.f90->sourcefile~comms.f90 sourcefile~constants.f90 constants.F90 sourcefile~library_interface.f90->sourcefile~constants.f90 sourcefile~disentangle.f90 disentangle.F90 sourcefile~library_interface.f90->sourcefile~disentangle.f90 sourcefile~error.f90 error.F90 sourcefile~library_interface.f90->sourcefile~error.f90 sourcefile~error_base.f90 error_base.F90 sourcefile~library_interface.f90->sourcefile~error_base.f90 sourcefile~io.f90 io.F90 sourcefile~library_interface.f90->sourcefile~io.f90 sourcefile~kmesh.f90 kmesh.F90 sourcefile~library_interface.f90->sourcefile~kmesh.f90 sourcefile~overlap.f90 overlap.F90 sourcefile~library_interface.f90->sourcefile~overlap.f90 sourcefile~plot.f90 plot.F90 sourcefile~library_interface.f90->sourcefile~plot.f90 sourcefile~readwrite.f90 readwrite.F90 sourcefile~library_interface.f90->sourcefile~readwrite.f90 sourcefile~transport.f90 transport.F90 sourcefile~library_interface.f90->sourcefile~transport.f90 sourcefile~types.f90 types.F90 sourcefile~library_interface.f90->sourcefile~types.f90 sourcefile~wannier90_readwrite.f90 wannier90_readwrite.F90 sourcefile~library_interface.f90->sourcefile~wannier90_readwrite.f90 sourcefile~wannier90_types.f90 wannier90_types.F90 sourcefile~library_interface.f90->sourcefile~wannier90_types.f90 sourcefile~wannierise.f90 wannierise.F90 sourcefile~library_interface.f90->sourcefile~wannierise.f90 sourcefile~comms.f90->sourcefile~constants.f90 sourcefile~comms.f90->sourcefile~error_base.f90 sourcefile~disentangle.f90->sourcefile~comms.f90 sourcefile~disentangle.f90->sourcefile~constants.f90 sourcefile~disentangle.f90->sourcefile~error.f90 sourcefile~disentangle.f90->sourcefile~io.f90 sourcefile~disentangle.f90->sourcefile~types.f90 sourcefile~disentangle.f90->sourcefile~wannier90_types.f90 sourcefile~sitesym.f90 sitesym.F90 sourcefile~disentangle.f90->sourcefile~sitesym.f90 sourcefile~utility.f90 utility.F90 sourcefile~disentangle.f90->sourcefile~utility.f90 sourcefile~error.f90->sourcefile~comms.f90 sourcefile~error.f90->sourcefile~error_base.f90 sourcefile~io.f90->sourcefile~comms.f90 sourcefile~io.f90->sourcefile~constants.f90 sourcefile~io.f90->sourcefile~error_base.f90 sourcefile~io.f90->sourcefile~types.f90 sourcefile~kmesh.f90->sourcefile~comms.f90 sourcefile~kmesh.f90->sourcefile~constants.f90 sourcefile~kmesh.f90->sourcefile~error.f90 sourcefile~kmesh.f90->sourcefile~io.f90 sourcefile~kmesh.f90->sourcefile~types.f90 sourcefile~kmesh.f90->sourcefile~utility.f90 sourcefile~overlap.f90->sourcefile~comms.f90 sourcefile~overlap.f90->sourcefile~constants.f90 sourcefile~overlap.f90->sourcefile~error.f90 sourcefile~overlap.f90->sourcefile~io.f90 sourcefile~overlap.f90->sourcefile~types.f90 sourcefile~overlap.f90->sourcefile~wannier90_types.f90 sourcefile~overlap.f90->sourcefile~sitesym.f90 sourcefile~overlap.f90->sourcefile~utility.f90 sourcefile~plot.f90->sourcefile~comms.f90 sourcefile~plot.f90->sourcefile~constants.f90 sourcefile~plot.f90->sourcefile~error.f90 sourcefile~plot.f90->sourcefile~io.f90 sourcefile~plot.f90->sourcefile~types.f90 sourcefile~plot.f90->sourcefile~wannier90_types.f90 sourcefile~hamiltonian.f90 hamiltonian.F90 sourcefile~plot.f90->sourcefile~hamiltonian.f90 sourcefile~plot.f90->sourcefile~utility.f90 sourcefile~ws_distance.f90 ws_distance.F90 sourcefile~plot.f90->sourcefile~ws_distance.f90 sourcefile~readwrite.f90->sourcefile~comms.f90 sourcefile~readwrite.f90->sourcefile~constants.f90 sourcefile~readwrite.f90->sourcefile~error.f90 sourcefile~readwrite.f90->sourcefile~io.f90 sourcefile~readwrite.f90->sourcefile~types.f90 sourcefile~readwrite.f90->sourcefile~utility.f90 sourcefile~transport.f90->sourcefile~comms.f90 sourcefile~transport.f90->sourcefile~constants.f90 sourcefile~transport.f90->sourcefile~error.f90 sourcefile~transport.f90->sourcefile~io.f90 sourcefile~transport.f90->sourcefile~types.f90 sourcefile~transport.f90->sourcefile~wannier90_types.f90 sourcefile~transport.f90->sourcefile~hamiltonian.f90 sourcefile~types.f90->sourcefile~constants.f90 sourcefile~wannier90_readwrite.f90->sourcefile~constants.f90 sourcefile~wannier90_readwrite.f90->sourcefile~error.f90 sourcefile~wannier90_readwrite.f90->sourcefile~io.f90 sourcefile~wannier90_readwrite.f90->sourcefile~readwrite.f90 sourcefile~wannier90_readwrite.f90->sourcefile~types.f90 sourcefile~wannier90_readwrite.f90->sourcefile~wannier90_types.f90 sourcefile~wannier90_readwrite.f90->sourcefile~utility.f90 sourcefile~wannier90_types.f90->sourcefile~constants.f90 sourcefile~wannierise.f90->sourcefile~comms.f90 sourcefile~wannierise.f90->sourcefile~constants.f90 sourcefile~wannierise.f90->sourcefile~error.f90 sourcefile~wannierise.f90->sourcefile~io.f90 sourcefile~wannierise.f90->sourcefile~types.f90 sourcefile~wannierise.f90->sourcefile~wannier90_readwrite.f90 sourcefile~wannierise.f90->sourcefile~wannier90_types.f90 sourcefile~wannierise.f90->sourcefile~hamiltonian.f90 sourcefile~wannierise.f90->sourcefile~sitesym.f90 sourcefile~wannierise.f90->sourcefile~utility.f90 sourcefile~hamiltonian.f90->sourcefile~comms.f90 sourcefile~hamiltonian.f90->sourcefile~constants.f90 sourcefile~hamiltonian.f90->sourcefile~error.f90 sourcefile~hamiltonian.f90->sourcefile~io.f90 sourcefile~hamiltonian.f90->sourcefile~types.f90 sourcefile~hamiltonian.f90->sourcefile~wannier90_types.f90 sourcefile~hamiltonian.f90->sourcefile~utility.f90 sourcefile~sitesym.f90->sourcefile~comms.f90 sourcefile~sitesym.f90->sourcefile~constants.f90 sourcefile~sitesym.f90->sourcefile~error.f90 sourcefile~sitesym.f90->sourcefile~wannier90_types.f90 sourcefile~sitesym.f90->sourcefile~utility.f90 sourcefile~utility.f90->sourcefile~comms.f90 sourcefile~utility.f90->sourcefile~constants.f90 sourcefile~utility.f90->sourcefile~error.f90 sourcefile~ws_distance.f90->sourcefile~comms.f90 sourcefile~ws_distance.f90->sourcefile~constants.f90 sourcefile~ws_distance.f90->sourcefile~error.f90 sourcefile~ws_distance.f90->sourcefile~io.f90 sourcefile~ws_distance.f90->sourcefile~types.f90 sourcefile~ws_distance.f90->sourcefile~utility.f90

Files dependent on this one

sourcefile~~library_interface.f90~~AfferentGraph sourcefile~library_interface.f90 library_interface.F90 sourcefile~c_interface.f90 c_interface.F90 sourcefile~c_interface.f90->sourcefile~library_interface.f90 sourcefile~library_extra.f90 library_extra.F90 sourcefile~library_extra.f90->sourcefile~library_interface.f90 sourcefile~wannier_prog.f90 wannier_prog.F90 sourcefile~wannier_prog.f90->sourcefile~library_interface.f90 sourcefile~wannier_prog.f90->sourcefile~library_extra.f90

Source Code

!-*- mode: F90 -*-!
!------------------------------------------------------------!
!                                                            !
!                       WANNIER90                            !
!                                                            !
!          The Maximally-Localised Generalised               !
!                 Wannier Functions Code                     !
!                                                            !
! Please cite                                                !
!                                                            !
!  [ref] "Wannier90 as a community code:                     !
!        new features and applications",                     !
!        G. Pizzi et al.,  J. Phys. Cond. Matt. 32,          !
!        165902 (2020).                                      !
!        http://doi.org/10.1088/1361-648X/ab51ff             !
!                                                            !
! in any publications arising from the use of this code.     !
!                                                            !
! Wannier90 is based on Wannier77, written by N. Marzari,    !
! I. Souza and D. Vanderbilt. For the method please cite     !
!                                                            !
! [ref] N. Marzari and D. Vanderbilt,                        !
!       Phys. Rev. B 56 12847 (1997)                         !
!       http://dx.doi.org/10.1103/PhysRevB.56.12847          !
!                                                            !
! [ref] I. Souza, N. Marzari and D. Vanderbilt,              !
!       Phys. Rev. B 65 035109 (2001)                        !
!       http://dx.doi.org/10.1103/PhysRevB.65.035109         !
!                                                            !
! [ref] N. Marzari, A. A. Mostofi, J. R. Yates, I. Souza,    !
!       D. Vanderbilt, "Maximally localized Wannier          !
!       functions: theory and applications",                 !
!       Rev. Mod. Phys. 84, 1419 (2012)                      !
!       http://dx.doi.org/10.1103/RevModPhys.84.1419         !
!                                                            !
! For a full list of authors and contributors, please        !
! see the README file in the root directory of the           !
! distribution.                                              !
!                                                            !
! 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>          !
!------------------------------------------------------------!

module w90_library
  ! as with fortran routines like allocate, the status variable indicates an error if non-zero
  ! positive is an error, negative is a warning (such as non-convergence) which is recoverable.

  ! note on the naming of output and error-output streams
  !   the specific names stdout and stderr cannot be used because
  !   the python wrapper uses an intermediate c representation with named arguments
  !   in this wrapper stdout/err are already defined as structs inconsistent
  !   which are inconsistent with the integer unit numbers used here.
  !   (The slightly 77 style naming here has no particular significance.)

  use w90_constants, only: dp, w90_physical_constants_type
  use w90_types
  use w90_wannier90_types
  use w90_comms, only: w90_comm_type
  use w90_io, only: prterr
  use iso_c_binding

  implicit none

  private :: dp ! avoid polluting calling program's namespace (dp is defined in w90_constants)
  private :: prterr

  ! datatype encapsulating types used by wannier90
  type lib_common_type
    character(len=50) :: seedname ! size=50 is a convention here
    !! base name for reading/writing of files

    ! matrices
    complex(kind=dp), allocatable :: ham_k(:, :, :)
    !! KS Hamiltonian, takes size (number of WF, number of WF, number of k-points)
    complex(kind=dp), allocatable :: ham_r(:, :, :)
    !! RS Hamiltonian, takes size (number of WF, number of WF, number of RS-points)
    complex(kind=dp), pointer :: a_matrix(:, :, :) => null()
    !! pointer to matrix "a" (projections), dimensioned (number of bands, number WF, number of k-points)
    complex(kind=dp), pointer :: m_matrix_local(:, :, :, :) => null()
    !! pointer to rank local part of "m", dimensioned (number of bands, number of bands, FD neighbours, number of k-points on this rank)
    complex(kind=dp), pointer :: u_matrix(:, :, :) => null()
    !! pointer to matrix "u", dimensionsed (number of WF, number of WF, number of k-points)
    complex(kind=dp), pointer :: u_matrix_opt(:, :, :) => null()
    !! pointer to matrix "u_matrix_opt", dimensioned (number of bands, number of WF, number of k-points)
    real(kind=dp), pointer :: eigval(:, :) => null()
    !! pointer to eigenvalue array dimensioned (number of bands, number of k-points)

    integer, allocatable :: dist_kpoints(:)
    !! distribution of k-points; dist_kpoints(i) = rank operating on k-point i
    integer, allocatable :: exclude_bands(:)
    !! index of bands to exclude from the calculation: passed to DFT code to define space for overlaps, projections, etc
    integer, allocatable :: irvec(:, :)
    integer, allocatable :: ndegen(:)
    integer, allocatable :: shift_vec(:, :)

    integer :: mp_grid(3)
    !! Dimensions of the Monkhorst-Pack grid
    integer :: nrpts
    !! Dimensions of the RS grid
    integer :: num_bands
    !! Number of bands (greater or equal to number of WFs)
    integer :: num_kpts
    !! Total number of k-points
    integer :: num_proj = 0
    !! Number of projectors defined
    integer :: num_wann
    !! Number of WFs
    integer :: optimisation = 3
    !! Algorithm control (affects memory use/speed)
    integer :: rpt_origin

    logical :: calc_only_A = .false.
    logical :: gamma_only = .false.
    !! Select gamma_only branch of some algorithms
    logical :: have_disentangled = .false.
    !! Flag that disentanglment has been performed
    logical :: lhasproj = .false.
    !! Flag that projectors are defined
    logical :: lsitesymmetry = .false.
    !! Flag that symmetry-adapted WFs are to be calculated
    logical :: use_bloch_phases = .false.
    !! Flag to bypass disentanglement
    logical :: setup_complete = .false.
    !! Internal flag to indicate completion of setup

    real(kind=dp), allocatable :: fermi_energy_list(:)
    !! Array of energies around the Fermi energy
    real(kind=dp), allocatable :: kpt_latt(:, :)
    !! Tabulation of k-points in crystal coordinates
    real(kind=dp), allocatable :: wannier_centres_translated(:, :)
    real(kind=dp) :: real_lattice(3, 3)

    ! See types.F90 and wannier90_types.F90 for the use and composition of the different types
    type(atom_data_type) :: atom_data
    type(band_plot_type) :: band_plot
    type(dis_control_type) :: dis_control
    type(dis_manifold_type) :: dis_manifold
    type(dis_spheres_type) :: dis_spheres
    type(fermi_surface_plot_type) :: fermi_surface_data
    type(ham_logical_type) :: ham_logical
    type(kmesh_info_type) :: kmesh_info
    type(kmesh_input_type) :: kmesh_input
    type(kpoint_path_type) :: kpoint_path
    type(output_file_type) :: output_file
    type(print_output_type) :: print_output
    type(proj_type), allocatable :: proj(:), proj_input(:)
    type(real_space_ham_type) :: real_space_ham
    type(select_projection_type) :: select_proj
    type(settings_type) :: settings
    !! container for input file (.win) data and options set via library interface
    type(sitesym_type) :: sitesym
    type(timer_list_type) :: timer
    type(transport_type) :: tran
    type(w90_calculation_type) :: w90_calculation
    type(w90_comm_type) :: comm
    type(w90_physical_constants_type) :: physics
    type(w90_system_type) :: w90_system
    type(wann_control_type) :: wann_control
    type(wannier_data_type) :: wannier_data
    type(wannier_plot_type) :: wann_plot
    type(wann_omega_type) :: omega
    type(wann_omega_type) :: wann_omega
    type(ws_region_type) :: ws_region
    type(wvfn_read_type) :: wvfn_read
  end type lib_common_type
  !! container type for all variables used by the Wannier90 library

  public :: w90_print_info
  !! prints a wide variety of simulation parameters to stdout
  public :: w90_print_timings
  !! prints a decorative summary of disentanglement/mlwf/plot timings
  public :: w90_create_kmesh
  ! trigers the generation of k-mesh info (as do get_nn*)
  ! this is called by get_nnkp and get_gkpb
  public :: w90_disentangle
  !! perform disentanglement
  public :: w90_distribute_kpts
  !! provides an MPI k-point distribution for codes that don't have one
  public :: w90_get_centres
  !! get wannier centers
  public :: w90_get_fortran_file
  !! open a file and get the corresponding unit number
  public :: w90_get_fortran_stderr
  !! get a fortran unit number corresponding to standard error
  public :: w90_get_fortran_stdout
  !! get a fortran unit number corresponding to standard output
  public :: w90_get_proj
  !! get projection info (after interpreting projector specification strings)
  public :: w90_get_spreads
  !! get spreads
  public :: w90_get_nnkp
  !! get k' indexes for finite-difference scheme
  public :: w90_get_nn
  !! get number of b-vectors (finite-difference points)
  public :: w90_get_gkpb
  !! get g offsets of k'
  public :: w90_get_num_excl_bands
  !! number of excluded bands, for sizing array arg of w90_get_excl_bands
  public :: w90_get_excl_bands
  !! list of indices of excluded bands (which may be possibly non-contiguous)
  public :: w90_input_reader
  !! optionally read additional input variables from .win file
  public :: w90_input_setopt
  !! act upon (interpret & setup) options specified by set_option interface
  public :: w90_plot
  !! performs plot functions
  public :: w90_project_overlap
  !! transform overlaps and initial projections
  public :: w90_set_comm
  !! setup MPI communicator in parallel case
  public :: w90_set_constant_bohr_to_ang
  !! set value of Bohr/Angstrom conversion
  public :: w90_set_eigval
  !! set (pointer to) eigenvalues
  public :: w90_set_m_local
  !! set (pointer to) m (potentially MPI decomposed by k-points)
  public :: w90_set_option
  !! specify options to the library; set_option is overloaded to accept arguments of various types
  public :: w90_set_u_matrix
  !! set (pointer to)  u matrix (nw,nw)
  public :: w90_set_u_opt
  !! set (pointer to) optimised (disentangled) u matrix (nb,nb)
  public :: w90_transport
  !! perform transport functions
  public :: w90_wannierise
  !! perform wannierisation

  interface w90_set_option
    module procedure w90_set_option_logical
    !module procedure w90_set_option_b1d
    module procedure w90_set_option_text
    module procedure w90_set_option_int
    module procedure w90_set_option_i1d
    module procedure w90_set_option_i2d
    module procedure w90_set_option_r1d
    module procedure w90_set_option_r2d
    module procedure w90_set_option_c2d
    module procedure w90_set_option_real
  end interface w90_set_option

contains

  subroutine w90_get_fortran_stdout(istdout)
    !! fortran unit number for stdout
    use iso_fortran_env, only: output_unit
    implicit none
    integer, intent(out) :: istdout
    istdout = output_unit
  end subroutine w90_get_fortran_stdout

  subroutine w90_get_fortran_stderr(istdout)
    !! fortran unit number for stderr
    use iso_fortran_env, only: error_unit
    implicit none
    integer, intent(out) :: istdout
    istdout = error_unit
  end subroutine w90_get_fortran_stderr

  subroutine w90_get_fortran_file(output, name)
    !! open a (formatted) file and return the corresponding fortran unit number
    implicit none
    integer, intent(out) :: output
    character(len=*), intent(in) :: name
    open (newunit=output, file=name, form='formatted', status='unknown')
  end subroutine w90_get_fortran_file

  subroutine w90_input_setopt(common_data, seedname, istdout, istderr, ierr)
    !! mechanism to act upon options supplied to the library
    !! input is parsed and interpreted (any errors are identified) and
    !! the library data structure (variable common_data) is populated ready for use

    ! w90_input_setopt() processes options stored in common_data%settings
    ! w90_input_reader() processes options stored in common_data%in_data (from .win file, should be empty here)

#ifdef W90_MPI08
    use mpi_f08
#endif
#ifdef W90_MPI90
    use mpi
#endif

    use w90_error_base, only: w90_error_type
    use w90_error, only: set_error_alloc, set_error_fatal, code_mpi
    use w90_comms, only: w90_comm_type, valid_communicator, mpisize, mpirank
    use w90_readwrite, only: w90_readwrite_write_win
    use w90_wannier90_readwrite, only: w90_wannier90_readwrite_read, &
                                       w90_wannier90_readwrite_read_special
    use w90_overlap, only: overlap_write

    implicit none

#ifdef W90_MPIH
    include 'mpif.h'
#endif

    ! arguments
    character(len=*), intent(in) :: seedname
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data
    !! instance of library data object, modified here

    ! local variables
    type(w90_error_type), allocatable :: error
    logical :: cp_pp

    ierr = 0

    if (.not. valid_communicator(common_data%comm)) then ! when MPI is not defined, valid_communicator returns true (see comms.F90)
      ! this is a problem: how do we exit using the parallel error handler when the communicator is unknown?
      write (istderr, *) ' Error: parallel Wannier90 library invoked with invalid communicator, exiting.  Use w90_set_comm()!'
      ierr = code_mpi
      return
    end if

    if (allocated(common_data%settings%in_data)) then
      call set_error_fatal(error, &
                           ' Error: w90_read_input() and w90_set_option() clash at w90_input_setopt() call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (.not. allocated(common_data%settings%entries)) then
      call set_error_fatal(error, ' Error: w90_input_setopt() called with no input set', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
      !else if (.not. allocated(common_data%dist_kpoints)) then
      !  call set_error_fatal(error, ' input_setopt called but distk unallocated', common_data%comm)
      !  call prterr(error, ierr, istdout, istderr, common_data%comm)
      !  return
    end if

    common_data%seedname = seedname ! set seedname for input/output files

    ! read_special can only be executed once per library object
    ! it sets key variables (eg, number of k-points sizing allocations, etc)
    call w90_wannier90_readwrite_read_special(common_data%settings, common_data%atom_data, &
                                              common_data%kmesh_input, common_data%kmesh_info, &
                                              common_data%kpt_latt, common_data%wann_control, &
                                              common_data%proj, common_data%proj_input, &
                                              common_data%select_proj, common_data%w90_system, &
                                              common_data%w90_calculation, &
                                              common_data%real_lattice, common_data%physics%bohr, &
                                              common_data%mp_grid, common_data%num_bands, &
                                              common_data%exclude_bands, &
                                              common_data%num_kpts, common_data%num_proj, &
                                              common_data%num_wann, common_data%gamma_only, &
                                              common_data%lhasproj, &
                                              common_data%use_bloch_phases, &
                                              common_data%dist_kpoints, istdout, error, &
                                              common_data%comm)
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    ! condition for disentanglement is number of bands > number of WF
    if (common_data%num_bands > common_data%num_wann) then
      allocate (common_data%dis_manifold%ndimwin(common_data%num_kpts), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error allocating ndimwin in w90_input_setopt() library call', common_data%comm)
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if
      allocate (common_data%dis_manifold%nfirstwin(common_data%num_kpts), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error allocating nfirstwin in w90_input_setopt() library call', common_data%comm)
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if
      allocate (common_data%dis_manifold%lwindow(common_data%num_bands, common_data%num_kpts), stat=ierr)
      if (ierr /= 0) then
        call set_error_alloc(error, 'Error allocating lwindow in w90_input_setopt() library call', common_data%comm)
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if
    end if

    allocate (common_data%wannier_data%centres(3, common_data%num_wann), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error allocating wannier_centres in w90_input_setopt() library call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
    common_data%wannier_data%centres = 0.0_dp

    allocate (common_data%wannier_data%spreads(common_data%num_wann), stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in allocating wannier_spreads in w90_input_setopt() library call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
    common_data%wannier_data%spreads = 0.0_dp

    ! read all other variables; mostly simple variables can be set and/or reset
    call w90_wannier90_readwrite_read(common_data%settings, common_data%band_plot, &
                                      common_data%dis_control, common_data%dis_spheres, &
                                      common_data%dis_manifold, common_data%fermi_energy_list, &
                                      common_data%fermi_surface_data, common_data%output_file, &
                                      common_data%wvfn_read, common_data%wann_control, &
                                      common_data%real_space_ham, common_data%kpoint_path, &
                                      common_data%w90_system, common_data%tran, &
                                      common_data%print_output, common_data%wann_plot, &
                                      common_data%ws_region, common_data%real_lattice, &
                                      common_data%w90_calculation, common_data%physics%bohr, &
                                      common_data%sitesym%symmetrize_eps, common_data%num_bands, &
                                      common_data%num_kpts, common_data%num_wann, &
                                      common_data%optimisation, common_data%calc_only_A, cp_pp, &
                                      common_data%gamma_only, common_data%lsitesymmetry, &
                                      common_data%use_bloch_phases, common_data%seedname, istdout, &
                                      error, common_data%comm)
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    if (common_data%output_file%write_win_ammats) then
      if (mpirank(common_data%comm) == 0) then
        call w90_readwrite_write_win(common_data%settings, seedname, error, common_data%comm)
      end if
      if (allocated(error)) then
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if
    end if

    ! clear settings container (from settings interface not .win file)
    deallocate (common_data%settings%entries, stat=ierr)
    if (ierr /= 0) then
      call set_error_alloc(error, 'Error in deallocating entries data in w90_input_setopt() library call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
  end subroutine w90_input_setopt

  subroutine w90_input_reader(common_data, istdout, istderr, ierr)
    !! mechanism to act upon tokens read from the input ".win" file
    !! input is parsed and interpreted (any errors are identified) and
    !! the library data structure (variable common_data), already setup by w90_input_setopt, is modified

    ! w90_input_setopt() processes options stored in common_data%settings (must be empty here; emptied by w90_input_setopt call)
    ! w90_input_reader() processes options stored in common_data%in_data (from .win file)

    use w90_comms, only: valid_communicator
    use w90_error_base, only: w90_error_type
    use w90_error, only: set_error_input, set_error_fatal, set_error_alloc, code_mpi
    use w90_readwrite, only: w90_readwrite_in_file, w90_readwrite_clean_infile
    use w90_wannier90_readwrite, only: w90_wannier90_readwrite_read

    implicit none

    ! arguments
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data

    ! local variables
    type(w90_error_type), allocatable :: error
    logical :: cp_pp

    ierr = 0

    if (.not. valid_communicator(common_data%comm)) then
      ! this is a problem: how do we exit using the parallel error handler when the communicator is unknown?
      write (istderr, *) ' Error: parallel Wannier90 library invoked with invalid communicator, exiting.  Use w90_set_comm()!'
      ierr = code_mpi
      return
    end if

    if (allocated(common_data%settings%entries)) then
      call set_error_fatal(error, &
                           'Error: input reader called when unspent options present (setopt must be called first)', &
                           common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    ! read data from .win file to internal string array
    call w90_readwrite_in_file(common_data%settings, common_data%seedname, error, common_data%comm)
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    ! set options corresponding to string array from .win file
    call w90_wannier90_readwrite_read(common_data%settings, common_data%band_plot, &
                                      common_data%dis_control, common_data%dis_spheres, &
                                      common_data%dis_manifold, common_data%fermi_energy_list, &
                                      common_data%fermi_surface_data, common_data%output_file, &
                                      common_data%wvfn_read, common_data%wann_control, &
                                      common_data%real_space_ham, common_data%kpoint_path, &
                                      common_data%w90_system, common_data%tran, &
                                      common_data%print_output, common_data%wann_plot, &
                                      common_data%ws_region, common_data%real_lattice, &
                                      common_data%w90_calculation, common_data%physics%bohr, &
                                      common_data%sitesym%symmetrize_eps, common_data%num_bands, &
                                      common_data%num_kpts, common_data%num_wann, &
                                      common_data%optimisation, common_data%calc_only_A, cp_pp, &
                                      common_data%gamma_only, common_data%lsitesymmetry, &
                                      common_data%use_bloch_phases, common_data%seedname, &
                                      istdout, error, common_data%comm)
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    ! remove any remaining acceptable keywords; anything that remains is an input error
    call w90_readwrite_clean_infile(common_data%settings, istdout, common_data%seedname, error, &
                                    common_data%comm)
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    if (allocated(common_data%settings%in_data)) deallocate (common_data%settings%in_data)
  end subroutine w90_input_reader

  subroutine w90_disentangle(common_data, istdout, istderr, ierr)
    !! perform disentanglement; assumes library data object is already setup after w90_input_setopt() call
    !! no effect if number of bands == number of WF

    use w90_disentangle_mod, only: dis_main, setup_m_loc
    use w90_error_base, only: w90_error_type
    use w90_error, only: set_error_fatal
    use w90_overlap, only: overlap_write

    implicit none

    ! arguments
    type(lib_common_type), intent(inout) :: common_data
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr

    ! local variables
    type(w90_error_type), allocatable :: error

    ierr = 0

    ! m_matrix_orig_local (nband*nwann for disentangle)
    if (.not. associated(common_data%m_matrix_local)) then ! (nband*nwann*nknode for wannierise)
      call set_error_fatal(error, 'Error: m_matrix_local not associated for w90_disentangle() call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (.not. associated(common_data%u_matrix)) then
      call set_error_fatal(error, 'Error: u_matrix not associated for w90_disentangle() call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (.not. associated(common_data%u_matrix_opt)) then
      call set_error_fatal(error, 'Error: u_matrix_opt not associated for w90_disentangle() call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (.not. associated(common_data%eigval)) then
      call set_error_fatal(error, 'Error: eigval not associated for w90_disentangle() call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    ! if not already initialised, set disentanglement window to limits of spectrum
    if (common_data%dis_manifold%win_min == -huge(0.0_dp)) common_data%dis_manifold%win_min = minval(common_data%eigval)
    if (common_data%dis_manifold%win_max == huge(0.0_dp)) common_data%dis_manifold%win_max = maxval(common_data%eigval)
    if (common_data%dis_manifold%frozen_states) then
      if (common_data%dis_manifold%froz_min == -huge(0.0_dp)) then
        common_data%dis_manifold%froz_min = minval(common_data%eigval(:, :))
      end if
    end if

    if (common_data%output_file%write_win_ammats) then
      ! for writing input m,a matrices
      call overlap_write(common_data%kmesh_info, common_data%u_matrix_opt, common_data%m_matrix_local, &
                         common_data%eigval, common_data%num_bands, common_data%num_kpts, &
                         common_data%num_proj, common_data%dist_kpoints, common_data%seedname, error, &
                         common_data%comm)
      if (allocated(error)) then
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if
    end if

    ! condition for disentanglement is number of bands > number of WF
    if (common_data%num_bands > common_data%num_wann) then
      call dis_main(common_data%dis_control, common_data%dis_spheres, common_data%dis_manifold, &
                    common_data%kmesh_info, common_data%kpt_latt, common_data%sitesym, &
                    common_data%print_output, common_data%m_matrix_local, common_data%u_matrix, &
                    common_data%u_matrix_opt, common_data%eigval, common_data%real_lattice, &
                    common_data%omega%invariant, common_data%num_bands, common_data%num_kpts, &
                    common_data%num_wann, common_data%gamma_only, common_data%lsitesymmetry, &
                    istdout, common_data%timer, common_data%dist_kpoints, error, common_data%comm)
      if (allocated(error)) then
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if

      call setup_m_loc(common_data%kmesh_info, common_data%print_output, common_data%m_matrix_local, &
                       common_data%m_matrix_local, common_data%u_matrix, common_data%num_bands, &
                       common_data%num_kpts, common_data%num_wann, common_data%timer, &
                       common_data%dist_kpoints, error, common_data%comm)
      if (allocated(error)) then
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if

      common_data%have_disentangled = .true.
    end if
  end subroutine w90_disentangle

  subroutine w90_project_overlap(common_data, istdout, istderr, ierr)
    use w90_error_base, only: w90_error_type
    use w90_error, only: set_error_fatal
    use w90_overlap, only: overlap_project, overlap_project_gamma
    use w90_overlap, only: overlap_write

    implicit none

    ! arguments
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data

    ! local variables
    type(w90_error_type), allocatable :: error
    integer :: ik, iw

    ierr = 0

    if (.not. associated(common_data%m_matrix_local)) then
      call set_error_fatal(error, 'm_matrix_local not set for w90_project_overlap call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (.not. associated(common_data%u_matrix_opt)) then
      call set_error_fatal(error, 'u_matrix_opt not set for w90_project_overlap call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (.not. associated(common_data%u_matrix)) then
      call set_error_fatal(error, 'u_matrixt not set for w90_project_overlap call', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    if (common_data%output_file%write_win_ammats .and. .not. common_data%have_disentangled) then
      ! for writing input m,a matrices
      call overlap_write(common_data%kmesh_info, common_data%u_matrix_opt, common_data%m_matrix_local, &
                         common_data%eigval, common_data%num_bands, common_data%num_kpts, &
                         common_data%num_proj, common_data%dist_kpoints, common_data%seedname, error, &
                         common_data%comm)
    end if

    if (.not. common_data%have_disentangled) then
      if (common_data%num_wann /= common_data%num_bands) then
        call set_error_fatal(error, 'Error: w90_project_overlap(): num_bands /= num_wann but disentanglement() was not called', &
                             common_data%comm)
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if

      ! fixme, document!
      common_data%u_matrix(:, :, :) = common_data%u_matrix_opt(:, :, :) ! u_matrix_opt contains initial projections
      common_data%u_matrix_opt(:, :, :) = 0.d0
      do ik = 1, common_data%num_kpts
        do iw = 1, common_data%num_wann
          common_data%u_matrix_opt(iw, iw, ik) = 1.d0
        end do
      end do

      if (common_data%gamma_only) then
        call overlap_project_gamma(common_data%m_matrix_local, common_data%u_matrix, &
                                   common_data%kmesh_info%nntot, common_data%num_wann, &
                                   common_data%print_output%timing_level, istdout, &
                                   common_data%timer, error, common_data%comm)
      else
        call overlap_project(common_data%sitesym, common_data%m_matrix_local, common_data%u_matrix, &
                             common_data%kmesh_info%nnlist, common_data%kmesh_info%nntot, &
                             common_data%num_wann, common_data%num_kpts, common_data%num_wann, &
                             common_data%print_output%timing_level, common_data%lsitesymmetry, &
                             istdout, common_data%timer, common_data%dist_kpoints, error, &
                             common_data%comm)
      end if
      if (allocated(error)) then
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if
    end if
  end subroutine w90_project_overlap

  subroutine w90_wannierise(common_data, istdout, istderr, ierr)
    ! perform MLWF algorithm

    use w90_comms, only: mpirank, comms_sync_error
    use w90_error_base, only: w90_error_type
    use w90_error, only: set_error_fatal
    use w90_wannierise_mod, only: wann_main, wann_main_gamma

    implicit none

    ! arguments
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data

    ! local variables
    type(w90_error_type), allocatable :: error

    ierr = 0

    if (.not. associated(common_data%m_matrix_local)) then
      call set_error_fatal(error, 'Error: m_matrix_local not set for call to w90_wannierise()', common_data%comm)
    else if (.not. associated(common_data%u_matrix)) then
      call set_error_fatal(error, 'Error: u_matrix not set for w90_wannierise()', common_data%comm)
    end if
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    if (common_data%gamma_only) then
      if (mpirank(common_data%comm) == 0) then
        call wann_main_gamma(common_data%kmesh_info, common_data%wann_control, common_data%omega, &
                             common_data%print_output, common_data%wannier_data, &
                             common_data%m_matrix_local, common_data%u_matrix, &
                             common_data%real_lattice, common_data%num_kpts, common_data%num_wann, &
                             istdout, common_data%timer, error, common_data%comm)
        if (allocated(error)) then
          call prterr(error, ierr, istdout, istderr, common_data%comm)
          return
        end if
      end if
      call comms_sync_error(common_data%comm, error, 0) ! this is necessary after root's excursion alone
      if (allocated(error)) then
        call prterr(error, ierr, istdout, istderr, common_data%comm)
        return
      end if
    else
      call wann_main(common_data%ham_logical, common_data%kmesh_info, common_data%kpt_latt, &
                     common_data%wann_control, common_data%omega, common_data%sitesym, &
                     common_data%print_output, common_data%wannier_data, common_data%ws_region, &
                     common_data%w90_calculation, common_data%ham_k, common_data%ham_r, &
                     common_data%m_matrix_local, common_data%u_matrix, common_data%real_lattice, &
                     common_data%wannier_centres_translated, common_data%irvec, &
                     common_data%mp_grid, common_data%ndegen, common_data%nrpts, &
                     common_data%num_kpts, common_data%num_proj, common_data%num_wann, &
                     common_data%optimisation, common_data%rpt_origin, common_data%band_plot%mode, &
                     common_data%tran%mode, common_data%lsitesymmetry, istdout, common_data%timer, &
                     common_data%dist_kpoints, error, common_data%comm)
    end if
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
  end subroutine w90_wannierise

  subroutine w90_plot(common_data, istdout, istderr, ierr)
    !! performs a variety of plotting functions

    use w90_error_base, only: w90_error_type
    use w90_plot_mod, only: plot_main

    implicit none

    ! arguments
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data ! inout due to ham_logical only

    ! local variables
    type(w90_error_type), allocatable :: error

    ierr = 0

    ! fixme(jj) what are our preconditions?

    call plot_main(common_data%atom_data, common_data%band_plot, common_data%dis_manifold, &
                   common_data%fermi_energy_list, common_data%fermi_surface_data, &
                   common_data%ham_logical, common_data%kmesh_info, common_data%kpt_latt, &
                   common_data%output_file, common_data%wvfn_read, common_data%real_space_ham, &
                   common_data%kpoint_path, common_data%print_output, common_data%wannier_data, &
                   common_data%wann_plot, common_data%ws_region, common_data%w90_calculation, &
                   common_data%ham_k, common_data%ham_r, common_data%m_matrix_local, &
                   common_data%u_matrix, common_data%u_matrix_opt, common_data%eigval, &
                   common_data%real_lattice, common_data%wannier_centres_translated, &
                   common_data%physics%bohr, common_data%irvec, common_data%mp_grid, &
                   common_data%ndegen, common_data%shift_vec, common_data%nrpts, &
                   common_data%num_bands, common_data%num_kpts, common_data%num_wann, &
                   common_data%rpt_origin, common_data%tran%mode, common_data%have_disentangled, &
                   common_data%lsitesymmetry, common_data%w90_system, common_data%seedname, &
                   istdout, common_data%timer, common_data%dist_kpoints, error, common_data%comm)
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
  end subroutine w90_plot

  subroutine w90_transport(common_data, istdout, istderr, ierr)
    !! performs a variety of transport calculations

    use w90_comms, only: mpirank, comms_sync_error
    use w90_error_base, only: w90_error_type
    use w90_transport_mod, only: tran_main

    implicit none

    ! arguments
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data ! because of ham_logical

    ! local variables
    type(w90_error_type), allocatable :: error

    ierr = 0

    ! fixme(jj) what are our preconditions?

    ! currently tran_main is entirely serial
    if (mpirank(common_data%comm) == 0) then
      call tran_main(common_data%atom_data, common_data%dis_manifold, &
                     common_data%fermi_energy_list, common_data%ham_logical, common_data%kpt_latt, &
                     common_data%output_file, common_data%real_space_ham, common_data%tran, &
                     common_data%print_output, common_data%wannier_data, common_data%ws_region, &
                     common_data%w90_calculation, common_data%ham_k, common_data%ham_r, &
                     common_data%u_matrix, common_data%u_matrix_opt, common_data%eigval, &
                     common_data%real_lattice, common_data%wannier_centres_translated, &
                     common_data%irvec, common_data%mp_grid, common_data%ndegen, &
                     common_data%shift_vec, common_data%nrpts, common_data%num_bands, &
                     common_data%num_kpts, common_data%num_wann, common_data%rpt_origin, &
                     common_data%band_plot%mode, common_data%have_disentangled, &
                     common_data%lsitesymmetry, common_data%seedname, istdout, common_data%timer, &
                     error, common_data%comm)
    end if
    call comms_sync_error(common_data%comm, error, 0) ! this is necessary after root's excursion alone
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
  end subroutine w90_transport

  subroutine w90_set_m_local(common_data, m_matrix_local) ! m_matrix_local_orig
    implicit none

    type(lib_common_type), intent(inout) :: common_data
    complex(kind=dp), intent(inout), target :: m_matrix_local(:, :, :, :)

    common_data%m_matrix_local => m_matrix_local
  end subroutine w90_set_m_local

  subroutine w90_set_u_matrix(common_data, u_matrix)
    implicit none

    type(lib_common_type), intent(inout) :: common_data
    complex(kind=dp), intent(inout), target :: u_matrix(:, :, :)

    common_data%u_matrix => u_matrix
  end subroutine w90_set_u_matrix

  subroutine w90_set_u_opt(common_data, u_matrix_opt)
    implicit none

    type(lib_common_type), intent(inout) :: common_data
    complex(kind=dp), intent(inout), target :: u_matrix_opt(:, :, :)

    common_data%u_matrix_opt => u_matrix_opt
  end subroutine w90_set_u_opt

  subroutine w90_set_eigval(common_data, eigval)
    implicit none

    type(lib_common_type), intent(inout) :: common_data
    real(kind=dp), intent(in), target :: eigval(:, :)

    common_data%eigval => eigval
  end subroutine w90_set_eigval

  subroutine w90_set_constant_bohr_to_ang(common_data, bohr_to_angstrom)
    !! used to set the bohr_to_angstrom value as used in the SCF code
    implicit none

    type(lib_common_type), intent(inout) :: common_data
    real(kind=dp), intent(in) :: bohr_to_angstrom

    common_data%physics%bohr = bohr_to_angstrom
    common_data%physics%bohr_version_str = "-> Using Bohr value from linked main code"
  end subroutine w90_set_constant_bohr_to_ang

  subroutine w90_create_kmesh(common_data, istdout, istderr, ierr)
    !! causes w90 to calculate finite difference neighbour lists
    use w90_error_base, only: w90_error_type
    use w90_kmesh, only: kmesh_get

    implicit none

    ! arguments
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data

    ! local variables
    type(w90_error_type), allocatable :: error

    ierr = 0
    if (common_data%setup_complete) return

    call kmesh_get(common_data%kmesh_input, common_data%kmesh_info, common_data%print_output, &
                   common_data%kpt_latt, common_data%real_lattice, common_data%num_kpts, &
                   common_data%gamma_only, common_data%seedname, istdout, common_data%timer, &
                   error, common_data%comm)
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    common_data%setup_complete = .true.
  end subroutine w90_create_kmesh

  subroutine w90_get_nn(common_data, nn, istdout, istderr, ierr)
    !! probe w90 library for number of finite difference k-point neighbours
    implicit none

    ! arguments
    integer, intent(out) :: nn, ierr
    !! nn is the number of neighbours in F.D. scheme
    integer, intent(in) :: istdout, istderr
    type(lib_common_type), intent(inout) :: common_data
    !! library data object

    if (.not. common_data%setup_complete) then
      call w90_create_kmesh(common_data, istdout, istderr, ierr)
      !! setup k-mesh (b vectors) if not already done (sets setup_complete)
      if (ierr > 0) return
    else
      ierr = 0
    end if

    nn = common_data%kmesh_info%nntot
  end subroutine w90_get_nn

  subroutine w90_get_nnkp(common_data, nnkp, istdout, istderr, ierr)
    !! probe w90 library for finite difference k-point neighbour indices
    implicit none

    integer, intent(out) :: nnkp(:, :), ierr
    !! nnkp must be dimensioned (n_neighbours, n_fbz)
    integer, intent(in) :: istdout, istderr
    type(lib_common_type), intent(inout) :: common_data
    !! library data object

    if (.not. common_data%setup_complete) then
      call w90_create_kmesh(common_data, istdout, istderr, ierr)
      !! setup k-mesh (b vectors) if not already done (sets setup_complete)
      if (ierr > 0) return
    else
      ierr = 0
    end if

    nnkp = common_data%kmesh_info%nnlist
  end subroutine w90_get_nnkp

  subroutine w90_get_gkpb(common_data, gkpb, istdout, istderr, ierr)
    !! probe w90 library for the triple of reciprocal lattice translations determining phase in k'= k+b
    implicit none

    integer, intent(out) :: gkpb(:, :, :), ierr
    !! gkpb must be dimensioned (3,nk,nnb)
    integer, intent(in) :: istdout, istderr
    type(lib_common_type), intent(inout) :: common_data
    !! library data object

    if (.not. common_data%setup_complete) then
      call w90_create_kmesh(common_data, istdout, istderr, ierr)
      !! setup k-mesh (b vectors) if not already done (sets setup_complete)
      if (ierr > 0) return
    else
      ierr = 0
    end if

    gkpb = common_data%kmesh_info%nncell
  end subroutine w90_get_gkpb

  subroutine w90_get_centres(common_data, centres)
    !! probes w90 library for (current) wannier centres
    implicit none

    real(kind=dp), intent(out) :: centres(:, :)
    !! must be allocated with size >= n_wannier
    type(lib_common_type), intent(in) :: common_data
    !! library data object

    centres = common_data%wannier_data%centres
  end subroutine w90_get_centres

  subroutine w90_get_spreads(common_data, spreads)
    !! probes w90 library for (current) wannier spreads
    implicit none

    real(kind=dp), intent(out) :: spreads(:)
    !! must be allocated with size >= n_wannier
    type(lib_common_type), intent(in) :: common_data
    !! library data object

    spreads = common_data%wannier_data%spreads
  end subroutine w90_get_spreads

  subroutine w90_get_num_excl_bands(common_data, num_excl_bands)
    implicit none

    integer, intent(out) :: num_excl_bands
    !! number of excluded bands, for sizing array arg of w90_get_excl_bands
    type(lib_common_type), intent(in) :: common_data
    !! library data object

    if (.not. allocated(common_data%exclude_bands)) then
      num_excl_bands = 0
    else
      num_excl_bands = size(common_data%exclude_bands(:))
    end if
  end subroutine w90_get_num_excl_bands

  subroutine w90_get_excl_bands(common_data, excl_bands, istdout, istderr, ierr)
    use w90_error, only: w90_error_type, set_error_fatal
    implicit none

    integer, allocatable, intent(inout) :: excl_bands(:)
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    !! must be allocated with size >= num_excl_bands
    type(lib_common_type), intent(in) :: common_data
    !! library data object

    type(w90_error_type), allocatable :: error

    ierr = 0

    if (.not. allocated(excl_bands)) then
      call set_error_fatal(error, &
                           'Error: array argument excl_bands in get_excl_bands() call is not allocated', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (.not. allocated(common_data%exclude_bands)) then
      call set_error_fatal(error, &
                           'Error: common_data%exclude_bands in get_excl_bands() call is not allocated', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(excl_bands) < size(common_data%exclude_bands)) then
      call set_error_fatal(error, &
                           'Error: array argument excl_bands in get_excl_bands() call is incorrectly sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else
      excl_bands(:) = common_data%exclude_bands(:)
    end if
  end subroutine w90_get_excl_bands

  subroutine w90_get_proj(common_data, n, site, l, m, s, rad, x, z, sqa, zona, istdout, istderr, ierr)
    !! probes library data object and returns arrays describing a list of projections
    !! projectors defined either in .win file or passed (using same syntax) through w90_setopt
    !! array arguments assumed allocated at call
    !! array arguments must have length >= number of projectors (checked here)
    use w90_error, only: w90_error_type, set_error_fatal
    implicit none
    integer, intent(in) :: istdout, istderr
    integer, intent(inout) :: n, l(:), m(:), s(:)
    !! number of projectors, angular, orbital and spin numbers
    integer, intent(inout) :: rad(:)
    !! radial function defining projector
    integer, intent(out) :: ierr
    !! ierr returned > 0 in case of error
    real(kind=dp), intent(inout) :: site(:, :)
    !! projector origin (kind, site of kind)
    real(kind=dp), intent(inout) :: sqa(:, :), z(:, :), x(:, :), zona(:)
    !! spin quantisation axis, z- an x-axes
    type(lib_common_type), intent(in), target :: common_data
    !! library data object

    ! local variables
    integer :: ip
    type(proj_type), pointer :: proj
    type(w90_error_type), allocatable :: error

    ierr = 0

    if (.not. allocated(common_data%proj_input)) then
      call set_error_fatal(error, &
                           'Error: projectors are not setup in Wannier90 library when requested via get_proj()', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    n = size(common_data%proj_input)

    ! check allocation of main output arrays
    if (size(l) < n) then
      call set_error_fatal(error, 'Error: array argument l in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(m) < n) then
      call set_error_fatal(error, 'Error: array argument m in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(s) < n) then
      call set_error_fatal(error, 'Error: array argument s in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(site, 2) < n) then
      call set_error_fatal(error, 'Error: array argument site in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(sqa, 2) < n) then
      call set_error_fatal(error, 'Error: array argument sqa in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(sqa, 2) < n) then
      call set_error_fatal(error, 'Error: array argument sqa in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(z, 2) < n) then
      call set_error_fatal(error, 'Error: array argument z in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    elseif (size(x, 2) < n) then
      call set_error_fatal(error, 'Error: array argument x in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    else if (size(rad) < n) then
      call set_error_fatal(error, 'Error: array argument rad in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
    if (size(zona) < n) then
      call set_error_fatal(error, 'Error: array argument zona in get_proj() call is insufficiently sized', common_data%comm)
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    do ip = 1, n
      proj => common_data%proj_input(ip)
      l(ip) = proj%l
      m(ip) = proj%m
      s(ip) = proj%s
      site(1:3, ip) = proj%site(1:3)
      sqa(1:3, ip) = proj%s_qaxis(1:3)
      z(1:3, ip) = proj%z(1:3)
      x(1:3, ip) = proj%x(1:3)
      rad(ip) = proj%radial
      zona(ip) = proj%zona
    end do
  end subroutine w90_get_proj

  subroutine w90_set_comm(common_data, comm)
#ifdef W90_MPI08
    use mpi_f08
#endif
    implicit none

#ifdef W90_MPIH
    include 'mpif.h'
#endif

    type(lib_common_type), intent(inout) :: common_data
#ifdef W90_MPI08
    type(mpi_comm), intent(in) :: comm
#else
    integer, intent(in) :: comm
#endif
    common_data%comm%comm = comm
  end subroutine w90_set_comm

  subroutine w90_print_info(common_data, istdout, istderr, ierr)
    use w90_error_base, only: w90_error_type
    use w90_readwrite, only: w90_readwrite_write_header
    use w90_wannier90_readwrite, only: w90_wannier90_readwrite_write
    use w90_comms, only: mpisize, mpirank

    implicit none

    ! arguments
    integer, intent(in) :: istdout, istderr
    integer, intent(out) :: ierr
    type(lib_common_type), intent(inout) :: common_data

    ! local variables
    type(w90_error_type), allocatable :: error
    integer :: mpi_size

    ierr = 0

    if (mpirank(common_data%comm) == 0) then
      mpi_size = mpisize(common_data%comm)
      ! write jazzy header info
      call w90_readwrite_write_header(common_data%physics%bohr_version_str, &
                                      common_data%physics%constants_version_str1, &
                                      common_data%physics%constants_version_str2, &
                                      mpi_size, istdout)
    end if

    ! write simulation details
    call w90_wannier90_readwrite_write(common_data%atom_data, common_data%band_plot, &
                                       common_data%dis_control, common_data%dis_spheres, &
                                       common_data%fermi_energy_list, &
                                       common_data%fermi_surface_data, common_data%kpt_latt, &
                                       common_data%output_file, common_data%wvfn_read, &
                                       common_data%wann_control, common_data%proj, &
                                       common_data%proj_input, common_data%real_space_ham, &
                                       common_data%select_proj, common_data%kpoint_path, &
                                       common_data%tran, common_data%print_output, &
                                       common_data%wannier_data, common_data%wann_plot, &
                                       common_data%w90_calculation, common_data%real_lattice, &
                                       common_data%sitesym%symmetrize_eps, common_data%mp_grid, &
                                       common_data%num_bands, common_data%num_kpts, &
                                       common_data%num_proj, common_data%num_wann, &
                                       common_data%optimisation, .false., common_data%gamma_only, &
                                       common_data%lsitesymmetry, common_data%w90_system%spinors, &
                                       common_data%use_bloch_phases, istdout)

    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if
  end subroutine w90_print_info

  subroutine w90_print_timings(common_data, istdout)
    use w90_comms, only: mpisize, mpirank
    use w90_io, only: io_print_timings

    implicit none

    ! arguments
    integer, intent(in) :: istdout
    type(lib_common_type), intent(inout) :: common_data

    ! io_print_timings does not test for iprint or rank being root
    ! adopt latter condition here--iprint irrelevant when this function is explicitly requested
    if (mpirank(common_data%comm) == 0) then
      call io_print_timings(common_data%timer, istdout)
    end if
  end subroutine w90_print_timings

  subroutine w90_set_option_text(common_data, keyword, text)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword, text
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%txtdata = text
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_text

  subroutine w90_set_option_logical(common_data, keyword, boolarg)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    logical, intent(in) :: boolarg
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%ldata = boolarg
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_logical

  subroutine w90_set_option_i1d(common_data, keyword, arr)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    integer, intent(in) :: arr(:)
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%i1d = arr ! this causes an automatic allocation
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_i1d

  subroutine w90_set_option_i2d(common_data, keyword, arr)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    integer, intent(in) :: arr(:, :)
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%i2d = arr
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_i2d

  subroutine w90_set_option_int(common_data, keyword, ival)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    integer, intent(in) :: ival
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%idata = ival
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_int

  subroutine w90_set_option_r1d(common_data, keyword, arr)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    real(kind=dp), intent(in) :: arr(:)
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%r1d = arr
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_r1d

  subroutine w90_set_option_r2d(common_data, keyword, arr)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    real(kind=dp), intent(in) :: arr(:, :)
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%r2d = arr
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_r2d

  subroutine w90_set_option_c2d(common_data, keyword, arr)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    character(len=*), intent(in) :: arr(:)
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%c2d = arr
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_c2d

  subroutine w90_set_option_real(common_data, keyword, rval)
    use w90_readwrite, only: init_settings, expand_settings

    implicit none

    character(*), intent(in) :: keyword
    real(kind=dp), intent(in) :: rval
    type(lib_common_type), intent(inout) :: common_data
    integer :: i

    if (.not. allocated(common_data%settings%entries)) call init_settings(common_data%settings)
    i = common_data%settings%num_entries + 1
    common_data%settings%entries(i)%keyword = keyword
    common_data%settings%entries(i)%rdata = rval
    common_data%settings%num_entries = i
    if (common_data%settings%num_entries == common_data%settings%num_entries_max) then
      call expand_settings(common_data%settings)
    end if
  end subroutine w90_set_option_real

  subroutine w90_distribute_kpts(common_data, num_kpts, mpi_size, dist_k, istdout, istderr, ierr)
    !! provide a distribution of num_kpts k-points across mpi_size MPI ranks
    ! should be called from all ranks in a parallel environment for error propagation
    use w90_comms, only: comms_sync_error
    use w90_error_base, only: w90_error_type
    use w90_error, only: set_error_fatal

    implicit none

    ! arguments
    integer, intent(in) :: num_kpts
    !! number of k-points
    integer, intent(in) :: mpi_size
    !! number of ranks in MPI communicator
    integer, intent(in) :: istdout, istderr
    !! destination for error messages
    integer, intent(inout), allocatable :: dist_k(:)
    !! already allocated array
    !! assigned here such that dist_k(i) = rank handling kpt i
    !! size and allocation status are tested
    integer, intent(out) :: ierr
    !! return code, nonzero in case of error
    type(lib_common_type), intent(in) :: common_data
    !! library object: only the communicator type is referenced

    ! local variables
    type(w90_error_type), allocatable :: error
    integer :: ctr, i, nkl

    ierr = 0

    if (mpi_size < 1) then
      call set_error_fatal(error, 'Error: mpi_size < 1 in w90_distribute_kpts call.', common_data%comm)
    elseif (num_kpts < 1) then
      call set_error_fatal(error, 'Error: num_kpts < 1 in w90_distribute_kpts call.', common_data%comm)
    elseif (.not. allocated(dist_k)) then
      call set_error_fatal(error, 'Error: dist_k not allocated in w90_distribute_kpts call.', common_data%comm)
    elseif (size(dist_k) < num_kpts) then
      call set_error_fatal(error, 'Error: size(dist_k) < num_kpts in w90_distribute_kpts call.', common_data%comm)
    end if
    if (allocated(error)) then
      call prterr(error, ierr, istdout, istderr, common_data%comm)
      return
    end if

    ctr = 0
    do i = 0, mpi_size - 1
      nkl = num_kpts/mpi_size ! number of kpoints per rank
      if (mod(num_kpts, mpi_size) > i) nkl = nkl + 1
      if (nkl > 0) then
        dist_k(ctr + 1:ctr + nkl) = i
        ctr = ctr + nkl
      end if
    end do
  end subroutine w90_distribute_kpts

  subroutine w90_free(common_data)
    type(lib_common_type), intent(inout) :: common_data
    type(lib_common_type) :: blank   ! default-initialised, nothing allocated
    common_data = blank
  end subroutine

end module w90_library