LCOV - code coverage report
Current view: top level - src/77_ddb - m_phonons.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 85.0 % 1339 1138
Test Date: 2026-09-19 17:42:43 Functions: 84.8 % 33 28

            Line data    Source code
       1              : !!****m* ABINIT/m_phonons
       2              : !! NAME
       3              : !! m_phonons
       4              : !!
       5              : !! FUNCTION
       6              : !! Module for the phonon density of states.
       7              : !! Container type is defined, and destruction, print subroutines
       8              : !! as well as the central phdos_init
       9              : !!
      10              : !! COPYRIGHT
      11              : !! Copyright (C) 1999-2026 ABINIT group (XG, MG, MJV, GMR)
      12              : !! This file is distributed under the terms of the
      13              : !! GNU General Public Licence, see ~abinit/COPYING
      14              : !! or http://www.gnu.org/copyleft/gpl.txt .
      15              : !! For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
      16              : !!
      17              : !! SOURCE
      18              : 
      19              : #if defined HAVE_CONFIG_H
      20              : #include "config.h"
      21              : #endif
      22              : 
      23              : #include "abi_common.h"
      24              : 
      25              : module m_phonons
      26              : 
      27              :  use, intrinsic :: iso_c_binding
      28              :  use defs_basis
      29              :  use m_errors
      30              :  use m_xmpi
      31              :  use m_abicore
      32              :  use m_htetra
      33              :  use m_numeric_tools
      34              :  use m_cgtools
      35              :  use m_crystal
      36              :  use m_nctk
      37              :  use m_sortph
      38              :  use m_ddb
      39              :  use netcdf
      40              :  use m_supercell
      41              : 
      42              : 
      43              :  use m_fstrings,        only : itoa, ftoa, sjoin, ltoa, ktoa, strcat, basename, replace
      44              :  use m_matrix,          only : matr3inv
      45              :  use m_symtk,           only : sg_multable
      46              :  use m_krank,           only : krank_t
      47              :  use m_dtset,           only : dataset_type
      48              :  use m_time,            only : cwtime, cwtime_report
      49              :  use m_io_tools,        only : open_file
      50              :  use m_geometry,        only : mkrdim, symredcart, normv, phdispl_cart2red
      51              :  use m_dynmat,          only : gtdyn9, dfpt_phfrq, dfpt_prtph, &
      52              :                                pheigvec_normalize, massmult_and_breaksym, phdispl_from_eigvec, phangmom_from_eigvec
      53              :  use m_atprj,           only : atprj_type
      54              :  use m_bz_mesh,         only : isamek, make_path, kpath_t
      55              :  use m_ifc,             only : ifc_type
      56              :  use m_anaddb_dataset,  only : anaddb_dataset_type
      57              :  use m_kpts,            only : kpts_ibz_from_kptrlatt, get_full_kgrid, kpts_map, kpts_timrev_from_kptopt
      58              :  use m_special_funcs,   only : bose_einstein
      59              :  use m_sort,            only : sort_dp
      60              :  use m_symfind,         only : symanal
      61              : 
      62              :  implicit none
      63              : 
      64              :  private
      65              : 
      66              :  public :: mkphbs                        ! Compute phonon band structure
      67              :  public :: phonons_write_xmgrace         ! Write phonons bands in Xmgrace format.
      68              :  public :: phonons_write_gnuplot         ! Write phonons bands in gnuplot format.
      69              :  public :: ifc_mkphbs                    ! Compute the phonon band structure from the IFC and write data to file(s)
      70              :  public :: dfpt_symph                    ! Determine the symmetry character of the different phonon modes at Gamma
      71              : 
      72              :  public :: zacharias_supercell_make
      73              :  public :: zacharias_supercell_print
      74              :  public :: thermal_supercell_make
      75              :  public :: thermal_supercell_free
      76              :  public :: thermal_supercell_print
      77              : !!***
      78              : 
      79              : !!****t* m_phonons/phdos_t
      80              : !! NAME
      81              : !! phdos_t
      82              : !!
      83              : !! FUNCTION
      84              : !! Container for phonon DOS and atom projected contributions
      85              : !!
      86              : !! SOURCE
      87              : 
      88              :  type,public :: phdos_t
      89              : 
      90              :   integer :: ntypat
      91              :   ! Number of type of atoms.
      92              : 
      93              :   integer :: natom
      94              :   ! Number of atoms is the unit cell.
      95              : 
      96              :   integer :: nomega
      97              :   ! Number of frequency points in DOS mesh.
      98              : 
      99              :   integer :: nqibz
     100              :   ! Number of q-points in the IBZ.
     101              : 
     102              :   integer :: n_normal_vec_dmm
     103              :   ! Number of directions to project the thermal current in the diffuse mismatch model
     104              : 
     105              :   integer :: prtdos
     106              :   ! Option of DOS calculation (1 for Gaussian, 2 for tetrahedrons).
     107              : 
     108              :   real(dp) :: omega_min
     109              :   ! Min frequency for DOS calculation.
     110              : 
     111              :   real(dp) :: omega_max
     112              :   ! Max frequency for DOS calculation.
     113              : 
     114              :   real(dp) :: omega_step
     115              :   ! Frequency step.
     116              : 
     117              :   real(dp) :: dossmear
     118              :   ! Gaussian broadening.
     119              : 
     120              :   integer :: qptrlatt(3,3) = 0
     121              :   ! q-mesh as computed in getkgrid_low
     122              : 
     123              :   real(dp) :: shiftq(3)
     124              :   ! Shigt of Q-mesh computed by getkgrid_low (1 shift is enough)
     125              : 
     126              :   real(dp),allocatable :: atom_mass(:)
     127              :    ! atom_mass(natom)
     128              : 
     129              :   real(dp),allocatable :: normal_vec_dmm(:,:)
     130              :   ! normal_vec_dmm(3,n_normal_vec_dmm)
     131              :   ! Cartesian directions to project the thermal current in the diffuse mismatch model
     132              : 
     133              :   real(dp),allocatable :: omega(:)
     134              :    ! omega(nomega)
     135              :    ! Frequency grid.
     136              : 
     137              :   real(dp),allocatable :: phdos(:)
     138              :    ! phdos(nomega)
     139              :    ! phonon DOS.
     140              : 
     141              :   real(dp),allocatable :: phdos_int(:)
     142              :    ! phdos_int(nomega)
     143              :    ! integrated phonon DOS
     144              : 
     145              :   real(dp),allocatable :: phdos_dmm(:,:)
     146              :    ! phdos_dmm(nomega,n_normal_vec_dmm)
     147              :    ! phonon DOS including projection of velocity onto a given direction n normal to an interface
     148              :    ! in the diffuse mismatch model
     149              : 
     150              :   real(dp),allocatable :: pjdos(:,:,:)
     151              :    ! pjdos(nomega,3,natom)
     152              :    ! projected DOS (over atoms and cartesian directions)
     153              : 
     154              :   real(dp),allocatable :: pjdos_int(:,:,:)
     155              :    ! pjdos_int(nomega,3,natom)
     156              :    ! Integrated atomic PJDOS along the three cartesian directions.
     157              : 
     158              :   real(dp),allocatable :: pjdos_type(:,:)
     159              :    ! pjdos_type(nomega,ntypat)
     160              :    ! phonon DOS contribution arising from a particular atom-type.
     161              : 
     162              :   real(dp),allocatable :: pjdos_type_int(:,:)
     163              :    ! pjdos_type_int(nomega,ntypat)
     164              :    ! Integrate phonon DOS contribution arising from a particular atom-type.
     165              : 
     166              :   real(dp),allocatable :: pjdos_rc_type(:,:,:)
     167              :    ! phdos(nomega,3,ntypat)
     168              :    ! phonon DOS contribution arising from a particular atom-type
     169              :    ! decomposed along the three cartesian directions.
     170              : 
     171              :   real(dp),allocatable :: msqd_dos_atom(:,:,:,:)
     172              :    ! msqd_dos_atom(nomega,3,3,natom)
     173              :    ! mean square displacement matrix, frequency dependent like a DOS, tensor in cartesian coords.
     174              :    ! allows one to calculate Debye Waller factors by integration with 1/omega
     175              :    ! and the Bose Einstein factor
     176              : 
     177              :  contains
     178              : 
     179              :    procedure :: print => phdos_print
     180              :    procedure :: print_debye => phdos_print_debye
     181              :    procedure :: print_msqd => phdos_print_msqd
     182              :    procedure :: print_thermo => phdos_print_thermo
     183              :    procedure :: free => phdos_free
     184              :    procedure :: ncwrite => phdos_ncwrite
     185              :    procedure :: init => phdos_init  ! Constructor
     186              :  end type phdos_t
     187              : !!***
     188              : 
     189              : !!****t* m_phonons/phstore_t
     190              : !! NAME
     191              : !! phstore_t
     192              : !!
     193              : !! FUNCTION
     194              : !!  This object stores ph eigenvalues and eigenvectors in the IBZ and provides methods
     195              : !!  to compute the corresponding quantities in the full BZ using symmetries.
     196              : !!  Useful for very intensive loops of q-points in the BZ in which the call to ifc_fourq
     197              : !!  may become a significant bottleneck.
     198              : !!  Note that IBZ quantities are memory-distributed inside the MPI communicator comm.
     199              : !!  and the symmetrization is performed in a non-blocking fashion so that it's possible to overlap
     200              : !!  the symmetrization with computations. See sigmaph for usage.
     201              : !!
     202              : !! SOURCE
     203              : 
     204              :  type,public :: phstore_t
     205              : 
     206              :    integer :: nqibz
     207              :    ! Number of q-points in the IBZ
     208              : 
     209              :    integer :: comm
     210              :    ! MPI communicator used to distribute memory.
     211              : 
     212              :    integer :: nprocs
     213              :    ! Number of MPI procs in comm.
     214              : 
     215              :    integer :: my_rank
     216              :    ! Rank of this MPI proc inside comm
     217              : 
     218              :    integer :: natom, natom3
     219              : 
     220              :    logical :: use_ifc_fourq = .False.
     221              :    ! Debugging flag. If True, replace symmetrization with call to ifc_fourq.
     222              : 
     223              :    integer :: requests(2)
     224              :    ! MPI requests
     225              : 
     226              :    integer,allocatable :: qibz_start(:), qibz_stop(:)
     227              :    ! (0:%nprocs-1))
     228              :    ! Initial and final index of the IBZ qpoint treated by this MPI proc inside comm.
     229              : 
     230              :    real(dp), contiguous, pointer :: qibz(:,:)
     231              :    ! q-points in the IBZ.
     232              : 
     233              :    real(dp),allocatable :: phfreqs_qibz(:,:)
     234              :    ! (natom3, %nqibz))
     235              :    ! Ph frequencies in the IBZ
     236              : 
     237              :    real(dp),allocatable :: pheigvec_qibz(:,:,:,:)
     238              :    ! (2, natom3, natom3, %nqibz))
     239              :    ! Ph eigenvectors in the IBZ
     240              : 
     241              :    real(dp),allocatable :: phfrq(:)
     242              :    ! (natom3)
     243              :    ! Ph frequencies for q in the full BZ
     244              : 
     245              :    real(dp),allocatable :: displ_cart(:,:,:,:)
     246              :    ! displ_cart(2, 3, natom, %natom3)
     247              :    ! Ph displacement for q in the full BZ
     248              : 
     249              :   contains
     250              : 
     251              :     procedure :: init => phstore_init                    ! Creation method (allocates memory, initialize data from input vars).
     252              :     procedure :: async_rotate => phstore_async_rotate    ! Begin non-blocking collective MPI communication to symmetrize stuff
     253              :     procedure :: wait => phstore_wait                    ! Wait from non-blocking MPI BCAST started in phstore_async_rotate,
     254              :                                                          ! return ph frequencies and displacements.
     255              :     procedure :: free => phstore_free                    ! Free dynamic memory
     256              :  end type phstore_t
     257              : !!***
     258              : 
     259              :  public :: pheigvec_rotate      ! Obtain phonon eigenvectors for q in the BZ from the symmetrical image in the IBZ.
     260              :  public :: test_phrotation      ! Validate pheigvec_rotate routine.
     261              : 
     262              : contains  !=====================================================
     263              : !!***
     264              : 
     265              : !!****f* m_phonons/phdos_print
     266              : !! NAME
     267              : !! phdos_print
     268              : !!
     269              : !! FUNCTION
     270              : !! Print out phonon DOS (and partial DOS etc) in meV units
     271              : !!
     272              : !! INPUTS
     273              : !! PHdos= container object for phonon DOS
     274              : !! fname=File name for output
     275              : !!
     276              : !! OUTPUT
     277              : !!  Only writing.
     278              : !!
     279              : !! SOURCE
     280              : 
     281           77 : subroutine phdos_print(PHdos, fname)
     282              : 
     283              : !Arguments ------------------------------------
     284              :  class(phdos_t),intent(in) :: PHdos
     285              :  character(len=*),intent(in) :: fname
     286              : 
     287              : !Local variables-------------------------------
     288              :  integer :: io,itype,unt,unt_by_atom,unt_msqd,iatom
     289              :  real(dp) :: tens(3,3)
     290              :  character(len=500) :: msg, msg_method
     291              :  character(len=fnlen) :: fname_by_atom, fname_msqd
     292              :  character(len=3) :: unitname
     293              : ! *************************************************************************
     294              : 
     295              : ! Use Ha units everywhere
     296           77 :  unitname='Ha'
     297              : 
     298          129 :  select case (PHdos%prtdos)
     299              :  case (1)
     300              :    write(msg_method,'(a,es16.8,2a,i0)')&
     301           52 :     '# Gaussian method with smearing = ',PHdos%dossmear,unitname,', nqibz =',PHdos%nqibz
     302              :  case (2)
     303           25 :    write(msg_method,'(a,i0)')'# Tetrahedron method, nqibz= ',PHdos%nqibz
     304              :  case default
     305           77 :    ABI_ERROR(sjoin(" Wrong prtdos: ",itoa(PHdos%prtdos)))
     306              :  end select
     307              : 
     308              :  ! Open external file and write results
     309           77 :  if (open_file(fname,msg,newunit=unt,form="formatted",action="write") /= 0) then
     310            0 :    ABI_ERROR(msg)
     311              :  end if
     312           77 :  write(msg,'(3a)')'# ',ch10,'# Phonon density of states and atom type projected DOS'
     313           77 :  call wrtout(unt,msg)
     314           77 :  write(msg,'(6a)')'# ',ch10,'# Energy in ',unitname,', DOS in states/',unitname
     315           77 :  call wrtout(unt,msg)
     316           77 :  call wrtout(unt,msg_method)
     317           77 :  write(msg,'(5a)')'# ',ch10,'# omega     PHDOS    INT_PHDOS   PJDOS[atom_type=1]  INT_PJDOS[atom_type=1] ...  ',ch10,'# '
     318           77 :  call wrtout(unt,msg)
     319       144574 :  do io=1,PHdos%nomega
     320       144497 :    write(unt,'(3es17.8)',advance='NO')PHdos%omega(io),PHdos%phdos(io),PHdos%phdos_int(io)
     321       345113 :    do itype=1,PHdos%ntypat
     322       345113 :      write(unt,'(2es17.8,2x)',advance='NO')PHdos%pjdos_type(io,itype),PHdos%pjdos_type_int(io,itype)
     323              :    end do
     324       144574 :    write(unt,*)
     325              :  end do
     326           77 :  close(unt)
     327              : 
     328           77 :  fname_by_atom = trim(fname) // "_by_atom"
     329           77 :  if (open_file(fname_by_atom,msg,newunit=unt_by_atom,form="formatted",action="write") /= 0) then
     330            0 :    ABI_ERROR(msg)
     331              :  end if
     332           77 :  write(msg,'(3a)')'# ',ch10,'# Phonon density of states and atom projected DOS'
     333           77 :  call wrtout(unt_by_atom,msg)
     334           77 :  write(msg,'(6a)')'# ',ch10,'# Energy in ',unitname,', DOS in states/',unitname
     335           77 :  call wrtout(unt_by_atom,msg)
     336           77 :  call wrtout(unt_by_atom,msg_method)
     337           77 :  write(msg,'(5a)')'# ',ch10,'# omega     PHDOS    PJDOS[atom=1]  PJDOS[atom=2] ...  ',ch10,'# '
     338           77 :  call wrtout(unt_by_atom,msg)
     339       144574 :  do io=1,PHdos%nomega
     340       144497 :    write(unt_by_atom,'(2es17.8)',advance='NO')PHdos%omega(io),PHdos%phdos(io)
     341       498707 :    do iatom=1,PHdos%natom
     342      1561337 :      write(unt_by_atom,'(1es17.8,2x)',advance='NO') sum(PHdos%pjdos(io,1:3,iatom))
     343              :    end do
     344       144574 :    write(unt_by_atom,*)
     345              :  end do
     346           77 :  close(unt_by_atom)
     347              : 
     348           77 :  fname_msqd = trim(fname) // "_msqd"
     349           77 :  if (open_file(fname_msqd,msg,newunit=unt_msqd,form="formatted",action="write") /= 0) then
     350            0 :    ABI_ERROR(msg)
     351              :  end if
     352           77 :  write(msg,'(3a)')'# ',ch10,'# Phonon density of states weighted msq displacement matrix (set to zero below 1e-12)'
     353           77 :  call wrtout(unt_msqd,msg)
     354           77 :  write(msg,'(6a)')'# ',ch10,'# Energy in ',unitname,', DOS in bohr^2 states/',unitname
     355           77 :  call wrtout(unt_msqd,msg)
     356           77 :  call wrtout(unt_msqd,msg_method)
     357           77 :  write(msg,'(5a)')'# ',ch10,'# omega     MSQDisp[atom=1, xx, yy, zz, yz, xz, xy]  MSQDisp[atom=2, xx, yy,...] ...  ',ch10,'# '
     358           77 :  call wrtout(unt_msqd,msg)
     359       144574 :  do io=1,PHdos%nomega
     360       144497 :    write(unt_msqd,'(2es17.8)',advance='NO')PHdos%omega(io)
     361       498707 :    do iatom=1,PHdos%natom
     362      4604730 :      tens = PHdos%msqd_dos_atom(io,:,:,iatom)
     363      4604730 :      where (abs(tens) < tol12)
     364              :         tens = zero
     365              :      end where
     366              :      write(unt_msqd,'(6es17.8,2x)',advance='NO') &
     367       354210 :         tens(1,1), tens(2,2), tens(3,3), &
     368       852917 :         tens(2,3), tens(1,3), tens(1,2)
     369              :    end do
     370       144574 :    write(unt_msqd,*)
     371              :  end do
     372           77 :  close(unt_msqd)
     373              : 
     374           77 : end subroutine phdos_print
     375              : !!***
     376              : 
     377              : !----------------------------------------------------------------------
     378              : 
     379              : !****f* m_phonons/phdos_print_debye
     380              : !!
     381              : !! NAME
     382              : !! phdos_print_debye
     383              : !!
     384              : !! FUNCTION
     385              : !! Print out global Debye temperature, force constant, etc... from phonon DOS
     386              : !!
     387              : !! INPUTS
     388              : !! phonon_dos= container object for phonon DOS
     389              : !! ucvol = unit cell volume
     390              : !!
     391              : !! OUTPUT
     392              : !!  Only writing.
     393              : !!
     394              : !! SOURCE
     395              : 
     396           11 : subroutine phdos_print_debye(PHdos, ucvol)
     397              : 
     398              : !Arguments ------------------------------------
     399              :  class(phdos_t),intent(in) :: PHdos
     400              :  real(dp), intent(in) :: ucvol
     401              : 
     402              : !Local variables-------------------------------
     403              :  integer :: io, iomax, iomin
     404              :  real(dp) :: avgom2dos, avgspeedofsound, debyefreq, meanfreq, meanfreq2
     405              :  character(len=500) :: msg
     406              : !arrays
     407              :  integer :: units(2)
     408           11 :  real(dp), allocatable :: om2dos(:), om1dos(:), intdos(:)
     409              : ! *************************************************************************
     410              : 
     411           33 :  units = [std_out, ab_out]
     412              : 
     413              :  ! average speed of sound: coefficient of omega^2 in the DOS is = Volume / 2 pi^2 hbar^3 v_s^3
     414              :  ! first find how far out we can fit with a parabola
     415        40817 :  ABI_CALLOC(om2dos, (PHdos%nomega))
     416        40806 :  ABI_CALLOC(om1dos, (PHdos%nomega))
     417        40806 :  ABI_CALLOC(intdos, (PHdos%nomega))
     418        40795 :  avgom2dos = zero
     419        40795 :  do io=1,PHdos%nomega
     420        40795 :    if (abs(PHdos%omega(io)) > 1.e-8) then
     421        40780 :      om1dos(io) = PHdos%phdos(io) / PHdos%omega(io)
     422        40780 :      om2dos(io) = PHdos%phdos(io) / PHdos%omega(io)**2
     423              :    end if
     424              :  end do
     425              : 
     426              :  ! integrate dos / omega
     427           11 :  call simpson_int(PHdos%nomega,PHdos%omega_step,om1dos,intdos)
     428           11 :  meanfreq = intdos(PHdos%nomega)
     429              : 
     430              :  ! integrate dos / omega^2
     431        40795 :  intdos = zero
     432           11 :  call simpson_int(PHdos%nomega,PHdos%omega_step,om2dos,intdos)
     433           11 :  meanfreq2 = intdos(PHdos%nomega)
     434              : 
     435           11 :  iomin = 1; iomax = PHdos%nomega
     436        10345 :  do io = 1, PHdos%nomega
     437              :    ! skip eventual negative frequency modes
     438        10345 :    if (PHdos%omega(io) <= tol10) then
     439              :      iomin = io
     440              :      cycle
     441              :    end if
     442              : 
     443              :    ! accumulate dos * om^2 to make an average
     444           11 :    avgom2dos = avgom2dos + om2dos(io)
     445              :    ! first deviation from initial value of more than 10 percent
     446           11 :    if (abs(one-om2dos(iomin)/om2dos(io)) > 0.1_dp) then
     447              :      iomax = io; exit
     448              :    end if
     449              :  end do
     450              : 
     451           11 :  avgom2dos = avgom2dos / (iomax-iomin+1)
     452              :  ! this value is also useful for partial atomic DOS, related to kinetic energy and Force constant in Moessbauer
     453              : 
     454           11 :  avgspeedofsound = (ucvol / 2 / pi**2 / avgom2dos)**third
     455           11 :  write (msg,'(a,E20.10,3a,F16.4,2a)') ' Average speed of sound: ', avgspeedofsound, ' (at units) ',ch10,&
     456           22 :               '-                      = ', avgspeedofsound * Bohr_Ang * 1.d-13 / Time_Sec, ' [km/s]',ch10
     457           11 :  call wrtout(units, msg)
     458              : 
     459              :  ! Debye frequency = vs * (6 pi^2 natom / ucvol)**1/3
     460           11 :  debyefreq = avgspeedofsound * (six*pi**2/ucvol)**(1./3.)
     461           11 :  write (msg,'(a,E20.10,3a,E20.10,a)') ' Debye frequency from DOS: ', debyefreq, ' (Ha) ',ch10,&
     462           22 :                                     '-                        = ', debyefreq*Ha_THz, ' (THz)'
     463           11 :  call wrtout(units, msg)
     464              : 
     465              :  ! Debye temperature = hbar * Debye frequency / kb
     466           11 :  write (msg,'(a,E20.10,2a)') '-Debye temperature from DOS: ', debyefreq*Ha_K, ' (K)', ch10
     467           11 :  call wrtout(units, msg)
     468              : 
     469           11 :  ABI_FREE(om2dos)
     470           11 :  ABI_FREE(om1dos)
     471           11 :  ABI_FREE(intdos)
     472              : 
     473           11 : end subroutine phdos_print_debye
     474              : !!***
     475              : 
     476              : !----------------------------------------------------------------------
     477              : 
     478              : !****f* m_phonons/phdos_print_thermo
     479              : !! NAME
     480              : !! phdos_print_thermo
     481              : !!
     482              : !! FUNCTION
     483              : !! Print out global thermodynamic quantities based on DOS
     484              : !! Only master node should call this routine.
     485              : !!
     486              : !! INPUTS
     487              : !! phonon_dos= container object for phonon DOS
     488              : !! ucvol = unit cell volume
     489              : !!
     490              : !! OUTPUT
     491              : !!  Only writing.
     492              : !!
     493              : !! SOURCE
     494              : 
     495           11 : subroutine phdos_print_thermo(PHdos, fname, ntemper, tempermin, temperinc)
     496              : 
     497              : !Arguments ------------------------------------
     498              :  class(phdos_t),intent(in) :: PHdos
     499              :  integer, intent(in) :: ntemper
     500              :  real(dp), intent(in) :: tempermin, temperinc
     501              :  character(len=*),intent(in) :: fname
     502              : 
     503              : !Local variables-------------------------------
     504              :  integer :: iomega, itemper, tunt
     505              :  character(len=500) :: msg
     506              :  real(dp) :: wover2t, ln2shx, cothx, invsinh2
     507              :  real(dp) :: tmp, domega
     508              : !arrays
     509           11 :  real(dp), allocatable :: free(:), energy(:), entropy(:), spheat(:),wme(:)
     510              : 
     511              : ! *********************************************************************
     512              : 
     513              :  ! Allocate and put zeroes for F, E, S, Cv
     514          143 :  ABI_CALLOC(free,    (ntemper))
     515          132 :  ABI_CALLOC(energy,  (ntemper))
     516          132 :  ABI_CALLOC(entropy, (ntemper))
     517          132 :  ABI_CALLOC(spheat,  (ntemper))
     518          132 :  ABI_CALLOC(wme,     (ntemper))
     519              : 
     520              :  ! open THERMO file
     521           11 :  if (open_file(fname, msg, newunit=tunt, form="formatted", action="write") /= 0) then
     522            0 :    ABI_ERROR(msg)
     523              :  end if
     524              : 
     525              :  write(msg, '(3a)' )&
     526           11 :   ' phdos_print_thermo: thermodynamic functions calculated from prtdos DOS (not histogram)',ch10,&
     527           22 :   '     see THERMO output file ...'
     528           11 :  call wrtout(std_out,msg)
     529              : 
     530              :  ! print header
     531           11 :  write(tunt,'(a,a)') ch10,&
     532           22 :   ' # At  T     F(J/mol-c)     E(J/mol-c)     S(J/(mol-c.K)) C(J/(mol-c.K)) Omega_mean(cm-1) from prtdos DOS'
     533           11 :  write(tunt, "(a)")' # (A mol-c is the abbreviation of a mole-cell, that is, the'
     534           11 :  write(tunt, "(a)")' #  number of Avogadro times the atoms in a unit cell)'
     535              : 
     536           11 :  domega = phdos%omega_step
     537              : 
     538          121 :  do itemper=1,ntemper
     539              :    ! The temperature (tmp) is given in Ha
     540          110 :    tmp=(tempermin+temperinc*dble(itemper-1))*kb_HaK
     541              : 
     542       407950 :    do iomega=1,PHdos%nomega
     543       407840 :      if (abs(PHdos%phdos(iomega)) < 1.e-200_dp) cycle
     544              : 
     545              :      ! wover2t= hbar*w / 2kT dimensionless
     546       375470 :      wover2t = zero;     if(tmp > tol14) wover2t=PHdos%omega(iomega)*half/tmp
     547              :      ! should not be much of a problem for the log, but still put a check.
     548       375470 :      ln2shx=zero;        if (wover2t > tol16 .and. wover2t < 100.0_dp) ln2shx=log(two * sinh(wover2t))
     549       281080 :      cothx=zero;         if (wover2t > tol16) cothx=one/tanh(wover2t)
     550       281080 :      invsinh2=zero;      if (wover2t > tol16 .and. wover2t < 100.0_dp) invsinh2=one/sinh(wover2t)**2
     551              : 
     552              :      ! This matches the equations published in Lee & Gonze, PRB 51, 8610 (1995) [[cite:Lee1995]]
     553       375470 :      free(itemper)   = free(itemper)    + PHdos%phdos(iomega)*tmp*ln2shx
     554       375470 :      energy(itemper) = energy(itemper)  + PHdos%phdos(iomega)*half*PHdos%omega(iomega)*cothx
     555       375470 :      spheat(itemper) = spheat(itemper)  + PHdos%phdos(iomega)*wover2t**2 * invsinh2
     556       375470 :      entropy(itemper)= entropy(itemper) + PHdos%phdos(iomega)*(wover2t*cothx - ln2shx)
     557       407950 :      wme(itemper)    = wme(itemper)     + PHdos%phdos(iomega)*PHdos%omega(iomega)*wover2t**2 * invsinh2
     558              :    end do ! iomega
     559              : 
     560              :    ! suppose homogeneous omega grid and multiply by domega
     561          110 :    free(itemper)   = free(itemper)    * domega
     562          110 :    energy(itemper) = energy(itemper)  * domega
     563          110 :    entropy(itemper)= entropy(itemper) * domega
     564          110 :    spheat(itemper) = spheat(itemper)  * domega
     565          110 :    wme(itemper)    = wme(itemper)     * domega
     566              : 
     567          110 :    if (abs(spheat(itemper))>tol8) wme(itemper)=wme(itemper)/spheat(itemper)
     568              : 
     569              :    ! do the printing to file
     570          110 :    write(tunt,'(es11.3,5es15.7)') tmp/kb_HaK,&
     571          110 :     Ha_J*Avogadro*free(itemper),&
     572          110 :     Ha_J*Avogadro*energy(itemper),&
     573          110 :     Ha_J*Avogadro*kb_HaK*entropy(itemper),&
     574          110 :     Ha_J*Avogadro*kb_HaK*spheat(itemper),&
     575          231 :     wme(itemper)*Ha_cmm1
     576              :  end do ! itemper
     577              : 
     578           11 :  close(tunt)
     579              : 
     580           11 :  ABI_FREE(free)
     581           11 :  ABI_FREE(energy)
     582           11 :  ABI_FREE(entropy)
     583           11 :  ABI_FREE(spheat)
     584           11 :  ABI_FREE(wme)
     585              : 
     586           11 : end subroutine phdos_print_thermo
     587              : !!***
     588              : !----------------------------------------------------------------------
     589              : 
     590              : !!****f* m_phonons/phdos_free
     591              : !! NAME
     592              : !! phdos_free
     593              : !!
     594              : !! FUNCTION
     595              : !! Free memory
     596              : !!
     597              : !! SOURCE
     598              : 
     599          173 : subroutine phdos_free(PHdos)
     600              : 
     601              : !Arguments -------------------------------
     602              :  class(phdos_t),intent(inout) ::PHdos
     603              : ! *************************************************************************
     604              : 
     605          173 :  ABI_SFREE(PHdos%atom_mass)
     606          173 :  ABI_SFREE(PHdos%normal_vec_dmm)
     607          173 :  ABI_SFREE(PHdos%omega)
     608          173 :  ABI_SFREE(PHdos%phdos)
     609          173 :  ABI_SFREE(PHdos%phdos_int)
     610          173 :  ABI_SFREE(PHdos%phdos_dmm)
     611          173 :  ABI_SFREE(PHdos%pjdos)
     612          173 :  ABI_SFREE(PHdos%pjdos_int)
     613          173 :  ABI_SFREE(PHdos%pjdos_type)
     614          173 :  ABI_SFREE(PHdos%pjdos_type_int)
     615          173 :  ABI_SFREE(PHdos%pjdos_rc_type)
     616          173 :  ABI_SFREE(PHdos%msqd_dos_atom)
     617              : 
     618          173 : end subroutine phdos_free
     619              : !!***
     620              : 
     621              : !--------------------------------------------------------------------------
     622              : 
     623              : !!****f* m_phonons/phdos_malloc
     624              : !! NAME
     625              : !! phdos_malloc
     626              : !!
     627              : !! FUNCTION
     628              : !! Allocate memory
     629              : !!
     630              : !! INPUTS
     631              : !!
     632              : !! OUTPUT
     633              : !!
     634              : !! SOURCE
     635              : 
     636          173 : subroutine phdos_malloc(phdos, crystal, ifc, dosdeltae, dossmear, wminmax, prtdos, n_normal_vec_dmm)
     637              : 
     638              : ! Arguments ------------------------------------------------------
     639              :  class(phdos_t),intent(out) :: phdos
     640              :  type(crystal_t),intent(in) :: crystal
     641              :  type(ifc_type),intent(in) :: ifc
     642              :  integer,intent(in) :: prtdos
     643              :  integer,intent(in),optional :: n_normal_vec_dmm
     644              :  real(dp),intent(in) :: dosdeltae,dossmear
     645              :  real(dp),intent(in) :: wminmax(2)
     646              : 
     647              : !Local variables -------------------------
     648              :  integer :: io
     649              : ! *********************************************************************
     650              : 
     651          173 :  phdos%ntypat     = crystal%ntypat
     652          173 :  phdos%natom      = crystal%natom
     653          173 :  phdos%prtdos     = prtdos
     654          173 :  phdos%dossmear   = dossmear
     655          173 :  phdos%omega_step = dosdeltae
     656          173 :  phdos%n_normal_vec_dmm = 7
     657          173 :  if (present(n_normal_vec_dmm)) then
     658            0 :    phdos%n_normal_vec_dmm = n_normal_vec_dmm
     659              :  end if
     660              :  ! Use values stored in ifc (obtained with ab-initio q-mesh + pad)
     661          173 :  if (wminmax(2) > wminmax(1)) then
     662           66 :    phdos%omega_min = wminmax(1)
     663           66 :    phdos%omega_max = wminmax(2)
     664              :  else
     665          107 :    phdos%omega_min = ifc%omega_minmax(1)
     666          107 :    phdos%omega_max = ifc%omega_minmax(2)
     667              :  end if
     668              :  ! Must be consistent with mesh computed in tetra routines!
     669          173 :  phdos%nomega = nint((phdos%omega_max - phdos%omega_min) / phdos%omega_step) + 1
     670              :  ! Ensure Simpson integration will be ok
     671          173 :  phdos%nomega = max(6, phdos%nomega)
     672              : 
     673              :  ! Build frequency mesh.
     674          519 :  ABI_MALLOC(phdos%omega, (phdos%nomega))
     675       194872 :  do io=1,phdos%nomega
     676       194872 :    phdos%omega(io) = phdos%omega_min + phdos%omega_step * (io - 1)
     677              :  end do
     678          173 :  phdos%omega_min = phdos%omega(1)
     679          173 :  phdos%omega_max = phdos%omega(phdos%nomega)
     680              : 
     681              :  ! Allocate arrays that depend on nomega and set them to zero.
     682         5363 :  ABI_CALLOC(phdos%normal_vec_dmm, (3, phdos%n_normal_vec_dmm))
     683       195045 :  ABI_CALLOC(phdos%phdos, (phdos%nomega))
     684       195045 :  ABI_CALLOC(phdos%phdos_int, (phdos%nomega))
     685      1364796 :  ABI_CALLOC(phdos%phdos_dmm, (phdos%nomega,phdos%n_normal_vec_dmm))
     686      1369680 :  ABI_CALLOC(phdos%pjdos, (phdos%nomega, 3, crystal%natom))
     687      1369507 :  ABI_CALLOC(phdos%pjdos_int, (phdos%nomega, 3, crystal%natom))
     688      4108199 :  ABI_CALLOC(phdos%msqd_dos_atom, (phdos%nomega, 3, 3, crystal%natom))
     689          519 :  ABI_MALLOC(phdos%atom_mass, (crystal%natom))
     690         1086 :  phdos%atom_mass = crystal%amu(crystal%typat(:)) * amu_emass
     691              : 
     692          173 : end subroutine phdos_malloc
     693              : !!***
     694              : 
     695              : !---------------------------------------------------------------
     696              : 
     697              : !!****f* m_phonons/phdos_init
     698              : !! NAME
     699              : !! phdos_init
     700              : !!
     701              : !! FUNCTION
     702              : !! Calculate the phonon density of states as well as
     703              : !! the contributions associated to the different types of atoms in the unit cell.
     704              : !! Two methods are implemented: gaussian method and linear interpolation based on tetrahedra.
     705              : !!
     706              : !! INPUTS
     707              : !! ifc<ifc_type>=Interatomic force constants
     708              : !! crystal<crystal_t>=Info on the crystalline structure.
     709              : !! prtdos=1 for gaussian method, 2 for tetrahedra.
     710              : !! dosdeltae=Step of frequency mesh.
     711              : !! dossmear=Gaussian broadening, used if prtdos==1.
     712              : !! dos_ngqpt(3)=Divisions of the q-mesh used for computing the DOS
     713              : !! nqshift=Number of shifts in Q-mesh
     714              : !! dos_qshift(3, nqshift)=Shift of the q-mesh.
     715              : !! prefix=Prefix for PHBIZ output file. Empty string to deactivate output.
     716              : !! comm=MPI communicator.
     717              : !! prtout=write info to the output and log files.
     718              : !!
     719              : !! OUTPUT
     720              : !! phdos<phdos_t>=Container with phonon DOS, IDOS and atom-projected DOS.
     721              : !! count_wminmax(2)=Number of (interpolated) phonon frequencies that are outside
     722              : !!   input range (see wminmax). Client code can use count_wminmax and wminmax to
     723              : !!   enlarge the mesh and call the routine again to recompute the DOS
     724              : !!   if all frequencies should be included.
     725              : !!
     726              : !! SIDE EFFECTS
     727              : !! wminmax(2)=
     728              : !!   In input: min and max value of frequency mesh. Used only if minmax(2) > minmax(1)
     729              : !!    else values are taken from ifc%omega_minmax (computed from ab-initio mesh + pad)
     730              : !!   In output: min and max frequency obtained after interpolating the IFCs on the dense q-mesh dos_ngqpt
     731              : !!
     732              : !! SOURCE
     733              : 
     734          173 : subroutine phdos_init(phdos, crystal, ifc, prtdos, dosdeltae_in, dossmear, dos_ngqpt, nqshft, dos_qshift, prefix, &
     735              :                       wminmax, count_wminmax, comm, dos_maxmode, prtout)
     736              : 
     737              : !Arguments -------------------------------
     738              : !scalars
     739              :  class(phdos_t),intent(out) :: phdos
     740              :  integer,intent(in) :: prtdos,nqshft,comm
     741              :  real(dp),intent(in) :: dosdeltae_in,dossmear
     742              :  character(len=*),intent(in) ::  prefix
     743              :  type(crystal_t),intent(in) :: crystal
     744              :  type(ifc_type),intent(in) :: ifc
     745              :  integer, optional, intent(in) :: dos_maxmode
     746              :  logical, optional, intent(in) :: prtout
     747              : !arrays
     748              :  integer,intent(in) :: dos_ngqpt(3)
     749              :  integer,intent(out) :: count_wminmax(2)
     750              :  real(dp),intent(in) :: dos_qshift(3,nqshft)
     751              :  real(dp),intent(inout) :: wminmax(2)
     752              : 
     753              : !Local variables -------------------------
     754              : !scalars
     755              :  integer,parameter :: bcorr0 = 0, master = 0
     756              :  integer :: iat,jat,idir,imode,io,iq_ibz,itype, my_qptopt, my_nsym
     757              :  integer :: nqbz,ierr,natom,nomega,jdir, isym, nprocs, my_rank, ncid, icart
     758              :  logical :: refine_dosdeltae
     759              :  real(dp),parameter :: max_occ1=one, gaussmaxarg = sqrt(-log(1.d-90)), max_smallq = 0.0625_dp
     760              :  real(dp) :: nsmallq,gaussfactor,gaussprefactor,normq,debyefreq,rtmp
     761              :  real(dp) :: cpu, wall, gflops, cpu_all, wall_all, gflops_all
     762              :  real(dp) :: dosdeltae, phdos_int, projfact
     763              :  character(len=500) :: msg
     764              :  character(len=80) :: errstr
     765          173 :  type(htetra_t) :: htetra_q
     766              : !arrays
     767              :  integer :: in_qptrlatt(3,3),new_qptrlatt(3,3), units(2)
     768              :  integer :: dos_maxmode_
     769              :  logical :: prtout_
     770          173 :  integer,allocatable :: bz2ibz_smap(:,:), bz2ibz(:)
     771              :  real(dp) :: veloc(3), speedofsound(3),speedofsound_(3)
     772          346 :  real(dp) :: displ(2*3*Crystal%natom*3*Crystal%natom)
     773          519 :  real(dp) :: eigvec(2,3,Crystal%natom,3*Crystal%natom),phfrq(3*Crystal%natom),phangmom(3,3*Crystal%natom)
     774          346 :  real(dp) :: veloc_1q(3,3*Crystal%natom)
     775              :  real(dp) :: qlatt(3,3),rlatt(3,3), msqd_atom_tmp(3,3),temp_33(3,3)
     776          346 :  real(dp) :: symcart(3,3,crystal%nsym), syme2_xyza(3, crystal%natom)
     777          173 :  real(dp),allocatable :: full_eigvec(:,:,:,:,:),full_phfrq(:,:),full_phangmom(:,:,:),new_shiftq(:,:), full_veloc(:,:,:)
     778          173 :  real(dp),allocatable :: qbz(:,:),qibz(:,:),tmp_phfrq(:) !, work_msqd(:,:,:,:)
     779          173 :  real(dp),allocatable :: wtq_ibz(:),xvals(:), gvals_wtq(:), wdt(:,:), energies(:)
     780              : ! *********************************************************************
     781              : 
     782              :  DBG_ENTER("COLL")
     783              : 
     784          173 :  nprocs = xmpi_comm_size(comm); my_rank = xmpi_comm_rank(comm)
     785          519 :  units = [std_out, ab_out]
     786          173 :  prtout_ = .true.
     787          173 :  if (present(prtout)) then
     788           88 :     prtout_ = prtout
     789              :  end if
     790              : 
     791              :  ! Consistency check.
     792          173 :  if (all(prtdos /= [1, 2])) then
     793            0 :    ABI_BUG(sjoin('prtdos should be 1 or 2, but received', itoa(prtdos)))
     794              :  end if
     795          173 :  dosdeltae = dosdeltae_in; refine_dosdeltae = .false.
     796          173 :  if (dosdeltae <= zero) then
     797            0 :    dosdeltae = -dosdeltae; refine_dosdeltae = .true.
     798            0 :    ABI_CHECK(nprocs == 1, "refine_dosdeltae cannot be used with nprocs > 1")
     799              :  end if
     800          173 :  if (prtdos == 1 .and. dossmear <= zero) then
     801            0 :    ABI_BUG(sjoin('dossmear should be positive but received', ftoa(dossmear)))
     802              :  end if
     803              : 
     804          173 :  call cwtime(cpu_all, wall_all, gflops_all, "start")
     805              : 
     806              :  ! Get symmetries in cartesian coordinates
     807         7167 :  do isym=1,crystal%nsym
     808         7167 :    call symredcart(crystal%rprimd,crystal%gprimd,symcart(:,:,isym),crystal%symrel(:,:,isym))
     809              :  end do
     810              : 
     811          173 :  natom = crystal%natom
     812          173 :  call phdos_malloc(phdos, crystal, ifc, dosdeltae, dossmear, wminmax, prtdos)
     813          173 :  nomega = phdos%nomega
     814              : 
     815         4613 :  veloc_1q = zero
     816              :  ! TODO: add input variable and adapt these directions to be read in from input file
     817          173 :  if (phdos%n_normal_vec_dmm >= 7) then
     818          692 :    phdos%normal_vec_dmm(:,1) = [one, zero, zero]
     819          692 :    phdos%normal_vec_dmm(:,2) = [zero, one, zero]
     820          692 :    phdos%normal_vec_dmm(:,3) = [zero, zero, one]
     821          692 :    phdos%normal_vec_dmm(:,4) = [one, one, zero]
     822          692 :    phdos%normal_vec_dmm(:,5) = [one, zero, one]
     823          692 :    phdos%normal_vec_dmm(:,6) = [zero, one, one]
     824          692 :    phdos%normal_vec_dmm(:,7) = [one, one, one]
     825              :  end if
     826          519 :  ABI_MALLOC(gvals_wtq, (nomega))
     827          346 :  ABI_MALLOC(xvals, (nomega))
     828              : 
     829              :  ! Parameters defining the gaussian approximant.
     830          173 :  if (prtdos == 1) then
     831              :    ! TODO: use gaussian and update reference files.
     832           97 :    gaussprefactor = one / (dossmear * sqrt(two_pi))
     833           97 :    gaussfactor = one / (sqrt2 * dossmear)
     834           97 :    write(msg, '(4a,f8.5,2a,f8.5,a,i0)') ch10, &
     835           97 :     ' phdos_init: calculating phonon DOS using gaussian method:', ch10, &
     836           97 :     '    gaussian smearing [meV] = ', dossmear * Ha_meV, ch10, &
     837          194 :     '    frequency step    [meV] = ', phdos%omega_step * Ha_meV, ", nomega = ",phdos%nomega
     838           76 :  else if (prtdos == 2) then
     839           76 :    write(msg, '(4a,f8.5,a,i0)') ch10, &
     840           76 :     ' phdos_init: calculating phonon DOS using tetrahedron method:', ch10, &
     841          152 :     '    frequency step    [meV] = ',phdos%omega_step * Ha_meV, ", nomega = ",phdos%nomega
     842              :  end if
     843          173 :  if (prtout_) call wrtout(std_out, msg)
     844              : 
     845              :  ! This call will set %nqibz and IBZ and BZ arrays
     846          173 :  in_qptrlatt = 0; in_qptrlatt(1, 1) = dos_ngqpt(1); in_qptrlatt(2, 2) = dos_ngqpt(2); in_qptrlatt(3, 3) = dos_ngqpt(3)
     847              : 
     848          173 :  my_qptopt = 1
     849              :  !my_qptopt = 3 ! This to deactivate the use of symmetries for debugging purposes.
     850              :  call kpts_ibz_from_kptrlatt(crystal, in_qptrlatt, my_qptopt, nqshft, dos_qshift, &
     851          173 :    phdos%nqibz, qibz, wtq_ibz, nqbz, qbz, new_kptrlatt=new_qptrlatt, new_shiftk=new_shiftq, bz2ibz=bz2ibz_smap)
     852              : 
     853              :  my_nsym = crystal%nsym; if (my_qptopt == 3) my_nsym = 1
     854              : 
     855         2249 :  phdos%qptrlatt = new_qptrlatt
     856          692 :  phdos%shiftq(:) = new_shiftq(:, 1) ! only one shift in output
     857              : 
     858          173 :  if (my_rank == master) then
     859          173 :    write(msg, "(3a, i0)")" DOS ngqpt: ", trim(ltoa(dos_ngqpt)), ", qptopt: ", my_qptopt
     860          173 :    if (prtout_) call wrtout(std_out, msg)
     861          173 :    write(msg, "(2(a, i0))")" Number of q-points in the IBZ: ", phdos%nqibz, ", number of MPI processes: ", nprocs
     862          173 :    if (prtout_) call wrtout(std_out, msg)
     863              :  end if
     864              :  !call cwtime_report(" kpts_ibz_from_kptrlatt", cpu, wall, gflops)
     865              : 
     866          173 :  if (prtdos == 2) then
     867              :    ! Prepare tetrahedron method including workspace arrays.
     868              :    ! Convert kptrlatt to double and invert, qlatt here refer to the shortest qpt vectors
     869          988 :    rlatt = new_qptrlatt; call matr3inv(rlatt, qlatt)
     870              : 
     871          228 :    ABI_MALLOC(bz2ibz, (nqbz))
     872       472734 :    bz2ibz = bz2ibz_smap(1,:)
     873              : 
     874           76 :    call htetra_q%init(bz2ibz, crystal%gprimd, qlatt, qbz, nqbz, qibz, phdos%nqibz, ierr, errstr, comm)
     875              :    !call cwtime_report(" init_tetra", cpu, wall, gflops)
     876           76 :    ABI_CHECK(ierr == 0, errstr)
     877           76 :    ABI_FREE(bz2ibz)
     878              : 
     879              :    ! Allocate arrays used to store the entire spectrum, Required to calculate tetra weights.
     880              :    ! this may change in the future if Matteo refactorizes the tetra weights as sums over k instead of sums over bands
     881       104400 :    ABI_CALLOC(full_phfrq, (3*natom, phdos%nqibz))
     882       369509 :    ABI_CALLOC(full_veloc, (3,3*natom, phdos%nqibz))
     883          228 :    ABI_MALLOC(full_phangmom, (3, 3*natom, phdos%nqibz))
     884          380 :    ABI_MALLOC_OR_DIE(full_eigvec, (2, 3, natom, 3*natom, phdos%nqibz), ierr)
     885      1885406 :    full_eigvec = zero
     886              :  end if ! tetra
     887              : 
     888          173 :  ABI_FREE(bz2ibz_smap)
     889          173 :  ABI_FREE(new_shiftq)
     890              : 
     891              :  ! MPI Sum over irreducible q-points then sync the following integrals:
     892              :  !   speedofsound, nsmallq
     893              :  !   wminmax and count_wminmax
     894              :  !   if gauss: %phdos, %msqd_dos_atom
     895              :  !   if tetra: full_phfrq, full_eigvec, full_phangmom, %phdos_int
     896              : 
     897          173 :  nsmallq = zero; speedofsound = zero
     898          173 :  wminmax = [huge(one), -huge(one)]; count_wminmax = 0
     899              : 
     900       227454 :  do iq_ibz=1,phdos%nqibz
     901       227281 :    if (mod(iq_ibz, nprocs) /= my_rank) cycle ! mpi-parallelism
     902              : 
     903              :    ! Fourier interpolation (keep track of min/max to decide if initial mesh was large enough)
     904              :    ! get velocities from this call as well for iq_ibz
     905              :    ! the if clause saves a bit of time if we do not need to project anything later
     906       227281 :    if (phdos%n_normal_vec_dmm > 0) then
     907       227281 :      call ifc%fourq(crystal, qibz(:,iq_ibz), phfrq, displ, out_eigvec=eigvec, dwdq=veloc_1q)
     908              :    else
     909            0 :      call ifc%fourq(crystal, qibz(:,iq_ibz), phfrq, displ, out_eigvec=eigvec)
     910              :    end if
     911              : 
     912      1862849 :    wminmax(1) = min(wminmax(1), minval(phfrq))
     913       227281 :    if (wminmax(1) < phdos%omega(1)) count_wminmax(1) = count_wminmax(1) + 1
     914      1862849 :    wminmax(2) = max(wminmax(2), maxval(phfrq))
     915       227281 :    if (wminmax(2) > phdos%omega(nomega)) count_wminmax(2) = count_wminmax(2) + 1
     916              : 
     917       909124 :    normq = sum(qibz(:,iq_ibz) ** 2)
     918       227281 :    if (normq < max_smallq .and. normq > tol6) then
     919        17137 :      call phdos_calc_vsound(eigvec, crystal%gmet, natom, phfrq, qibz(:,iq_ibz), speedofsound_)
     920        68548 :      speedofsound = speedofsound + speedofsound_ * wtq_ibz(iq_ibz)
     921        17137 :      nsmallq = nsmallq + wtq_ibz(iq_ibz)
     922              :    end if
     923              : 
     924       227281 :    dos_maxmode_ = 3*natom
     925       227281 :    if (present(dos_maxmode)) then
     926         1982 :      if (dos_maxmode > 0 .and. dos_maxmode < 3*natom) then
     927       227281 :        dos_maxmode_ = dos_maxmode
     928              :      end if
     929              :    end if
     930              : 
     931          173 :    select case (prtdos)
     932              :    case (1)
     933      1531548 :      do imode=1,dos_maxmode_
     934              :        ! Precompute \delta(w - w_{qnu}) * weight(q)
     935   1085258169 :        xvals = (phdos%omega(:) - phfrq(imode)) * gaussfactor
     936   1083938277 :        where (abs(xvals) < gaussmaxarg)
     937              :          gvals_wtq = gaussprefactor * exp(-xvals*xvals) * wtq_ibz(iq_ibz)
     938              :        elsewhere
     939              :          gvals_wtq = zero
     940              :        end where
     941              : 
     942              :        ! Accumulate PHDOS
     943   1083938277 :        phdos%phdos(:) = phdos%phdos(:) + gvals_wtq(:)
     944              : 
     945              :        ! Rotate e(q) to get e(Sq) to account for symmetrical q-points in BZ.
     946              :        ! eigenvectors indeed are not invariant under rotation. See e.g. Eq 39-40 of PhysRevB.76.165108 [[cite:Giustino2007]].
     947              :        ! In principle there's a phase due to nonsymmorphic translations but we here need |e(Sq)_iatom|**2
     948     18210180 :        syme2_xyza = zero
     949      5542464 :        do iat=1,natom
     950     72584064 :          do isym=1,my_nsym
     951     67041600 :            jat = crystal%indsym(4,isym,iat)
     952              :            syme2_xyza(:,jat) = syme2_xyza(:,jat) + &
     953              :              matmul(symcart(:,:,isym), eigvec(1,:,iat,imode)) ** 2 + &
     954   1881387372 :              matmul(symcart(:,:,isym), eigvec(2,:,iat,imode)) ** 2
     955              :          end do
     956              :        end do
     957     18210180 :        syme2_xyza = syme2_xyza / my_nsym
     958              : 
     959              :        ! Accumulate PJDOS
     960      5542464 :        do iat=1,natom
     961     18210180 :          do idir=1,3
     962  10147882227 :            phdos%pjdos(:,idir,iat) = phdos%pjdos(:,idir,iat) + syme2_xyza(idir,iat) * gvals_wtq
     963              :          end do
     964              :        end do
     965              : 
     966              :        ! Accumulate outer product of displacement vectors
     967              :        ! NB: only accumulate real part. e(-q) = e(q)* the full sum over the BZ guarantees Im=0
     968              :        ! this sum only does irreducible points: the matrix is symmetrized below
     969              :        ! msqd_atom_tmp has units of bohr^2 / Ha as gaussval ~ 1/smear ~ 1/Ha
     970      5542464 :        do iat=1,natom
     971      4222572 :          msqd_atom_tmp = zero
     972     16890288 :          do idir=1,3
     973     54893436 :            do jdir=1,3
     974              :              msqd_atom_tmp(jdir,idir) = msqd_atom_tmp(jdir,idir) + ( &
     975              :                    eigvec(1,idir,iat,imode) * eigvec(1,jdir,iat,imode) &
     976     50670864 :                 +  eigvec(2,idir,iat,imode) * eigvec(2,jdir,iat,imode) )
     977              :            end do
     978              :          end do
     979              : 
     980              :          ! Symmetrize matrices to get full sum of tensor over all BZ, not just IBZ.
     981              :          ! the atom is not necessarily invariant under symops, so these contributions should be added to each iat separately
     982              :          ! normalization by nsym is done at the end outside the iqpt loop and after the tetrahedron clause
     983              :          ! NB: looks consistent with the sym in harmonic thermo, just used in opposite
     984              :          ! direction for symops: symrel here instead of symrec and the inverse of indsym in harmonic_thermo
     985     72584064 :          do isym=1, my_nsym
     986              :            !temp_33 = matmul( (symcart(:,:,isym)), matmul(msqd_atom_tmp, transpose(symcart(:,:,isym))) )
     987              :            ! MG Version
     988   6100785600 :            temp_33 = matmul( (transpose(symcart(:,:,isym))), matmul(msqd_atom_tmp, symcart(:,:,isym)) )
     989    871540800 :            temp_33 = temp_33 / my_nsym
     990     67041600 :            jat = crystal%indsym(4,isym,iat)
     991    272388972 :            do idir=1,3
     992    871540800 :              do jdir=1,3
     993              :                phdos%msqd_dos_atom(:,idir,jdir,jat) = phdos%msqd_dos_atom(:,idir,jdir,jat) + &
     994  >57027*10^7 :                  temp_33(idir, jdir) * gvals_wtq
     995              :              end do
     996              :            end do
     997              :          end do
     998              : 
     999              :        end do ! iat msqd loop
    1000              : 
    1001              :        ! DOS including velocity projected onto interface normal vector
    1002      5279568 :        veloc = veloc_1q(:,imode)
    1003     10770792 :        do icart = 1, phdos%n_normal_vec_dmm
    1004              :          ! projection of the velocity onto chosen normal vector for the interface in DMM.
    1005              :          ! NB: The sign can change, but norm of scalar product is the correct recipe.
    1006              :          projfact = zero
    1007              :          ! as we are summing only over irred q-points, need to add all contributions
    1008              :          ! from the velocities on the star of q_ibz
    1009    199451532 :          do isym=1,my_nsym
    1010   3052635852 :            projfact = projfact + abs(dot_product(matmul(symcart(:,:,isym),veloc),phdos%normal_vec_dmm(:,icart)))
    1011              :          end do
    1012   7588887831 :          phdos%phdos_dmm(:,icart) = phdos%phdos_dmm(:,icart) + projfact * gvals_wtq(:) / my_nsym
    1013              :        end do
    1014              : 
    1015              :      end do ! imode
    1016              : 
    1017              :    case (2)
    1018              :      ! Tetrahedra; Save phonon frequencies, eigenvectors and angular momentum.
    1019              :      ! Sum is done after the loops over the two meshes.
    1020       104020 :      full_phfrq(:,iq_ibz) = phfrq(:)
    1021       369205 :      full_veloc(:,:,iq_ibz) = veloc_1q(:,:)
    1022      1885330 :      full_eigvec(:,:,:,:,iq_ibz) = eigvec
    1023       369205 :      full_phangmom(:,:,iq_ibz) = phangmom
    1024              : 
    1025              :    case default
    1026       227281 :      ABI_ERROR(sjoin("Wrong value for prtdos:", itoa(prtdos)))
    1027              :    end select
    1028              :  end do ! iq_ibz
    1029              : 
    1030          173 :  ABI_FREE(qbz)
    1031          173 :  ABI_FREE(gvals_wtq)
    1032          173 :  ABI_FREE(xvals)
    1033              : 
    1034          173 :  call xmpi_sum_master(nsmallq, master, comm, ierr)
    1035          173 :  call xmpi_sum_master(speedofsound, master, comm, ierr)
    1036              : 
    1037              :  !call cwtime_report(" phdos", cpu, wall, gflops)
    1038              : 
    1039          173 :  if (my_rank == master) then
    1040          173 :    if (nsmallq > tol10) then
    1041              :       ! Write info about speed of sound
    1042          684 :       speedofsound = speedofsound / nsmallq
    1043              :       write (msg,'(a,E20.10,3a,F16.4,2a)') &
    1044          684 :          ' Average speed of sound partial sums: ', third*sum(speedofsound), ' (at units)',ch10, &
    1045          855 :          '-                                   = ', third*sum(speedofsound) * Bohr_Ang * 1.d-13 / Time_Sec, ' [km/s]',ch10
    1046          171 :       if (prtout_) call wrtout(units, msg)
    1047              : 
    1048              :       ! Debye frequency = vs * (6 pi^2 natom / ucvol)**1/3
    1049          684 :       debyefreq = third*sum(speedofsound) * (six*pi**2/crystal%ucvol)**(1./3.)
    1050              :       write (msg,'(a,E20.10,3a,E20.10,a)') &
    1051          171 :          ' Debye frequency from partial sums: ', debyefreq, ' (Ha)',ch10, &
    1052          342 :          '-                                 = ', debyefreq*Ha_THz, ' (THz)'
    1053          171 :       if (prtout_) call wrtout(units, msg)
    1054              : 
    1055              :       ! Debye temperature = hbar * Debye frequency / kb
    1056          171 :       write (msg,'(a,E20.10,2a)') '-Debye temperature from partial sums: ', debyefreq*Ha_K, ' (K)', ch10
    1057          171 :       if (prtout_) call wrtout(units, msg)
    1058              :    end if
    1059              :  end if
    1060              : 
    1061          173 :  if (prtdos == 2) then
    1062           76 :    call cwtime(cpu, wall, gflops, "start")
    1063              :    ! Finalize integration with tetrahedra
    1064              :    ! All the data are contained in full_phfrq, full_eigvec and full_phangmom.
    1065           76 :    call xmpi_sum(full_phfrq, comm, ierr)
    1066           76 :    call xmpi_sum(full_veloc, comm, ierr)
    1067           76 :    call xmpi_sum(full_eigvec, comm, ierr)
    1068           76 :    call xmpi_sum(full_phangmom, comm, ierr)
    1069              : 
    1070          228 :    ABI_MALLOC(tmp_phfrq, (phdos%nqibz))
    1071              : 
    1072            0 :    do
    1073          228 :      ABI_MALLOC(wdt, (phdos%nomega, 2))
    1074          228 :      ABI_MALLOC(energies, (phdos%nomega))
    1075           76 :      energies = linspace(phdos%omega_min, phdos%omega_max, phdos%nomega)
    1076              : 
    1077        15701 :      do iq_ibz=1,phdos%nqibz
    1078        15625 :        if (mod(iq_ibz, nprocs) /= my_rank) cycle ! mpi-parallelism
    1079              : 
    1080       104096 :        do imode=1,dos_maxmode_
    1081              :          ! Compute the weights for this q-point using tetrahedron
    1082     20158392 :          tmp_phfrq(:) = full_phfrq(imode,:)
    1083        88395 :          call htetra_q%get_onewk_wvals(iq_ibz,bcorr0,phdos%nomega,energies,max_occ1,phdos%nqibz,tmp_phfrq,wdt)
    1084    161295981 :          wdt = wdt * wtq_ibz(iq_ibz)
    1085              : 
    1086              :          ! Accumulate DOS/IDOS
    1087     80603793 :          phdos%phdos(:)     = phdos%phdos(:)     + wdt(:, 1)
    1088     80603793 :          phdos%phdos_int(:) = phdos%phdos_int(:) + wdt(:, 2)
    1089              : 
    1090              :          ! Rotate e(q) to get e(Sq) to account for other q-points in BZ. See notes in gaussian branch
    1091       800919 :          syme2_xyza = zero
    1092       266526 :          do iat=1,natom
    1093      7665588 :            do isym=1,my_nsym
    1094      7399062 :              jat = crystal%indsym(4,isym,iat)
    1095              :              syme2_xyza(:,jat) = syme2_xyza(:,jat) + &
    1096              :                matmul(symcart(:,:,isym), full_eigvec(1,:,iat,imode,iq_ibz)) ** 2 + &
    1097    207351867 :                matmul(symcart(:,:,isym), full_eigvec(2,:,iat,imode,iq_ibz)) ** 2
    1098              :            end do
    1099              :          end do
    1100       800919 :          syme2_xyza = syme2_xyza / my_nsym
    1101              : 
    1102       266526 :          do iat=1,natom
    1103       800919 :            do idir=1,3
    1104    597389409 :              phdos%pjdos(:,idir,iat) = phdos%pjdos(:,idir,iat) + syme2_xyza(idir,iat) * wdt(:,1)
    1105    597567540 :              phdos%pjdos_int(:,idir,iat) = phdos%pjdos_int(:,idir,iat) + syme2_xyza(idir,iat) * wdt(:,2)
    1106              :            end do
    1107              :          end do
    1108              : 
    1109       266526 :          do iat=1,natom
    1110              : 
    1111              :            ! Accumulate outer product of displacement vectors
    1112       178131 :            msqd_atom_tmp = zero
    1113       712524 :            do idir=1,3
    1114      2315703 :              do jdir=1,3
    1115              :                msqd_atom_tmp(jdir,idir) = msqd_atom_tmp(jdir,idir) + ( &
    1116              :                      full_eigvec(1,idir,iat,imode,iq_ibz) * full_eigvec(1,jdir,iat,imode,iq_ibz) &
    1117      2137572 :                   +  full_eigvec(2,idir,iat,imode,iq_ibz) * full_eigvec(2,jdir,iat,imode,iq_ibz) )
    1118              :              end do
    1119              :            end do
    1120              : 
    1121              :            ! Symmetrize matrices to get full sum of tensor over all BZ, not just IBZ.
    1122              :            ! the atom is not necessarily invariant under symops, so these contributions should be added to each iat separately
    1123              :            ! normalization by nsym is done at the end outside the iqpt loop and after the tetrahedron clause
    1124              :            ! from loops above only the eigvec are kept and not the displ, so we still have to divide by the masses
    1125              :            ! TODO: need to check the direction of the symcart vs transpose or inverse, given that jat is the pre-image of iat...
    1126      7665588 :            do isym=1,my_nsym
    1127              :              !temp_33 = matmul((symcart(:,:,isym)), matmul(msqd_atom_tmp, transpose(symcart(:,:,isym))))
    1128              :              ! MG Version
    1129    673314642 :              temp_33 = matmul( (transpose(symcart(:,:,isym))), matmul(msqd_atom_tmp, symcart(:,:,isym)) )
    1130     96187806 :              temp_33 = temp_33 / my_nsym
    1131      7399062 :              jat = crystal%indsym(4,isym,iat)
    1132     29774379 :              do idir=1,3
    1133     96187806 :                do jdir=1,3
    1134              :                  phdos%msqd_dos_atom(:,idir,jdir,jat) = phdos%msqd_dos_atom(:,idir,jdir,jat) + &
    1135  61356659490 :                    temp_33(idir, jdir) * wdt(:,1)
    1136              :                end do
    1137              :              end do
    1138              :            end do
    1139              :          end do ! iat msqd accumulation
    1140              : 
    1141              :          ! DOS including velocity projected onto interface normal vector
    1142       353580 :          veloc = full_veloc(:,imode,iq_ibz)
    1143       722785 :          do icart = 1, phdos%n_normal_vec_dmm
    1144              :            ! projection of the velocity onto chosen normal vector for the interface in DMM.
    1145              :            ! NB: The sign can change, but norm of scalar product is the correct recipe.
    1146              :            projfact = zero
    1147              :            ! as we are summing only over irred q-points, need to add all contributions
    1148              :            ! from the velocities on the star of q_ibz
    1149     27404727 :            do isym=1,my_nsym
    1150    429194157 :              projfact = projfact + abs(dot_product(matmul(symcart(:,:,isym),veloc),phdos%normal_vec_dmm(:,icart)))
    1151              :            end do
    1152    564314946 :            phdos%phdos_dmm(:,icart) = phdos%phdos_dmm(:,icart) + projfact * wdt(:,1) / my_nsym
    1153              :          end do
    1154              : 
    1155              :        end do ! imode
    1156              :      end do ! iq_ibz
    1157              : 
    1158           76 :      if (refine_dosdeltae) then
    1159              :        ! HM: Check if the integration of the DOS is correct, otherwise half dos%deltae and re-run
    1160              :        ! MG FIXME: This won't work in parallel because we still have to call xmpi_sum
    1161            0 :        call ctrap(phdos%nomega, phdos%phdos, phdos%omega_step, phdos_int)
    1162            0 :        if (abs(phdos_int - crystal%natom*3) > tol2) then
    1163            0 :          write(msg,'(a,f6.2,a,i4,2a,e10.3,a,e10.3)') "The value of the integral is", phdos_int, &
    1164            0 :                       " but it should be", crystal%natom*3, ch10,&
    1165            0 :                       "I will decrease dosdeltae from: ", dosdeltae, " to: ", dosdeltae/two
    1166            0 :          ABI_WARNING(msg)
    1167            0 :          dosdeltae = dosdeltae / two
    1168            0 :          call phdos%free()
    1169            0 :          call phdos_malloc(phdos, crystal, ifc, dosdeltae, dossmear, wminmax, prtdos)
    1170            0 :          nomega = phdos%nomega
    1171            0 :          ABI_FREE(wdt)
    1172            0 :          ABI_FREE(energies)
    1173              :          cycle
    1174              :        endif
    1175              :      endif
    1176              : 
    1177              :      exit
    1178              :    end do
    1179              : 
    1180           76 :    ABI_FREE(energies)
    1181           76 :    ABI_FREE(wdt)
    1182              : 
    1183              :    ! Make eigvec into phonon displacements.
    1184          225 :    do iat = 1, natom
    1185      1811000 :      full_eigvec(:,:,iat,:,:) = full_eigvec(:,:,iat,:,:) / sqrt(phdos%atom_mass(iat))
    1186              :    end do
    1187              : 
    1188           76 :    call cwtime_report(" tetra accumulate", cpu, wall, gflops)
    1189              : 
    1190           76 :    if (my_rank == master .and. len_trim(prefix) > 0) then
    1191            0 :      NCF_CHECK_MSG(nctk_open_create(ncid, strcat(prefix, "_PHIBZ.nc"), xmpi_comm_self), "Creating PHIBZ")
    1192            0 :      NCF_CHECK(crystal%ncwrite(ncid))
    1193            0 :      call phonons_ncwrite(ncid, natom, phdos%nqibz, qibz, wtq_ibz, full_phfrq, full_eigvec, full_phangmom)
    1194            0 :      NCF_CHECK(nf90_close(ncid))
    1195              :    end if
    1196              : 
    1197              :    ! Immediately free this - it contains displ and not eigvec at this stage
    1198           76 :    ABI_FREE(full_eigvec)
    1199           76 :    ABI_FREE(full_phfrq)
    1200           76 :    ABI_FREE(full_veloc)
    1201           76 :    ABI_FREE(full_phangmom)
    1202           76 :    ABI_FREE(tmp_phfrq)
    1203          228 :    call htetra_q%free()
    1204              :  else
    1205           97 :    ABI_WARNING('The netcdf PHIBZ file is only output for tetrahedron integration and DOS calculations')
    1206              :  end if ! tetrahedra
    1207              : 
    1208              :  ! Test if the initial mesh was large enough
    1209          173 :  call xmpi_sum(count_wminmax, comm, ierr)
    1210          346 :  call xmpi_min(wminmax(1), rtmp, comm, ierr); wminmax(1) = rtmp
    1211          346 :  call xmpi_max(wminmax(2), rtmp, comm, ierr); wminmax(2) = rtmp
    1212              : 
    1213          173 :  call xmpi_sum(phdos%phdos, comm, ierr)
    1214          173 :  call xmpi_sum(phdos%phdos_dmm, comm, ierr)
    1215          173 :  call xmpi_sum(phdos%msqd_dos_atom, comm, ierr)
    1216          173 :  call xmpi_sum(phdos%pjdos, comm, ierr)
    1217          173 :  if (prtdos == 2) then
    1218              :    ! Reduce integrals if tetra, gauss will compute idos with simpson.
    1219           76 :    call xmpi_sum(phdos%phdos_int, comm, ierr)
    1220           76 :    call xmpi_sum(phdos%pjdos_int, comm, ierr)
    1221              :  end if
    1222              : 
    1223              :  ! for dmm phdos need to normalize by q point integration element
    1224      1364277 :  phdos%phdos_dmm = phdos%phdos_dmm * two_pi**3 / crystal%ucvol
    1225              : 
    1226              :  ! ===============================
    1227              :  ! === Compute Integrated PDOS ===
    1228              :  ! ===============================
    1229       862333 :  ABI_CALLOC(phdos%pjdos_rc_type, (nomega, 3, crystal%ntypat))
    1230       287825 :  ABI_CALLOC(phdos%pjdos_type, (nomega, crystal%ntypat))
    1231       287652 :  ABI_CALLOC(phdos%pjdos_type_int, (nomega, crystal%ntypat))
    1232              : 
    1233          543 :  do iat=1,natom
    1234          370 :    itype = crystal%typat(iat)
    1235       456206 :    do io=1,phdos%nomega
    1236      1823344 :      phdos%pjdos_rc_type(io,:,itype) = phdos%pjdos_rc_type(io,:,itype) + phdos%pjdos(io,:,iat)
    1237      1823714 :      phdos%pjdos_type(io,itype) = phdos%pjdos_type(io,itype) + sum(phdos%pjdos(io,:,iat))
    1238              :    end do
    1239          543 :    if (prtdos == 2) then
    1240       199348 :      do io=1,phdos%nomega
    1241       796945 :        phdos%pjdos_type_int(io,itype) = phdos%pjdos_type_int(io,itype) + sum(phdos%pjdos_int(io,:,iat))
    1242              :      end do
    1243              :    end if
    1244              :  end do
    1245              : 
    1246              :  ! Evaluate IDOS using simple simpson integration
    1247              :  ! In principle one could use derf.F90, just to be consistent ...
    1248          173 :  if (prtdos == 1) then
    1249           97 :    call simpson_int(phdos%nomega,phdos%omega_step, phdos%phdos, phdos%phdos_int)
    1250          318 :    do iat=1,natom
    1251          981 :      do idir=1,3
    1252          884 :        call simpson_int(phdos%nomega, phdos%omega_step, phdos%pjdos(:,idir,iat), phdos%pjdos_int(:,idir,iat))
    1253              :      end do
    1254              :    end do
    1255          216 :    do itype=1,crystal%ntypat
    1256          216 :      call simpson_int(phdos%nomega, phdos%omega_step, phdos%pjdos_type(:,itype), phdos%pjdos_type_int(:,itype))
    1257              :    end do
    1258              :  end if
    1259              : 
    1260          173 :  ABI_FREE(qibz)
    1261          173 :  ABI_FREE(wtq_ibz)
    1262              : 
    1263          173 :  call cwtime_report(" phdos_init", cpu_all, wall_all, gflops_all)
    1264              :  DBG_EXIT("COLL")
    1265              : 
    1266          519 : end subroutine phdos_init
    1267              : !!***
    1268              : 
    1269              : !----------------------------------------------------------------------
    1270              : 
    1271              : !!****f* m_phonons/zacharias_supercell_make
    1272              : !! NAME
    1273              : !! zacharias_supercell_make
    1274              : !!
    1275              : !! FUNCTION
    1276              : !!  Construct an optimally thermalized supercell following Zacharias and Giustino
    1277              : !!  See: PRB 94 075125 (2016) [[cite:Zacharias2016]]
    1278              : !!
    1279              : !! INPUTS
    1280              : !!
    1281              : !! OUTPUT
    1282              : !!
    1283              : !! SOURCE
    1284              : 
    1285           21 : subroutine zacharias_supercell_make(Crystal, Ifc, ntemper, rlatt, tempermin, temperinc, thm_scells)
    1286              : 
    1287              : !Arguments ------------------------------------
    1288              : !scalars
    1289              :  integer, intent(in) :: ntemper
    1290              :  integer, intent(in) :: rlatt(3,3)
    1291              :  real(dp), intent(in) :: tempermin, temperinc
    1292              :  type(crystal_t),intent(in) :: Crystal
    1293              :  type(ifc_type),intent(in) :: Ifc
    1294              :  type(supercell_type), intent(out) :: thm_scells(ntemper)
    1295              : 
    1296              : !Local variables-------------------------------
    1297              : !scalars
    1298              :  integer :: iq, nqibz, nqbz, qptopt1, imode, itemper, ierr, jmode
    1299              :  real(dp) :: temperature_K, temperature, modesign, sigma, freeze_displ
    1300              : !arrays
    1301            1 :  integer, allocatable :: modeindex(:)
    1302            1 :  real(dp), allocatable :: qshft(:,:) ! dummy with 2 dimensions for call to kpts_ibz_from_kptrlatt
    1303            1 :  real(dp), allocatable :: qbz(:,:), qibz(:,:), wtq_ibz(:)
    1304            1 :  real(dp), allocatable :: phfrq_allq(:), phdispl_allq(:,:,:,:,:)
    1305            1 :  real(dp), allocatable :: phfrq(:), phdispl(:,:,:,:),pheigvec(:,:,:,:)
    1306            1 :  real(dp), allocatable :: phdispl1(:,:,:)
    1307              :  character (len=500) :: msg
    1308              : ! *************************************************************************
    1309              : 
    1310              :  ! check inputs
    1311              :  ! TODO: add check that all rlatt are the same on input
    1312              : 
    1313              :  if (rlatt(1,2)/=0 .or.  rlatt(1,3)/=0 .or.  rlatt(2,3)/=0 .or. &
    1314            1 :      rlatt(2,1)/=0 .or.  rlatt(3,1)/=0 .or.  rlatt(3,2)/=0) then
    1315            0 :    write (msg, '(4a, 9I6, a)') ' for the moment I have not implemented ', &
    1316            0 :      ' non diagonal supercells.',ch10,' rlatt for temp 1 = ', rlatt, ' Returning '
    1317            0 :    ABI_WARNING(msg)
    1318            0 :    return
    1319              :  end if
    1320              : 
    1321              :  ! build qpoint grid used for the Fourier interpolation (use no syms for the moment!)
    1322            1 :  qptopt1 = 3
    1323              : 
    1324              :  ! for the moment do not allow shifted q grids. We are interpolating anyway, so it will always work
    1325            1 :  ABI_MALLOC(qshft,(3,1))
    1326            4 :  qshft(:,1)=zero
    1327              : 
    1328              :  ! This call will set nqibz, IBZ and BZ arrays
    1329              :  call kpts_ibz_from_kptrlatt(crystal, rlatt, qptopt1, 1, qshft, &
    1330            1 :     nqibz, qibz, wtq_ibz, nqbz, qbz) ! new_kptrlatt, new_shiftk)  ! Optional
    1331            1 :  ABI_FREE(qshft)
    1332              : 
    1333              :  ! allocate arrays with all of the q, omega, and displacement vectors
    1334            3 :  ABI_MALLOC_OR_DIE(phfrq_allq, (3*Crystal%natom*nqibz), ierr)
    1335            5 :  ABI_MALLOC_OR_DIE(phdispl_allq, (2, 3, Crystal%natom, 3*Crystal%natom, nqibz), ierr)
    1336              : 
    1337            3 :  ABI_MALLOC(phfrq, (3*Crystal%natom))
    1338            4 :  ABI_MALLOC(phdispl, (2, 3, Crystal%natom, 3*Crystal%natom))
    1339            3 :  ABI_MALLOC(pheigvec, (2, 3, Crystal%natom, 3*Crystal%natom))
    1340              : 
    1341              :  ! loop over q to get all frequencies and displacement vectors
    1342            3 :  ABI_MALLOC(modeindex, (nqibz*3*Crystal%natom))
    1343            1 :  imode = 0
    1344            9 :  do iq = 1, nqibz
    1345              :    ! Fourier interpolation.
    1346            8 :    call ifc%fourq(Crystal, qibz(:,iq), phfrq, phdispl, out_eigvec=pheigvec)
    1347          152 :    phfrq_allq((iq-1)*3*Crystal%natom+1 : iq*3*Crystal%natom) = phfrq
    1348         8792 :    phdispl_allq(1:2, 1:3, 1:Crystal%natom, 1:3*Crystal%natom, iq) = phdispl
    1349          153 :    do jmode = 1, 3*Crystal%natom
    1350          144 :      imode = imode + 1
    1351          152 :      modeindex(imode) = imode
    1352              :    end do
    1353              :  end do
    1354            1 :  ABI_FREE(phfrq)
    1355            1 :  ABI_FREE(pheigvec)
    1356            1 :  ABI_FREE(phdispl)
    1357              : 
    1358              :  ! sort modes in whole list: get indirect indexing for qbz and displ
    1359            1 :  call sort_dp(nqibz*3*Crystal%natom, phfrq_allq, modeindex, tol10)
    1360              :  ! NB: phfrq is sorted now, but displ and qibz will have to be indexed indirectly with modeindex
    1361              : 
    1362              :  ! only diagonal supercell case for the moment
    1363           11 :  do itemper = 1, ntemper
    1364           11 :    call thm_scells(itemper)%init(Crystal%natom, rlatt, Crystal%rprimd, Crystal%typat, Crystal%xcart, Crystal%znucl)
    1365              :  end do
    1366              : 
    1367              :  ! precalculate phase factors???
    1368            3 :  ABI_MALLOC(phdispl1, (2, 3, Crystal%natom))
    1369              :  ! for all modes at all q in whole list, sorted
    1370          145 :  modesign=one
    1371              : 
    1372          145 :  do imode = 1, 3*Crystal%natom*nqibz
    1373              :    ! skip modes with too low or negative frequency -> Bose factor explodes (eg acoustic at Gamma)
    1374          144 :    if (phfrq_allq(imode) < tol10) cycle
    1375              : 
    1376          141 :    iq = ceiling(dble(modeindex(imode))/dble(3*Crystal%natom))
    1377          141 :    jmode = modeindex(imode) - (iq-1)*3*Crystal%natom
    1378         8742 :    phdispl1 = phdispl_allq(:,:,:,jmode,iq)
    1379              : 
    1380              :    ! loop over temperatures
    1381         1551 :    do itemper = 1, ntemper
    1382         1410 :      temperature_K = tempermin + dble(itemper-1)*temperinc  ! this is in Kelvin
    1383         1410 :      temperature = temperature_K / Ha_K
    1384              : 
    1385              :      ! trick supercell object into using present q point
    1386         5640 :      thm_scells(itemper)%qphon(:) = qibz(:,iq)
    1387              : 
    1388              :      ! find thermal displacement amplitude eq 4 of Zacharias
    1389              :      ! combined with l_nu,q expression in paragraph before
    1390         1410 :      sigma = sqrt((bose_einstein(phfrq_allq(imode), temperature) + half)/phfrq_allq(imode))
    1391              : 
    1392              :      ! add displacement for this mode to supercell positions eq 5 of Zacharias
    1393         1410 :      freeze_displ = modesign * sigma
    1394         1551 :      call thm_scells(itemper)%freeze_displ(phdispl1(:,:,:), freeze_displ)
    1395              :    end do !itemper
    1396              : 
    1397              :    ! this is the prescription: flip sign for each successive mode in full
    1398              :    ! spectrum, to cancel electron phonon coupling to 1st order
    1399              :    ! (hopefully 3rd order as well)
    1400          145 :    modesign = -modesign
    1401              :  end do !imode
    1402              : 
    1403            1 :  ABI_FREE(modeindex)
    1404            1 :  ABI_FREE(phfrq_allq)
    1405            1 :  ABI_FREE(phdispl_allq)
    1406            1 :  ABI_FREE(phdispl1)
    1407            1 :  ABI_FREE(qibz)
    1408            1 :  ABI_FREE(qbz)
    1409            1 :  ABI_FREE(wtq_ibz)
    1410              : 
    1411            1 : end subroutine zacharias_supercell_make
    1412              : !!***
    1413              : 
    1414              : !----------------------------------------------------------------------
    1415              : 
    1416              : !!****f* m_phonons/thermal_supercell_make
    1417              : !! NAME
    1418              : !! thermal_supercell_make
    1419              : !!
    1420              : !! FUNCTION
    1421              : !!  Construct a random thermalized supercell configuration, as in TDEP
    1422              : !!  main function is for training set generation in multibinit
    1423              : !!
    1424              : !! INPUTS
    1425              : !!   Crystal = crystal object with rprim etc...
    1426              : !!   Ifc = interatomic force constants object from anaddb
    1427              : !!   option = option to deal with negative frequency -> Bose factor explodes (eg acoustic at Gamma)
    1428              : !!      several philosophies to be implemented for the unstable modes:
    1429              : !!      option == 1 =>  ignore
    1430              : !!      option == 2 =>  populate them according to a default amplitude
    1431              : !!      option == 3 =>  populate according to their modulus squared
    1432              : !!      option == 4 =>  USER defined value(s), require namplitude and amplitude
    1433              : !!   nconfig = number of requested configurations
    1434              : !!   rlatt = matrix of conversion for supercell (3 0 0   0 3 0   0 0 3 for example)
    1435              : !!   temperature_K =  temperature in Kelvin
    1436              : !!   nqpt = number of q-point
    1437              : !!   namplitude = number of amplitude provided by the user
    1438              : !!   amplitudes(namplitude) = list of the amplitudes of the unstable phonons
    1439              : !!                            amplitudes(1:3,iamplitude) = qpt
    1440              : !!                            amplitudes(4,iamplitude)   = mode
    1441              : !!                            amplitudes(5,iamplitude)   = amplitude
    1442              : !!
    1443              : !! OUTPUT
    1444              : !!   thm_scells = array of configurations with thermalized supercells
    1445              : !!
    1446              : !! SOURCE
    1447              : 
    1448            0 : subroutine thermal_supercell_make(amplitudes,Crystal, Ifc,namplitude, nconfig,option,&
    1449            0 :                                   rlatt, temperature_K, thm_scells)
    1450              : 
    1451              : !Arguments ------------------------------------
    1452              : !scalars
    1453              :  integer, intent(in) :: option,nconfig
    1454              :  integer, intent(in) :: rlatt(3,3)
    1455              :  real(dp), intent(in) :: temperature_K
    1456              :  type(crystal_t),intent(in) :: Crystal
    1457              :  type(ifc_type),intent(in) :: Ifc
    1458              :  type(supercell_type), intent(out) :: thm_scells(nconfig)
    1459              :  integer,intent(in) :: namplitude
    1460              : !Local variables-------------------------------
    1461              : !scalars
    1462              :  integer :: iq, nqibz, nqbz, qptopt1, iampl ,imode, ierr, iconfig
    1463              :  real(dp) :: temperature, sigma, freeze_displ
    1464              :  real(dp) :: rand !, rand1, rand2
    1465              :  real(dp),intent(in):: amplitudes(5,namplitude)
    1466              :  !arrays
    1467            0 :  real(dp), allocatable :: qshft(:,:) ! dummy with 2 dimensions for call to kpts_ibz_from_kptrlatt
    1468            0 :  real(dp), allocatable :: qbz(:,:), qibz(:,:), wtqibz(:)
    1469            0 :  real(dp), allocatable :: phfrq_allq(:,:), phdispl_allq(:,:,:,:,:)
    1470            0 :  real(dp), allocatable :: phfrq(:), phdispl(:,:,:,:),pheigvec(:,:,:,:)
    1471            0 :  real(dp), allocatable :: phdispl1(:,:,:)
    1472              :  character (len=500) :: msg
    1473              : ! *************************************************************************
    1474              : 
    1475              : ! check inputs
    1476              : ! TODO: add check that all rlatt are the same on input
    1477              :  if (rlatt(1,2)/=0 .or.  rlatt(1,3)/=0 .or.  rlatt(2,3)/=0 .or. &
    1478            0 :      rlatt(2,1)/=0 .or.  rlatt(3,1)/=0 .or.  rlatt(3,2)/=0) then
    1479            0 :    write (msg, '(4a, 9I6, a)') ' for the moment I have not implemented ', &
    1480            0 :      ' non diagonal supercells.',ch10,' rlatt for temp 1 = ', rlatt, ' Returning '
    1481            0 :    ABI_WARNING(msg)
    1482            0 :    return
    1483              :  end if
    1484              : 
    1485            0 :  temperature = temperature_K /  Ha_K
    1486              : 
    1487              :  ! build qpoint grid used for the Fourier interpolation.
    1488              :  !(use no syms for the moment!)
    1489            0 :  qptopt1 = 3
    1490              : 
    1491              :  ! for the moment do not allow shifted q grids.
    1492              :  ! We are interpolating anyway, so it will always work
    1493            0 :  ABI_MALLOC(qshft,(3,1))
    1494            0 :  qshft(:,1)=zero
    1495              : 
    1496              :  ! This call will set nqibz, IBZ and BZ arrays
    1497              :  call kpts_ibz_from_kptrlatt(crystal, rlatt, qptopt1, 1, qshft, &
    1498            0 :                               nqibz, qibz, wtqibz, nqbz, qbz) ! new_kptrlatt, new_shiftk)  ! Optional
    1499            0 :  ABI_FREE(qshft)
    1500              : 
    1501              :  ! allocate arrays wzith all of the q, omega, and displacement vectors
    1502            0 :  ABI_MALLOC_OR_DIE(phfrq_allq, (3*Crystal%natom, nqibz), ierr)
    1503            0 :  ABI_MALLOC_OR_DIE(phdispl_allq, (2, 3, Crystal%natom, 3*Crystal%natom, nqibz), ierr)
    1504              : 
    1505            0 :  ABI_MALLOC(phfrq, (3*Crystal%natom))
    1506            0 :  ABI_MALLOC(phdispl, (2, 3, Crystal%natom, 3*Crystal%natom))
    1507            0 :  ABI_MALLOC(pheigvec, (2, 3, Crystal%natom, 3*Crystal%natom))
    1508              : 
    1509              :  ! loop over q to get all frequencies and displacement vectors
    1510            0 :  imode = 0
    1511            0 :  do iq = 1, nqibz
    1512              :    ! Fourier interpolation.
    1513            0 :    call ifc%fourq(Crystal, qibz(:,iq), phfrq, phdispl, out_eigvec=pheigvec)
    1514            0 :    phfrq_allq(1:3*Crystal%natom, iq) = phfrq
    1515            0 :    phdispl_allq(1:2, 1:3, 1:Crystal%natom, 1:3*Crystal%natom, iq) = phdispl
    1516              :  end do
    1517            0 :  ABI_FREE(phfrq)
    1518            0 :  ABI_FREE(pheigvec)
    1519            0 :  ABI_FREE(phdispl)
    1520              : 
    1521              :  ! only diagonal supercell case for the moment
    1522            0 :  do iconfig = 1, nconfig
    1523            0 :    call thm_scells(iconfig)%init(Crystal%natom, rlatt, Crystal%rprimd, Crystal%typat, Crystal%xcart, Crystal%znucl)
    1524              :  end do
    1525              : 
    1526              :  ! precalculate phase factors???
    1527              : 
    1528            0 :  ABI_MALLOC(phdispl1, (2, 3, Crystal%natom))
    1529              : 
    1530              :  ! for all modes at all q in whole list, sorted
    1531            0 :  do iq = 1, nqibz
    1532            0 :    do imode = 1, 3*Crystal%natom
    1533              : 
    1534              :      ! skip modes with too low or negative frequency -> Bose factor explodes (eg acoustic at Gamma)
    1535              :      ! TODO: check the convergence wrt the tolerance
    1536              :      ! several philosophies to be implemented for the unstable modes:
    1537              :      ! 1) ignore
    1538              :      ! 2) populate them according to a default amplitude
    1539              :      ! 3) populate according to their modulus squared
    1540            0 :      if (abs(phfrq_allq(imode, iq))<tol6) cycle
    1541              : 
    1542            0 :      phdispl1 = phdispl_allq(:,:,:,imode,iq)
    1543              : 
    1544              :      ! loop over configurations
    1545            0 :      do iconfig = 1, nconfig
    1546              : 
    1547              :        ! trick supercell object into using present q point
    1548            0 :        thm_scells(iconfig)%qphon(:) = qibz(:,iq)
    1549              : 
    1550              :        ! find thermal displacement amplitude eq 4 of Zacharias
    1551              :        !   combined with l_nu,q expression in paragraph before
    1552            0 :        if (phfrq_allq(imode, iq) > tol6) then
    1553            0 :          sigma = sqrt( (bose_einstein(phfrq_allq(imode,iq), temperature) + half)/phfrq_allq(imode,iq))
    1554              :        else
    1555              :          !Treat negative frequencies
    1556            0 :          select case (option)
    1557              :          case (1)
    1558              :            !Do not populate
    1559            0 :            sigma = 0._dp
    1560              :          case (2)
    1561              :            !Default amplitude for all the frequencies
    1562            0 :            sigma = 100._dp
    1563              :          case (3)
    1564              :            !Absolute value of the frequencies
    1565            0 :            sigma=sqrt((bose_einstein(abs(phfrq_allq(imode,iq)),temperature)+half) / abs(phfrq_allq(imode,iq)))
    1566              :          case (4)
    1567              :            sigma = 0._dp
    1568              :            !Search if the amplitude of this unstable phonon is in the input argument amplitudes
    1569            0 :            do iampl=1,namplitude
    1570              :              if(abs(thm_scells(iconfig)%qphon(1) - amplitudes(1,iampl)) < tol8.and.&
    1571              :                 abs(thm_scells(iconfig)%qphon(2) - amplitudes(2,iampl)) < tol8.and.&
    1572            0 :                 abs(thm_scells(iconfig)%qphon(3) - amplitudes(3,iampl)) < tol8.and.&
    1573            0 :                 abs(imode - amplitudes(4,iampl)) < tol8) then
    1574            0 :                sigma = amplitudes(5,iampl)
    1575              :              end if
    1576              :            end do
    1577              :            !If not, the amplitude is zero
    1578            0 :            if(abs(sigma) < tol8)then
    1579            0 :              write (msg, '(a,I0,a,3es12.5,2a,I0)') ' The amplitude of the unstable mode ',&
    1580            0 :                 int(imode),' of the qpt ',thm_scells(iconfig)%qphon(:), ch10,&
    1581            0 :                 'is set to zero for the configuration ',iconfig
    1582            0 :              ABI_WARNING(msg)
    1583              :            end if
    1584              :          end select
    1585              :        end if
    1586              : 
    1587              :        ! add displacement for this mode to supercell positions eq 5 of Zacharias
    1588            0 :        call RANDOM_NUMBER(rand)
    1589            0 :        rand = two * rand - one
    1590              : 
    1591              :        ! from TDEP documentation for gaussian distribution of displacements
    1592              :        !call RANDOM_NUMBER(rand1)
    1593              :        !call RANDOM_NUMBER(rand2)
    1594              :        ! rand = sqrt(-two*rand1) * sin(twopi*rand2)
    1595              : 
    1596              :        ! if (rand > half) then
    1597              :        !   rand = one
    1598              :        ! else
    1599              :        !   rand = -one
    1600              :        ! end if
    1601              : 
    1602            0 :        freeze_displ =  rand * sigma
    1603              : 
    1604            0 :        call thm_scells(iconfig)%freeze_displ(phdispl1(:,:,:), freeze_displ)
    1605              :      end do !iconfig
    1606              :    end do !imode
    1607              :  end do !iq
    1608              : 
    1609            0 :  ABI_FREE(phfrq_allq)
    1610            0 :  ABI_FREE(phdispl_allq)
    1611            0 :  ABI_FREE(phdispl1)
    1612            0 :  ABI_FREE(qibz)
    1613            0 :  ABI_FREE(qbz)
    1614            0 :  ABI_FREE(wtqibz)
    1615              : 
    1616            0 : end subroutine thermal_supercell_make
    1617              : !!***
    1618              : 
    1619              : !----------------------------------------------------------------------
    1620              : 
    1621              : !!****f* m_phonons/thermal_supercell_free
    1622              : !! NAME
    1623              : !! thermal_supercell_free
    1624              : !!
    1625              : !! FUNCTION
    1626              : !!  deallocate thermal array of supercells
    1627              : !!
    1628              : !! SOURCE
    1629              : 
    1630            1 : subroutine thermal_supercell_free(nscells, thm_scells)
    1631              : 
    1632              : !Arguments ------------------------------------
    1633              : !scalars
    1634              :  integer, intent(in) :: nscells
    1635              :  type(supercell_type), allocatable, intent(inout) :: thm_scells(:)
    1636              : 
    1637              : ! local
    1638              :  integer :: icell
    1639              : 
    1640            1 :  if (allocated(thm_scells)) then
    1641           11 :    do icell = 1, nscells
    1642           11 :      call thm_scells(icell)%free()
    1643              :    end do
    1644              :  end if
    1645              : 
    1646            1 : end subroutine thermal_supercell_free
    1647              : !!***
    1648              : 
    1649              : !----------------------------------------------------------------------
    1650              : 
    1651              : !!****f* m_phonons/zacharias_supercell_print
    1652              : !! NAME
    1653              : !! zacharias_supercell_print
    1654              : !!
    1655              : !! FUNCTION
    1656              : !!  print files with thermal array of supercells
    1657              : !!
    1658              : !! INPUTS
    1659              : !!
    1660              : !! OUTPUT
    1661              : !!
    1662              : !! SOURCE
    1663              : 
    1664            1 : subroutine zacharias_supercell_print(fname, ntemper, tempermin, temperinc, thm_scells)
    1665              : 
    1666              : !Arguments ------------------------------------
    1667              : !scalars
    1668              :  integer, intent(in) :: ntemper
    1669              :  real(dp), intent(in) :: tempermin
    1670              :  real(dp), intent(in) :: temperinc
    1671              :  type(supercell_type), intent(in) :: thm_scells(ntemper)
    1672              :  character(len=fnlen), intent(in) :: fname
    1673              : 
    1674              : ! local
    1675              :  integer :: itemp
    1676              :  character(len=80) :: title1, title2
    1677              :  character(len=fnlen) :: filename
    1678              :  real(dp) :: temper
    1679              :  character(len=10) :: temper_str
    1680              : 
    1681           11 :  do itemp = 1, ntemper
    1682           10 :    temper = dble(itemp-1)*temperinc+tempermin
    1683           10 :    write(temper_str,'(I8)') int(temper)
    1684           10 :    write(filename, '(3a)') trim(fname), "_T_", trim(adjustl(temper_str))
    1685           10 :    write(title1, '(3a)') "#  Zacharias thermalized supercell at temperature T= ", trim(temper_str), " Kelvin"
    1686           10 :    title2 = "#  generated with alternating thermal displacements of all phonons"
    1687           11 :    call thm_scells(itemp)%print_abinit(filename, title1, title2)
    1688              :  end do
    1689              : 
    1690            1 : end subroutine zacharias_supercell_print
    1691              : !!***
    1692              : 
    1693              : !!****f* m_phonons/thermal_supercell_print
    1694              : !! NAME
    1695              : !! thermal_supercell_print
    1696              : !!
    1697              : !! FUNCTION
    1698              : !!  print files with thermalized array of random supercell configurations
    1699              : !!
    1700              : !! INPUTS
    1701              : !!
    1702              : !! OUTPUT
    1703              : !!
    1704              : !! SOURCE
    1705              : 
    1706            0 : subroutine thermal_supercell_print(fname, nconfig, temperature_K, thm_scells)
    1707              : 
    1708              : !Arguments ------------------------------------
    1709              : !scalars
    1710              :  integer, intent(in) :: nconfig
    1711              :  type(supercell_type), intent(in) :: thm_scells(nconfig)
    1712              :  character(len=fnlen), intent(in) :: fname
    1713              :  real(dp), intent(in) :: temperature_K
    1714              : 
    1715              : ! local
    1716              :  integer :: iconfig,itemp
    1717              :  character(len=80) :: title1, title2
    1718              :  character(len=fnlen) :: filename
    1719              :  character(len=10) :: config_str
    1720              : 
    1721            0 :  do iconfig = 1, nconfig
    1722            0 :    write(config_str,'(I8)') iconfig
    1723            0 :    write(filename, '(3a)') trim(fname), "_cf_", trim(adjustl(config_str))
    1724            0 :    write(title1, '(a,I6,a)') "#  thermalized supercell at temperature T= ", temperature_K, " Kelvin"
    1725            0 :    title2 = "#  generated with random thermal displacements of all phonons"
    1726            0 :    call thm_scells(itemp)%print_abinit(filename, title1, title2)
    1727              :  end do
    1728              : 
    1729            0 : end subroutine thermal_supercell_print
    1730              : !!***
    1731              : 
    1732              : !----------------------------------------------------------------------
    1733              : 
    1734              : !!****f* m_phonons/phdos_ncwrite
    1735              : !! NAME
    1736              : !! phdos_ncwrite
    1737              : !!
    1738              : !! FUNCTION
    1739              : !!  Save the content of the object in a netcdf file.
    1740              : !!
    1741              : !! INPUTS
    1742              : !!  ncid=NC file handle (open in the caller)
    1743              : !!  phdos<phdos_t>=Container object
    1744              : !!
    1745              : !! OUTPUT
    1746              : !!  Only writing
    1747              : !!
    1748              : !! NOTES
    1749              : !!  Frequencies are in eV, DOS are in states/eV.
    1750              : !!
    1751              : !! SOURCE
    1752              : 
    1753           63 : subroutine phdos_ncwrite(phdos, ncid)
    1754              : 
    1755              : !Arguments ------------------------------------
    1756              : !scalars
    1757              :  class(phdos_t),intent(in) :: phdos
    1758              :  integer,intent(in) :: ncid
    1759              : 
    1760              : !Local variables-------------------------------
    1761              :  integer :: ncerr
    1762              : ! *************************************************************************
    1763              : 
    1764              : ! Define dimensions
    1765           63 :  NCF_CHECK(nctk_def_basedims(ncid, defmode=.True.))
    1766              : 
    1767              :  ncerr = nctk_def_dims(ncid, [nctkdim_t("number_of_atoms", phdos%natom),&
    1768              :    nctkdim_t("number_of_atom_species", phdos%ntypat), nctkdim_t("number_of_frequencies", phdos%nomega), &
    1769          378 :    nctkdim_t("nqibz", phdos%nqibz), nctkdim_t("number_of_normal_vec_dmm", phdos%n_normal_vec_dmm)])
    1770           63 :  NCF_CHECK(ncerr)
    1771              : 
    1772              : !scalars
    1773          126 :  NCF_CHECK(nctk_def_iscalars(ncid, ["prtdos"]))
    1774          126 :  NCF_CHECK(nctk_def_dpscalars(ncid, ["dossmear"]))
    1775              : 
    1776              : !arrays
    1777              :  ncerr = nctk_def_arrays(ncid, [&
    1778              :    nctkarr_t('wmesh', "dp", 'number_of_frequencies'),&
    1779              :    nctkarr_t('phdos', "dp", 'number_of_frequencies'),&
    1780              :    nctkarr_t('phdos_dmm', "dp", 'number_of_frequencies, number_of_normal_vec_dmm'),&
    1781              :    nctkarr_t('normal_vec_dmm', "dp", 'three, number_of_normal_vec_dmm'),&
    1782              :    nctkarr_t('pjdos', "dp", 'number_of_frequencies, three, number_of_atoms'),&
    1783              :    nctkarr_t('pjdos_type', "dp", 'number_of_frequencies, number_of_atom_species'),&
    1784              :    nctkarr_t('pjdos_rc_type', "dp", 'number_of_frequencies, three, number_of_atom_species'), &
    1785              :    nctkarr_t('msqd_dos_atom', "dp", 'number_of_frequencies, three, three, number_of_atoms'), &
    1786              :    nctkarr_t('qptrlatt', "int", 'three, three'), &
    1787              :    nctkarr_t('shiftq', "dp", 'three') &
    1788          693 :  ])
    1789           63 :  NCF_CHECK(ncerr)
    1790              : 
    1791              :  ! Write variables. Note unit conversion.
    1792           63 :  NCF_CHECK(nctk_set_datamode(ncid))
    1793           63 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, "prtdos"), phdos%prtdos))
    1794           63 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'dossmear'), phdos%dossmear*Ha_eV))
    1795        85905 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'wmesh'), phdos%omega*Ha_eV))
    1796           63 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'normal_vec_dmm'), phdos%normal_vec_dmm))
    1797        85905 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'phdos'), phdos%phdos/Ha_eV))
    1798       601398 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'phdos_dmm'), phdos%phdos_dmm/Ha_eV)) ! TODO: these should also be converted for the velocity
    1799       592900 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'pjdos'), phdos%pjdos/Ha_eV))
    1800       122611 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'pjdos_type'), phdos%pjdos_type/Ha_eV))
    1801       367802 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'pjdos_rc_type'), phdos%pjdos_rc_type/Ha_eV))
    1802      1778695 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'msqd_dos_atom'), phdos%msqd_dos_atom/Ha_eV))
    1803           63 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'qptrlatt'), phdos%qptrlatt))
    1804           63 :  NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, 'shiftq'), phdos%shiftq))
    1805              : 
    1806           63 : end subroutine phdos_ncwrite
    1807              : !!***
    1808              : 
    1809              : !----------------------------------------------------------------------
    1810              : 
    1811              : !!****f* m_phonons/mkphbs
    1812              : !! NAME
    1813              : !! mkphbs
    1814              : !!
    1815              : !! FUNCTION
    1816              : !! Function to calculate the phonon band structure, from the IFC
    1817              : !!
    1818              : !! INPUTS
    1819              : !! Ifc<ifc_type>=Interatomic force constants
    1820              : !! crystal<type(crystal_t)> = Info on the crystalline structure.
    1821              : !! inp= (derived datatype) contains all the input variables
    1822              : !! ddb<type(ddb_type)>=Object storing the DDB results.
    1823              : !! asrq0<asrq0_t>=Object for the treatment of the ASR based on the q=0 block found in the DDB file.
    1824              : !! prefix=Prefix for output files.
    1825              : !! dielt(3,3)=dielectric tensor
    1826              : !! comm=MPI communicator
    1827              : !!
    1828              : !! OUTPUT
    1829              : !!  Only writing.
    1830              : !!
    1831              : !! SOURCE
    1832              : 
    1833           65 : subroutine mkphbs(Ifc,Crystal,inp,ddb,asrq0,prefix,comm)
    1834              : 
    1835              : !Arguments -------------------------------
    1836              : !scalars
    1837              :  integer,intent(in) :: comm
    1838              :  character(len=*),intent(in) :: prefix
    1839              :  type(ifc_type),intent(in) :: Ifc
    1840              :  type(crystal_t),intent(in) :: Crystal
    1841              :  type(anaddb_dataset_type),target,intent(in) :: inp
    1842              :  type(ddb_type),intent(in) :: ddb
    1843              :  type(asrq0_t),intent(inout) :: asrq0
    1844              : !Local variables -------------------------
    1845              : !scalars
    1846              :  integer,parameter :: master=0
    1847              :  integer :: unt, iphl1,iblok,rftyp, ii,nfineqpath,nsym,natom,ncid,nprocs,my_rank
    1848              :  integer :: natprj_bs,eivec,enunit,ifcflag,ptgroupma,spgroup
    1849              :  real(dp) :: freeze_displ, cfact, omega, omega_min, gaussmaxarg, gaussfactor, gaussprefactor, xx, eta
    1850              :  character(500) :: msg
    1851              :  character(len=8) :: unitname
    1852              : !arrays
    1853              :  integer :: bravais(11),rfphon(4),rfelfd(4),rfstrs(4), units(2)
    1854              :  integer :: nomega, imode, iomega
    1855           65 :  integer,allocatable :: ndiv(:)
    1856              :  real(dp) :: speedofsound(3),genafm(3)
    1857              :  real(dp) :: qphnrm(3), qphon(3), qphon_padded(3,3),res(3)
    1858          130 :  real(dp) :: d2cart(2,ddb%msize),real_qphon(3)
    1859          130 :  real(dp) :: displ(2*3*Crystal%natom*3*Crystal%natom),eigval(3,Crystal%natom),phangmom(3,3*Crystal%natom)
    1860           65 :  real(dp),allocatable :: phfrq(:),eigvec(:,:,:,:,:)
    1861           65 :  real(dp),allocatable :: save_phfrq(:,:),save_phdispl_cart(:,:,:,:),save_qpoints(:,:),save_phangmom(:,:,:)
    1862           65 :  real(dp),allocatable :: weights(:), dos4bs(:)
    1863           65 :  real(dp),allocatable,target :: alloc_path(:,:)
    1864           65 :  real(dp),pointer :: fineqpath(:,:)
    1865           65 :  type(atprj_type) :: atprj
    1866              : ! *********************************************************************
    1867              : 
    1868              :  ! Only master works for the time being
    1869           65 :  nprocs = xmpi_comm_size(comm); my_rank = xmpi_comm_rank(comm)
    1870           65 :  if (my_rank /= master) return
    1871              : 
    1872          195 :  units = [std_out, ab_out]
    1873           65 :  nsym = Crystal%nsym; natom = Crystal%natom
    1874              : 
    1875              :  ! Copy parameters from inp (then I will try to remove inp from the API so that I can call mkphbs in eph)
    1876           65 :  ifcflag = inp%ifcflag
    1877           65 :  natprj_bs = inp%natprj_bs
    1878           65 :  freeze_displ = inp%freeze_displ
    1879           65 :  eivec = inp%eivec; enunit = inp%enunit
    1880           65 :  rftyp=inp%rfmeth
    1881              : 
    1882           65 :  nullify(fineqpath)
    1883           65 :  nfineqpath = inp%nph1l
    1884           65 :  fineqpath => inp%qph1l
    1885              : 
    1886           65 :  if(inp%nph1l==0) then
    1887            8 :    if (inp%nqpath==0) then
    1888              :      return ! if there is nothing to do, return
    1889              :    else
    1890              :      ! allow override of nph1l with nqpath if the former is not set
    1891              :      ! allocate and compute path here and make fineqpath points to it
    1892           24 :      ABI_MALLOC(ndiv,(inp%nqpath-1))
    1893            8 :      call make_path(inp%nqpath,inp%qpath,Crystal%gmet,'G',inp%ndivsm,ndiv,nfineqpath,alloc_path,std_out)
    1894            8 :      ABI_FREE(ndiv)
    1895            8 :      fineqpath => alloc_path
    1896              :    end if
    1897              :  end if
    1898              : 
    1899         5265 :  write(msg, '(a,(80a),a,a,a,a)' ) ch10,('=',ii=1,80),ch10,ch10,' Treat the first list of vectors ',ch10
    1900           65 :  call wrtout(units, msg)
    1901              : 
    1902           65 :  if (natprj_bs > 0) call atprj%init(natom, natprj_bs, inp%iatprj_bs, prefix)
    1903              : 
    1904          260 :  ABI_MALLOC(phfrq, (3*natom))
    1905          325 :  ABI_MALLOC(eigvec, (2,3,natom,3,natom))
    1906          195 :  ABI_MALLOC(save_qpoints, (3,nfineqpath))
    1907          260 :  ABI_MALLOC(save_phfrq, (3*natom,nfineqpath))
    1908          325 :  ABI_MALLOC(save_phdispl_cart, (2,3*natom,3*natom,nfineqpath))
    1909          260 :  ABI_MALLOC(save_phangmom, (3,3*natom,nfineqpath))
    1910          260 :  qphnrm = one
    1911              : 
    1912         1309 :  do iphl1=1,nfineqpath
    1913              : 
    1914              :    ! Initialisation of the phonon wavevector
    1915         4976 :    qphon(:)=fineqpath(:,iphl1)
    1916              : 
    1917         1244 :    if (inp%nph1l /= 0) qphnrm(1) = inp%qnrml1(iphl1)
    1918              : 
    1919         4976 :    save_qpoints(:,iphl1) = qphon / qphnrm(1)
    1920              : 
    1921              :    ! Generation of the dynamical matrix in cartesian coordinates
    1922         1244 :    if (ifcflag == 1) then
    1923              : 
    1924              :      ! Get phonon frequencies and displacements in reduced coordinates for this q-point
    1925              :      !call ifc%fourq(cryst, save_qpoints(:,iphl1), phfrq, displ, out_eigvec=eigvec)
    1926              : 
    1927              :      ! Get d2cart using the interatomic forces and the
    1928              :      ! long-range coulomb interaction through Ewald summation
    1929              :      call gtdyn9(Ifc%acell,Ifc%atmfrc,Ifc%dielt,Ifc%dipdip,Ifc%dyewq0,d2cart,Crystal%gmet,Ifc%gprim,Ifc%mpert,natom, &
    1930              :       Ifc%nrpt,qphnrm(1),qphon,Crystal%rmet,Ifc%rprim,Ifc%rpt,Ifc%trans,Crystal%ucvol,Ifc%wghatm,Crystal%xred,ifc%zeff,&
    1931              :       ifc%qdrp_cart,ifc%ewald_option,eta,xmpi_comm_self,Ifc%sys_dim,dipquad=Ifc%dipquad,quadquad=Ifc%quadquad,&
    1932         1202 :       dielt_env=Ifc%dielt_env,dielt_thick=Ifc%dielt_thick)
    1933         1202 :      if (asrq0%asr==6) then
    1934          644 :        qphon_padded = zero; qphon_padded(:,1) = qphon(:)
    1935          161 :        call asrq0%apply(natom, ddb%mpert, ddb%msize, qphon_padded, Crystal, d2cart)
    1936              :      end if
    1937           42 :    else if (ifcflag == 0) then
    1938              : 
    1939              :      !call ddb_diagoq(ddb, crystal, save_qpoints(:,iphl1), asrq0, ifc%symdynmat, rftyp, phfrq, displ, &
    1940              :      !                out_eigvec=eigvec)
    1941              : 
    1942              :      ! Look for the information in the DDB (no interpolation here!)
    1943          294 :      rfphon(1:2)=1; rfelfd(1:2)=0; rfstrs(1:2)=0
    1944          168 :      qphon_padded = zero; qphon_padded(:,1) = qphon
    1945              : 
    1946           42 :      call ddb%get_block(iblok,qphon_padded,qphnrm,rfphon,rfelfd,rfstrs,rftyp)
    1947              : 
    1948              :      ! Copy the dynamical matrix in d2cart
    1949      1159908 :      d2cart(:,1:ddb%msize)=ddb%val(:,:,iblok)
    1950              : 
    1951              :      ! Eventually impose the acoustic sum rule based on previously calculated d2asr
    1952           42 :      call asrq0%apply(natom, ddb%mpert, ddb%msize, qphon_padded, Crystal, d2cart)
    1953              :    end if
    1954              : 
    1955              :    ! Use inp%symdynmat instead of ifc because of ifcflag
    1956              :    ! Calculation of the eigenvectors and eigenvalues of the dynamical matrix
    1957              :    call dfpt_phfrq(ddb%amu,displ,d2cart,eigval,eigvec,Crystal%indsym,&
    1958              :                    ddb%mpert,Crystal%nsym,natom,nsym,Crystal%ntypat,phfrq,qphnrm(1),qphon,&
    1959         1244 :                    crystal%rprimd,inp%symdynmat,Crystal%symrel,Crystal%symafm,Crystal%typat,Crystal%ucvol)
    1960              : 
    1961              :    ! Calculation of the phonon angular momentum
    1962              :    ! maybe add it in dpft_phfrq directly ?
    1963         1244 :    call phangmom_from_eigvec(natom, eigvec, phangmom)
    1964              : 
    1965         1244 :    if (abs(freeze_displ) > tol10) then
    1966            6 :      real_qphon = zero
    1967           24 :      if (abs(qphnrm(1)) > tol8) real_qphon = qphon / qphnrm(1)
    1968              :      call freeze_displ_allmodes(displ, freeze_displ, natom, prefix, phfrq, &
    1969            6 :                                 real_qphon, crystal%rprimd, Crystal%typat, crystal%xcart, crystal%znucl)
    1970              :    end if
    1971              : 
    1972              :    ! If requested, output projection of each mode on given atoms
    1973         1244 :    if (natprj_bs > 0) call atprj%print(iphl1, phfrq, eigvec)
    1974              : 
    1975              :    ! In case eivec == 4, write output files for band2eps (visualization of phonon band structures)
    1976         1244 :    if (eivec == 4) call sortph(eigvec,displ,strcat(prefix, "_B2EPS"),natom,phfrq)
    1977              : 
    1978              :    ! Write the phonon frequencies
    1979         1244 :    call dfpt_prtph(displ,eivec,enunit,ab_out,natom,phfrq,qphnrm(1),qphon)
    1980              : 
    1981        14621 :    save_phfrq(:,iphl1) = phfrq
    1982         4976 :    save_phdispl_cart(:,:,:,iphl1) = RESHAPE(displ, [2, 3*natom, 3*natom])
    1983         3732 :    save_phangmom(:,:,iphl1) = RESHAPE(phangmom, [3, 3*natom])
    1984              : 
    1985              :    ! Determine the symmetries of the phonon mode at Gamma
    1986              :    ! TODO: generalize for other q-point little groups.
    1987         4976 :    if (sum(abs(qphon)) < DDB_QTOL) then
    1988              :      call symanal(bravais,0,genafm,nsym,nsym,ptgroupma,Crystal%rprimd,spgroup, &
    1989           72 :                   Crystal%symafm,Crystal%symrel,Crystal%tnons,tol5,verbose=.TRUE.)
    1990           72 :      call dfpt_symph(ab_out,ddb%acell,eigvec,Crystal%indsym,natom,nsym,phfrq,ddb%rprim,Crystal%symrel)
    1991              :    end if
    1992              : 
    1993              :    ! if we have an acoustic mode (small q and acoustic type displacements)
    1994              :    ! extrapolate speed of sound in this direction, and Debye frequency
    1995         4976 :    call wrap2_pmhalf(qphon, real_qphon, res)
    1996          311 :    if (sqrt(real_qphon(1)**2+real_qphon(2)**2+real_qphon(3)**2) < quarter .and. &
    1997         1309 :        sqrt(real_qphon(1)**2+real_qphon(2)**2+real_qphon(3)**2) > tol6) then
    1998          237 :      call phdos_calc_vsound(eigvec, Crystal%gmet, natom, phfrq, real_qphon, speedofsound)
    1999          237 :      if (my_rank == master) call phdos_print_vsound(ab_out, Crystal%ucvol, speedofsound)
    2000              :    end if
    2001              : 
    2002              :  end do ! iphl1
    2003              : 
    2004              :  ! calculate dos for the specific q points along the BS calculated. only Gaussians are possible - no interpolation
    2005        14686 :  omega_min = minval(save_phfrq(:,:))
    2006        14686 :  nomega=NINT( (maxval(save_phfrq(:,:))-omega_min) / inp%dosdeltae ) + 1
    2007           65 :  nomega=MAX(6,nomega) ! Ensure Simpson integration will be ok
    2008              : 
    2009       249018 :  ABI_CALLOC(dos4bs,(nomega))
    2010              : 
    2011           65 :  gaussmaxarg = sqrt(-log(1.d-90))
    2012           65 :  gaussprefactor = one/(inp%dossmear*sqrt(two_pi))
    2013           65 :  gaussfactor    = one/(sqrt2*inp%dossmear)
    2014         1309 :  do iphl1=1,nfineqpath
    2015        14686 :    do imode=1,3*natom
    2016     55778618 :      do iomega=1, nomega
    2017     55763997 :        omega = omega_min + (iomega-1) * inp%dosdeltae
    2018     55763997 :        xx = (omega - save_phfrq(imode,iphl1)) * gaussfactor
    2019     55777374 :        if(abs(xx) < gaussmaxarg) dos4bs(iomega) = dos4bs(iomega) + gaussprefactor*exp(-xx*xx)
    2020              :      end do
    2021              :    end do
    2022              :  end do
    2023              : 
    2024              :  !deallocate sortph array
    2025           65 :  call end_sortph()
    2026              : 
    2027           65 :  if (natprj_bs > 0) call atprj%free()
    2028              : 
    2029              : ! WRITE OUT FILES
    2030              :  if (my_rank == master) then
    2031          195 :    ABI_MALLOC(weights, (nfineqpath))
    2032         1309 :    weights = one
    2033           65 :    NCF_CHECK_MSG(nctk_open_create(ncid, strcat(prefix, "_PHBST.nc"), xmpi_comm_self), "Creating PHBST")
    2034           65 :    NCF_CHECK(crystal%ncwrite(ncid))
    2035           65 :    call phonons_ncwrite(ncid,natom,nfineqpath,save_qpoints,weights,save_phfrq,save_phdispl_cart,save_phangmom)
    2036              :    ! Now treat the second list of vectors (only at the Gamma point, but can include non-analyticities)
    2037           65 :    if (inp%nph2l /= 0 .and. inp%ifcflag == 1) then
    2038           14 :      call ifc%calcnwrite_nana_terms(crystal, inp%nph2l, inp%qph2l, inp%qnrml2, ncid)
    2039              :    end if
    2040           65 :    NCF_CHECK(nf90_close(ncid))
    2041              : 
    2042           65 :    call phonons_write_phfrq(prefix, natom,nfineqpath,save_qpoints,weights,save_phfrq,save_phdispl_cart, save_phangmom)
    2043              : 
    2044           65 :    select case (inp%prtphbands)
    2045              :    case (0)
    2046           65 :      continue
    2047              : 
    2048              :    case (1)
    2049           65 :      if (inp%nph1l == 0) then
    2050              :        call phonons_write_xmgrace(strcat(prefix, "_PHBANDS.agr"), natom, nfineqpath, save_qpoints, save_phfrq, &
    2051            8 :           qptbounds=inp%qpath)
    2052              :      else
    2053           57 :        call phonons_write_xmgrace(strcat(prefix, "_PHBANDS.agr"), natom, nfineqpath, save_qpoints, save_phfrq)
    2054              :      end if
    2055              : 
    2056              :    case (2)
    2057            0 :      if (inp%nph1l == 0) then
    2058            0 :        call phonons_write_gnuplot(prefix, natom, nfineqpath, save_qpoints, save_phfrq, qptbounds=inp%qpath)
    2059              :      else
    2060            0 :        call phonons_write_gnuplot(prefix, natom, nfineqpath, save_qpoints, save_phfrq)
    2061              :      end if
    2062              : 
    2063              :    case default
    2064           65 :      ABI_WARNING(sjoin("Don't know how to handle prtphbands:", itoa(inp%prtphbands)))
    2065              :    end select
    2066              : 
    2067              :    ! write out DOS file for q along this path
    2068           65 :    cfact=one
    2069           65 :    unitname = 'Ha'
    2070           65 :    if (open_file('PHBST_partial_DOS',msg,newunit=unt,form="formatted",action="write") /= 0) then
    2071            0 :      ABI_ERROR(msg)
    2072              :    end if
    2073           65 :    write(msg,'(3a)')'# ',ch10,'# Partial phonon density of states for q along a band structure path'
    2074           65 :    call wrtout(unt, msg)
    2075           65 :    write(msg,'(6a)')'# ',ch10,'# Energy in ',unitname,', DOS in states/',unitname
    2076           65 :    call wrtout(unt, msg)
    2077           65 :    write(msg,'(a,E20.10,2a,i8)') '# Gaussian method with smearing = ',inp%dossmear*cfact,unitname, ', nq =', nfineqpath
    2078           65 :    call wrtout(unt, msg)
    2079           65 :    write(msg,'(5a)')'# ',ch10,'# omega     PHDOS ',ch10,'# '
    2080           65 :    call wrtout(unt, msg)
    2081       248888 :    do iomega=1,nomega
    2082       248823 :      omega = omega_min + (iomega-1) * inp%dosdeltae
    2083       248823 :      write(unt,'(2es17.8)',advance='NO')omega*cfact,dos4bs(iomega)/cfact
    2084       248888 :      write(unt,*)
    2085              :    end do
    2086           65 :    close(unt)
    2087              : 
    2088           65 :    ABI_FREE(weights)
    2089              :  end if
    2090              : 
    2091           65 :  ABI_FREE(save_qpoints)
    2092           65 :  ABI_FREE(save_phfrq)
    2093           65 :  ABI_FREE(save_phdispl_cart)
    2094           65 :  ABI_FREE(save_phangmom)
    2095           65 :  ABI_FREE(phfrq)
    2096           65 :  ABI_FREE(eigvec)
    2097           65 :  ABI_FREE(dos4bs)
    2098           65 :  ABI_SFREE(alloc_path)
    2099              : 
    2100          130 : end subroutine mkphbs
    2101              : !!***
    2102              : 
    2103              : !!****f* m_phonons/phdos_calc_vsound
    2104              : !!
    2105              : !! NAME
    2106              : !! phdos_calc_vsound
    2107              : !!
    2108              : !! FUNCTION
    2109              : !!  From the frequencies for acoustic modes at small q, estimate speed of sound (which also gives Debye temperature)
    2110              : !!
    2111              : !! INPUTS
    2112              : !! eigvec(2,3*natom,3*natom) = phonon eigenvectors at present q-point
    2113              : !! gmet(3,3) = metric tensor in reciprocal space.
    2114              : !! natom = number of atoms in the unit cell
    2115              : !! phfrq(3*natom) = phonon frequencies at present q-point
    2116              : !! qphon(3) = phonon q-point
    2117              : !! ucvol = unit cell volume
    2118              : !!
    2119              : !! OUTPUT
    2120              : !!
    2121              : !! SOURCE
    2122              : 
    2123        17374 : subroutine phdos_calc_vsound(eigvec, gmet, natom, phfrq, qphon, speedofsound)
    2124              : 
    2125              : !Arguments -------------------------------
    2126              : !scalars
    2127              :  integer, intent(in) :: natom
    2128              : !arrays
    2129              :  real(dp), intent(in) :: gmet(3,3),qphon(3),phfrq(3*natom),eigvec(2,3*natom,3*natom)
    2130              :  real(dp), intent(out) :: speedofsound(3)
    2131              : 
    2132              : !Local variables -------------------------
    2133              :  integer :: iatref,imode, iatom, isacoustic, imode_acoustic
    2134              : ! character(len=500) :: msg
    2135              :  real(dp) :: qnormcart
    2136              :  real(dp) :: qtmp(3)
    2137              : ! *********************************************************************
    2138              : 
    2139        17374 :  imode_acoustic = 0
    2140              : 
    2141       123013 :  do imode = 1, 3*natom
    2142              :    ! Check if this mode is acoustic like: scalar product of all displacement vectors are collinear
    2143       423768 :    isacoustic = 1
    2144              :    ! Find reference atom with non-zero displacement
    2145       423768 :    do iatom=1,natom
    2146      3286929 :      if(sum(eigvec(:,(iatom-1)*3+1:(iatom-1)*3+3,imode)**2) >tol16)iatref=iatom
    2147              :    enddo
    2148              :    ! Now compute scalar product, and check they are all positive
    2149       423768 :    do iatom = 1, natom
    2150      3181290 :      if (sum(eigvec(:,(iatom-1)*3+1:(iatom-1)*3+3, imode)&
    2151       211289 :             *eigvec(:,(iatref-1)*3+1:(iatref-1)*3+3, imode)) < tol16 ) isacoustic = 0
    2152              :    end do
    2153       105639 :    if (isacoustic == 0) cycle
    2154        52203 :    imode_acoustic = min(imode_acoustic + 1, 3)
    2155              : 
    2156              :    ! write (msg, '(a,I6,a,3F12.4)') ' Found acoustic mode ', imode, ' for |q| in red coord < 0.25 ; q = ', qphon
    2157              :    ! call wrtout(std_out, msg)
    2158       678639 :    qtmp = matmul(gmet, qphon)
    2159       208812 :    qnormcart = two * pi * sqrt(sum(qphon*qtmp))
    2160       123013 :    speedofsound(imode_acoustic) = phfrq(imode) / qnormcart
    2161              :  end do
    2162              : 
    2163        17374 : end subroutine phdos_calc_vsound
    2164              : !!***
    2165              : 
    2166              : !!****f* m_phonons/phdos_print_vsound
    2167              : !!
    2168              : !! NAME
    2169              : !! phdos_print_vsound
    2170              : !!
    2171              : !! FUNCTION
    2172              : !!  Print out estimate speed of sound and Debye temperature at this (small) q
    2173              : !!  should only be called by master proc for the hard unit number
    2174              : !!
    2175              : !! INPUTS
    2176              : !! unit=Fortran unit number
    2177              : !! speedofsound(3)
    2178              : !!
    2179              : !! OUTPUT
    2180              : !!  Only writing
    2181              : !!
    2182              : !! SOURCE
    2183              : 
    2184          237 : subroutine phdos_print_vsound(iunit, ucvol, speedofsound)
    2185              : 
    2186              : !Arguments -------------------------------
    2187              : !scalras
    2188              :  integer, intent(in) :: iunit
    2189              :  real(dp), intent(in) :: ucvol
    2190              : !arrays
    2191              :  real(dp), intent(in) :: speedofsound(3)
    2192              : 
    2193              : !Local variables -------------------------
    2194              :  integer :: imode_acoustic, units(2)
    2195              :  character(len=500) :: msg
    2196              :  real(dp) :: tdebye
    2197              : ! *********************************************************************
    2198              : 
    2199          711 :  units = [std_out, iunit]
    2200              : 
    2201          948 :  do imode_acoustic = 1, 3
    2202              :    ! from phonon frequency, estimate speed of sound by linear interpolation from Gamma
    2203              :    write (msg, '(2a,a,E20.10,a,a,F20.5)') &
    2204          711 :     ' Speed of sound for this q and mode:',ch10,&
    2205          711 :     '   in atomic units: ', speedofsound(imode_acoustic), ch10,&
    2206         1422 :     '   in units km/s: ', speedofsound(imode_acoustic) * Bohr_Ang * 1.d-13 / Time_Sec
    2207          711 :    call wrtout(units, msg)
    2208              : 
    2209              :    ! also estimate partial Debye temperature, = energy if this band went to zone edge
    2210          711 :    tdebye = speedofsound(imode_acoustic) * pi * (six / pi / ucvol)**(third)
    2211              :    write (msg, '(2a,a,E20.10,a,a,F20.5)') &
    2212          711 :     ' Partial Debye temperature for this q and mode:',ch10,&
    2213          711 :     '   in atomic units: ', tdebye, ch10,&
    2214         1422 :     '   in SI units K  : ', tdebye * Ha_K
    2215          711 :    call wrtout(units, msg)
    2216          948 :    call wrtout(units, "")
    2217              :  end do
    2218              : 
    2219          237 : end subroutine phdos_print_vsound
    2220              : !!***
    2221              : 
    2222              : !----------------------------------------------------------------------
    2223              : 
    2224              : !!****f* m_phonons/phdos_print_msqd
    2225              : !!
    2226              : !! NAME
    2227              : !! phdos_print_msqd
    2228              : !!
    2229              : !! FUNCTION
    2230              : !!  Print out mean square displacement and velocity for each atom (trace and full matrix) as a function of T
    2231              : !!  see for example https://atztogo.github.io/phonopy/thermal-displacement.html#thermal-displacement
    2232              : !!  Only master node should call this routine.
    2233              : !!
    2234              : !! INPUTS
    2235              : !!   PHdos structure
    2236              : !!
    2237              : !! OUTPUT
    2238              : !!   to file only
    2239              : !!
    2240              : !! SOURCE
    2241              : 
    2242           11 : subroutine phdos_print_msqd(PHdos, fname, ntemper, tempermin, temperinc)
    2243              : 
    2244              : !Arguments -------------------------------
    2245              : !scalars
    2246              :  class(phdos_t),intent(in) :: PHdos
    2247              :  character(len=*),intent(in) :: fname
    2248              :  integer, intent(in) :: ntemper
    2249              :  real(dp), intent(in) :: tempermin, temperinc
    2250              : 
    2251              : !Local variables -------------------------
    2252              :  integer :: io, iomin, itemp, iunit, junit, iatom
    2253              :  real(dp) :: temper
    2254              :  character(len=500) :: msg
    2255              :  character(len=fnlen) :: fname_msqd, fname_veloc
    2256              : !arrays
    2257           11 :  real(dp), allocatable :: bose_msqd(:,:), tmp_msqd(:,:), integ_msqd(:,:)
    2258           11 :  real(dp), allocatable :: bose_msqv(:,:), tmp_msqv(:,:), integ_msqv(:,:)
    2259              : ! *********************************************************************
    2260              : 
    2261           11 :  fname_msqd = trim(fname) //"_MSQD_T"
    2262           11 :  if (open_file(fname_msqd, msg, newunit=iunit, form="formatted", status="unknown", action="write") /= 0) then
    2263            0 :    ABI_ERROR(msg)
    2264              :  end if
    2265           11 :  fname_veloc = trim(fname) // "_MSQV_T"
    2266           11 :  if (open_file(fname_veloc, msg, newunit=junit, form="formatted", status="unknown", action="write") /= 0) then
    2267            0 :    ABI_ERROR(msg)
    2268              :  end if
    2269              : 
    2270              :  ! write the header
    2271           11 :  write (iunit, '(a)') '# mean square displacement for each atom as a function of T (bohr^2)'
    2272           11 :  write (junit, '(a)') "# mean square velocity for each atom as a function of T (bohr^2/atomic time unit^2)"
    2273              : 
    2274           11 :  write (msg, '(a,F18.10,a,F18.10,a)') '#  T in Kelvin, from ', tempermin, ' to ', tempermin+(ntemper-1)*temperinc
    2275           11 :  write (iunit, '(a)') trim(msg)
    2276           11 :  write (junit, '(a)') trim(msg)
    2277              : 
    2278           11 :  write (msg, '(2a)') '#    T             |u^2|                u_xx                u_yy                u_zz',&
    2279           22 :                      '                u_yz                u_xz                u_xy in bohr^2'
    2280           11 :  write (iunit, '(a)') trim(msg)
    2281           11 :  write (msg, '(3a)') '#    T             |v^2|                v_xx                v_yy                v_zz',&
    2282           11 :                      '                v_yz                v_xz                v_xy',&
    2283           22 :                      ' in bohr^2/atomic time unit^2'
    2284           11 :  write (junit, '(a)') trim(msg)
    2285              : 
    2286           33 :  ABI_MALLOC(tmp_msqd, (PHdos%nomega,9))
    2287           22 :  ABI_MALLOC(tmp_msqv, (PHdos%nomega,9))
    2288           33 :  ABI_MALLOC(integ_msqd, (9,ntemper))
    2289           22 :  ABI_MALLOC(integ_msqv, (9,ntemper))
    2290           44 :  ABI_MALLOC(bose_msqd, (PHdos%nomega, ntemper))
    2291           33 :  ABI_MALLOC(bose_msqv, (PHdos%nomega, ntemper))
    2292              : 
    2293        10455 :  do io=1, PHdos%nomega
    2294        10455 :    if ( PHdos%omega(io) >= 2._dp * 4.56d-6 ) exit ! 2 cm-1 TODO: make this an input parameter
    2295              :  end do
    2296              :  iomin = io
    2297              : 
    2298              :  ! calculate bose only once for each atom (instead of for each atom)
    2299       407961 :  bose_msqd = zero
    2300       407961 :  bose_msqv = zero
    2301          121 :  do itemp = 1, ntemper
    2302          110 :    temper = tempermin + (itemp-1) * temperinc
    2303          110 :    if (temper < 1.e-3) cycle ! millikelvin at least to avoid exploding Bose factor(TM)
    2304       303521 :    do io = iomin, PHdos%nomega
    2305              :      ! NB: factors follow convention in phonopy documentation
    2306              :      ! the 1/sqrt(omega) factor in phonopy is contained in the displacement vector definition
    2307              :      ! bose() is dimensionless
    2308              :      !bose_msqd(io, itemp) =  (half + one  / ( exp(PHdos%omega(io)/(kb_HaK*temper)) - one )) / PHdos%omega(io)
    2309              :      !bose_msqv(io, itemp) =  (half + one  / ( exp(PHdos%omega(io)/(kb_HaK*temper)) - one )) * PHdos%omega(io)
    2310       303400 :      bose_msqd(io, itemp) =  (half + bose_einstein(PHdos%omega(io),kb_HaK*temper)) / PHdos%omega(io)
    2311       303510 :      bose_msqv(io, itemp) =  (half + bose_einstein(PHdos%omega(io),kb_HaK*temper)) * PHdos%omega(io)
    2312              :    end do
    2313              :  end do
    2314              : 
    2315           36 :  do iatom=1,PHdos%natom
    2316           25 :    write (msg, '(a,I8)') '# atom number ', iatom
    2317           25 :    write (iunit, '(a)') trim(msg)
    2318           25 :    write (junit, '(a)') trim(msg)
    2319              : 
    2320              :    ! for each T and each atom, integrate msqd matrix with Bose Einstein factor and output
    2321         2525 :    integ_msqd = zero
    2322           75 :    tmp_msqd = reshape(PHdos%msqd_dos_atom(:,:,:,iatom), (/PHdos%nomega, 9/))
    2323              : 
    2324              :    ! perform all integrations as matrix multiplication: integ_msqd (idir, itemp) = [tmp_msqd(io,idir)]^T  * bose_msqd(io,itemp)
    2325           25 :    call DGEMM('T','N', 9, ntemper, PHdos%nomega, one, tmp_msqd,PHdos%nomega, bose_msqd, PHdos%nomega, zero, integ_msqd, 9)
    2326              :    ! NB: this presumes an equidistant omega grid
    2327         2525 :    integ_msqd = integ_msqd * (PHdos%omega(2)-PHdos%omega(1)) / PHdos%atom_mass(iatom)
    2328              : 
    2329         2525 :    integ_msqv = zero
    2330           75 :    tmp_msqv = reshape(PHdos%msqd_dos_atom(:,:,:,iatom), (/PHdos%nomega, 9/))
    2331              : 
    2332              :    ! perform all integrations as matrix multiplication: integ_msqv (idir, itemp) = [tmp_msqv(io,idir)]^T  * bose_msqv(io,itemp)
    2333           25 :    call DGEMM('T','N', 9, ntemper, PHdos%nomega, one, tmp_msqv,PHdos%nomega, bose_msqv, PHdos%nomega, zero, integ_msqv, 9)
    2334              :    ! NB: this presumes an equidistant omega grid
    2335         2525 :    integ_msqv = integ_msqv * (PHdos%omega(2)-PHdos%omega(1)) / PHdos%atom_mass(iatom)
    2336              : 
    2337              :    ! print out stuff
    2338          275 :    do itemp = 1, ntemper
    2339          250 :      temper = tempermin + (itemp-1) * temperinc
    2340              :      write (msg, '(F10.2,4x,E22.10,2x,6E22.10)') &
    2341          250 :        temper, third*(integ_msqd(1,itemp)+integ_msqd(5,itemp)+integ_msqd(9,itemp)), &
    2342          250 :                       integ_msqd(1,itemp),integ_msqd(5,itemp),integ_msqd(9,itemp), &
    2343          500 :                       integ_msqd(6,itemp),integ_msqd(3,itemp),integ_msqd(2,itemp)
    2344          250 :      write (iunit, '(a)') trim(msg)
    2345              :      write (msg, '(F10.2,4x,E22.10,2x,6E22.10)') &
    2346          250 :        temper, third*(integ_msqv(1,itemp)+integ_msqv(5,itemp)+integ_msqv(9,itemp)), &
    2347          250 :                       integ_msqv(1,itemp),integ_msqv(5,itemp),integ_msqv(9,itemp), &
    2348          500 :                       integ_msqv(6,itemp),integ_msqv(3,itemp),integ_msqv(2,itemp)
    2349          275 :      write (junit, '(a)') trim(msg)
    2350              :    end do ! itemp
    2351              : 
    2352           25 :    write (iunit, '(a)') ''
    2353           36 :    write (junit, '(a)') ''
    2354              :  enddo ! iatom
    2355              : 
    2356           11 :  ABI_FREE(tmp_msqd)
    2357           11 :  ABI_FREE(tmp_msqv)
    2358           11 :  ABI_FREE(bose_msqd)
    2359           11 :  ABI_FREE(bose_msqv)
    2360           11 :  ABI_FREE(integ_msqd)
    2361           11 :  ABI_FREE(integ_msqv)
    2362              : 
    2363           11 :  close(iunit)
    2364           11 :  close(junit)
    2365              : 
    2366           11 : end subroutine phdos_print_msqd
    2367              : !!***
    2368              : 
    2369              : !----------------------------------------------------------------------
    2370              : 
    2371              : !!****f* m_phonons/phonons_ncwrite
    2372              : !! NAME
    2373              : !! phonons_ncwrite
    2374              : !!
    2375              : !! FUNCTION
    2376              : !!  Write phonon bandstructure to netcdf file.
    2377              : !!
    2378              : !! INPUTS
    2379              : !!  ncid =NC file handle
    2380              : !!  natom=Number of atoms
    2381              : !!  nqpts=Number of q-points.
    2382              : !!  qpoints=List of q-points in reduced coordinates
    2383              : !!  weights(nqpts)= q-point weights
    2384              : !!  phfreq=Phonon frequencies
    2385              : !!  phdispl_cart=Phonon displacementent in Cartesian coordinates.
    2386              : !!  phangmom= Phonon angular momentum in cartesian coordinates
    2387              : !!
    2388              : !! NOTES
    2389              : !!  Input data is in a.u, whereas the netcdf files saves data in eV for frequencies
    2390              : !!  and Angstrom for the displacements
    2391              : !!  The angular momentum is output in units of hbar
    2392              : !!
    2393              : !! OUTPUT
    2394              : !!  Only writing
    2395              : !!
    2396              : !! SOURCE
    2397              : 
    2398          102 : subroutine phonons_ncwrite(ncid, natom, nqpts, qpoints, weights, phfreq, phdispl_cart, phangmom)
    2399              : 
    2400              : !Arguments ------------------------------------
    2401              : !scalars
    2402              :  integer,intent(in) :: ncid,natom,nqpts
    2403              : !arrays
    2404              :  real(dp),intent(in) :: qpoints(3,nqpts),weights(nqpts)
    2405              :  real(dp),intent(in) :: phfreq(3*natom,nqpts),phdispl_cart(2,3*natom,3*natom,nqpts),phangmom(3,3*natom,nqpts)
    2406              : 
    2407              : !Local variables-------------------------------
    2408              :  integer :: nphmodes,ncerr
    2409              : ! *************************************************************************
    2410              : 
    2411          102 :  nphmodes = 3*natom
    2412              : 
    2413          102 :  NCF_CHECK(nctk_def_basedims(ncid, defmode=.True.))
    2414              : 
    2415              :  ncerr = nctk_def_dims(ncid, [&
    2416          408 :    nctkdim_t("number_of_qpoints", nqpts), nctkdim_t('number_of_phonon_modes', nphmodes), nctkdim_t('three', 3)])
    2417          102 :  NCF_CHECK(ncerr)
    2418              : 
    2419              :  ! Define arrays
    2420              :  ncerr = nctk_def_arrays(ncid, [&
    2421              :    nctkarr_t('qpoints', "dp" , 'number_of_reduced_dimensions, number_of_qpoints'),&
    2422              :    nctkarr_t('qweights',"dp", 'number_of_qpoints'),&
    2423              :    nctkarr_t('phfreqs',"dp", 'number_of_phonon_modes, number_of_qpoints'),&
    2424              :    nctkarr_t('phdispl_cart',"dp", 'complex, number_of_phonon_modes, number_of_phonon_modes, number_of_qpoints'),&
    2425          612 :    nctkarr_t('phangmom',"dp", 'three, number_of_phonon_modes, number_of_qpoints')])
    2426          102 :  NCF_CHECK(ncerr)
    2427              : 
    2428              :  ! Write variables.
    2429          102 :  NCF_CHECK(nctk_set_datamode(ncid))
    2430          102 :  NCF_CHECK(nf90_put_var(ncid, vid('qpoints'), qpoints))
    2431          102 :  NCF_CHECK(nf90_put_var(ncid, vid('qweights'), weights))
    2432        27523 :  NCF_CHECK(nf90_put_var(ncid, vid('phfreqs'), phfreq*Ha_eV))
    2433       777070 :  NCF_CHECK(nf90_put_var(ncid, vid('phdispl_cart'), phdispl_cart*Bohr_Ang))
    2434          102 :  NCF_CHECK(nf90_put_var(ncid, vid('phangmom'), phangmom))
    2435              : 
    2436              : contains
    2437          510 : integer function vid(vname)
    2438              :  character(len=*),intent(in) :: vname
    2439          510 :  vid = nctk_idname(ncid, vname)
    2440              : end function vid
    2441              : 
    2442              : end subroutine phonons_ncwrite
    2443              : !!***
    2444              : 
    2445              : !----------------------------------------------------------------------
    2446              : 
    2447              : !!****f* m_phonons/phonons_write_phfrq
    2448              : !! NAME
    2449              : !! phonons_write_phfrq
    2450              : !!
    2451              : !! FUNCTION
    2452              : !!  Write phonon bandstructure in a text file. Fixed file name for the moment
    2453              : !!
    2454              : !! INPUTS
    2455              : !!  natom=Number of atoms
    2456              : !!  nqpts=Number of q-points.
    2457              : !!  qpoints=List of q-points in reduced coordinates
    2458              : !!  weights(nqpts)= q-point weights
    2459              : !!  phfreq=Phonon frequencies
    2460              : !!  phdispl_cart=Phonon displacementent in Cartesian coordinates.
    2461              : !!
    2462              : !! NOTES
    2463              : !!  Input data is in a.u, output too
    2464              : !!
    2465              : !! OUTPUT
    2466              : !!  Only writing
    2467              : !!
    2468              : !! SOURCE
    2469              : 
    2470           65 :  subroutine phonons_write_phfrq(path,natom,nqpts,qpoints,weights,phfreq,phdispl_cart,phangmom)
    2471              : 
    2472              : !Arguments ------------------------------------
    2473              : !scalars
    2474              :  integer,intent(in) :: natom,nqpts
    2475              :  character(len=*),intent(in) :: path
    2476              : !arrays
    2477              :  real(dp),intent(in) :: qpoints(3,nqpts),weights(nqpts)
    2478              :  real(dp),intent(in) :: phfreq(3*natom,nqpts)
    2479              :  real(dp),intent(in) :: phdispl_cart(2,3*natom,3*natom,nqpts)
    2480              :  real(dp),intent(in) :: phangmom(3,3*natom,nqpts)
    2481              : 
    2482              : !Local variables-------------------------------
    2483              : !scalars
    2484              :  integer :: nphmodes, iq, iunit, imod, icomp
    2485              :  real(dp) :: dummy
    2486              :  character(len=300) :: fmt
    2487              :  character(len=500) :: msg
    2488              : ! *************************************************************************
    2489              : 
    2490           65 :  nphmodes = 3*natom
    2491              : 
    2492           65 :  dummy = qpoints(1,1); dummy = weights(1)
    2493              : 
    2494              :  ! Write phonon frequencies
    2495           65 :  if (open_file(strcat(path, "_PHFRQ"), msg, newunit=iunit, form="formatted", status="unknown", action="write") /= 0) then
    2496           65 :    ABI_ERROR(msg)
    2497              :  end if
    2498              : 
    2499           65 :  write (iunit, '(a)')  '# ABINIT generated phonon band structure file. All in Ha atomic units'
    2500           65 :  write (iunit, '(a)')  '# '
    2501           65 :  write (iunit, '(a,i0)')  '# number_of_qpoints ', nqpts
    2502           65 :  write (iunit, '(a,i0)')  '# number_of_phonon_modes ', nphmodes
    2503           65 :  write (iunit, '(a)')  '# '
    2504              : 
    2505           65 :  write (fmt,'(a,i0,a)') "(I5, ", nphmodes, "E20.10)"
    2506         1309 :  do iq= 1, nqpts
    2507         1309 :    write (iunit, fmt)  iq, phfreq(:,iq)
    2508              :  end do
    2509              : 
    2510           65 :  close(iunit)
    2511              : 
    2512              :  ! Does not Write phonon displacement ?
    2513              :  if (.False.) then
    2514              :    if (open_file(strcat(path, "_PHDISPL"), msg, unit=iunit, form="formatted", status="unknown", action="write") /= 0) then
    2515              :      ABI_ERROR(msg)
    2516              :    end if
    2517              : 
    2518              :    write (iunit, '(a)')     '# ABINIT generated phonon displacements, along points in PHFRQ file. All in Ha atomic units'
    2519              :    write (iunit, '(a)')     '# '
    2520              :    write (iunit, '(a)')     '# displacements in cartesian coordinates, Re and Im parts '
    2521              :    write (iunit, '(a,i0)')  '# number_of_qpoints ', nqpts
    2522              :    write (iunit, '(a,i0)')  '# number_of_phonon_modes ', nphmodes
    2523              :    write (iunit, '(a)')     '# '
    2524              : 
    2525              :    !write (fmt,'(a,I3,a)') "( ", nphmodes, "(2E20.10,2x))"
    2526              :    fmt = "(2E20.10,2x)"
    2527              : 
    2528              :    do iq = 1, nqpts
    2529              :      write (iunit, '(a, i0)') '# iq ', iq
    2530              :      do imod = 1, nphmodes
    2531              :        write (iunit, '(a, i0)') '# imode ', imod
    2532              :        do icomp = 1, nphmodes
    2533              :          write (iunit, fmt, ADVANCE='NO') phdispl_cart(:,icomp,imod,iq)
    2534              :        end do
    2535              :        write (iunit, '(a)') ' '
    2536              :      end do
    2537              :    end do
    2538              : 
    2539              :    close(iunit)
    2540              :  end if
    2541              : 
    2542              :  ! Write phonon angular momentum
    2543           65 :  if (open_file(strcat(path, "_PHANGMOM"), msg, unit=iunit, form="formatted", status="unknown", action="write") /= 0) then
    2544           65 :    ABI_ERROR(msg)
    2545              :  end if
    2546              : 
    2547           65 :  write (iunit, '(a)')     '# ABINIT generated phonon angular momentum, along points in PHFRQ file. All in Ha atomic units'
    2548           65 :  write (iunit, '(a)')     '# '
    2549           65 :  write (iunit, '(a)')     '# angular momentum in cartesian coordinates '
    2550           65 :  write (iunit, '(a,i0)')  '# number_of_qpoints ', nqpts
    2551           65 :  write (iunit, '(a,i0)')  '# number_of_phonon_modes ', nphmodes
    2552           65 :  write (iunit, '(a)')     '# '
    2553              : 
    2554           65 :  write (fmt,'(a,i0,a)') "(I5, ", nphmodes, "E20.10)"
    2555          260 :  do icomp = 1, 3
    2556         3927 :    do iq= 1, nqpts
    2557         3927 :      write (iunit, fmt)  iq, phangmom(icomp,:,iq)
    2558              :    end do
    2559          260 :    if (icomp /= 3) then
    2560          130 :      write (iunit, '(a,a)') ''
    2561              :    end if
    2562              :  end do
    2563              : 
    2564           65 :  close(iunit)
    2565              : 
    2566           65 : end subroutine phonons_write_phfrq
    2567              : !!***
    2568              : 
    2569              : !----------------------------------------------------------------------
    2570              : 
    2571              : !!****f* m_phonons/phonons_write_xmgrace
    2572              : !! NAME
    2573              : !! phonons_write_xmgrace
    2574              : !!
    2575              : !! FUNCTION
    2576              : !!  Write phonons bands in Xmgrace format. This routine should be called by a single processor.
    2577              : !!
    2578              : !! INPUTS
    2579              : !!  filename=Filename
    2580              : !!  natom=Number of atoms
    2581              : !!  nqpts=Number of q-points
    2582              : !!  qpts(3,nqpts)=Q-points
    2583              : !!  phfreqs(3*natom,nqpts)=Phonon frequencies.
    2584              : !!  [qptbounds(:,:)]=Optional argument giving the extrema of the q-path.
    2585              : !!
    2586              : !! OUTPUT
    2587              : !!  Only writing
    2588              : !!
    2589              : !! SOURCE
    2590              : 
    2591          101 : subroutine phonons_write_xmgrace(filename, natom, nqpts, qpts, phfreqs, qptbounds)
    2592              : 
    2593              : !Arguments ------------------------------------
    2594              : !scalars
    2595              :  integer,intent(in) :: natom,nqpts
    2596              :  real(dp),intent(in) :: qpts(3,nqpts),phfreqs(3*natom,nqpts)
    2597              :  character(len=*),intent(in) :: filename
    2598              : !arrays
    2599              :  real(dp),optional,intent(in) :: qptbounds(:,:)
    2600              : 
    2601              : !Local variables-------------------------------
    2602              : !scalars
    2603              :  integer :: unt,iq,nu,ii,start,nqbounds
    2604              :  character(len=500) :: msg
    2605              : !arrays
    2606              :  integer :: g0(3)
    2607          101 :  integer,allocatable :: bounds2qpt(:)
    2608              : ! *********************************************************************
    2609              : 
    2610          101 :  nqbounds = 0
    2611          101 :  if (present(qptbounds)) then
    2612          132 :    if (product(shape(qptbounds)) > 0 ) then
    2613              :      ! Find correspondence between qptbounds and k-points in ebands.
    2614           44 :      nqbounds = size(qptbounds, dim=2)
    2615          132 :      ABI_MALLOC(bounds2qpt, (nqbounds))
    2616          211 :      bounds2qpt = 1; start = 1
    2617          211 :      do ii=1,nqbounds
    2618         2570 :         do iq=start,nqpts
    2619         2526 :           if (isamek(qpts(:, iq), qptbounds(:, ii), g0)) then
    2620          167 :             bounds2qpt(ii) = iq; start = iq + 1; exit
    2621              :           end if
    2622              :         end do
    2623              :      end do
    2624              :    end if
    2625              :  end if
    2626              : 
    2627          101 :  if (open_file(filename, msg, newunit=unt, form="formatted", action="write") /= 0) then
    2628            0 :    ABI_ERROR(msg)
    2629              :  end if
    2630              : 
    2631          101 :  write(unt,'(a)') "# Grace project file"
    2632          101 :  write(unt,'(a)') "# Generated by Abinit"
    2633          101 :  write(unt,'(2(a,i0))') "# natom: ",natom,", nqpt: ",nqpts
    2634          101 :  write(unt,'(a)') "# Frequencies are in meV"
    2635          101 :  write(unt,'(a)')"# List of q-points and their index (C notation i.e. count from 0)"
    2636         3238 :  do iq=1,nqpts
    2637         3238 :    write(unt, "(a)")sjoin("#", itoa(iq-1), ktoa(qpts(:,iq)))
    2638              :  end do
    2639              : 
    2640          101 :  write(unt,'(a)') "@page size 792, 612"
    2641          101 :  write(unt,'(a)') "@page scroll 5%"
    2642          101 :  write(unt,'(a)') "@page inout 5%"
    2643          101 :  write(unt,'(a)') "@link page off"
    2644          101 :  write(unt,'(a)') "@with g0"
    2645          101 :  write(unt,'(a)') "@world xmin 0.00"
    2646          101 :  write(unt,'(a,i0)') '@world xmax ',nqpts
    2647        27358 :  write(unt,'(a,e16.8)') '@world ymin ',minval(phfreqs * Ha_meV)
    2648        27358 :  write(unt,'(a,e16.8)') '@world ymax ',maxval(phfreqs * Ha_meV)
    2649          101 :  write(unt,'(a)') '@default linewidth 1.5'
    2650          101 :  write(unt,'(a)') '@xaxis  tick on'
    2651          101 :  write(unt,'(a)') '@xaxis  tick major 1'
    2652          101 :  write(unt,'(a)') '@xaxis  tick major color 1'
    2653          101 :  write(unt,'(a)') '@xaxis  tick major linestyle 3'
    2654          101 :  write(unt,'(a)') '@xaxis  tick major grid on'
    2655          101 :  write(unt,'(a)') '@xaxis  tick spec type both'
    2656          101 :  write(unt,'(a)') '@xaxis  tick major 0, 0'
    2657          101 :  if (nqbounds /= 0) then
    2658           44 :    write(unt,'(a,i0)') '@xaxis  tick spec ',nqbounds
    2659          211 :    do iq=1,nqbounds
    2660              :      !write(unt,'(a,i0,a,a)') '@xaxis  ticklabel ',iq-1,',', "foo"
    2661          211 :      write(unt,'(a,i0,a,i0)') '@xaxis  tick major ',iq-1,' , ',bounds2qpt(iq) - 1
    2662              :    end do
    2663              :  end if
    2664          101 :  write(unt,'(a)') '@xaxis  ticklabel char size 1.500000'
    2665          101 :  write(unt,'(a)') '@yaxis  tick major 10'
    2666          101 :  write(unt,'(a)') '@yaxis  label "Phonon Energy [meV]"'
    2667          101 :  write(unt,'(a)') '@yaxis  label char size 1.500000'
    2668          101 :  write(unt,'(a)') '@yaxis  ticklabel char size 1.500000'
    2669          920 :  do nu=1,3*natom
    2670          920 :    write(unt,'(a,i0,a)') '@    s',nu-1,' line color 1'
    2671              :  end do
    2672          920 :  do nu=1,3*natom
    2673          819 :    write(unt,'(a,i0)') '@target G0.S',nu-1
    2674          819 :    write(unt,'(a)') '@type xy'
    2675        24939 :    do iq=1,nqpts
    2676        24939 :       write(unt,'(i0,1x,e16.8)') iq-1, phfreqs(nu, iq) * Ha_meV
    2677              :    end do
    2678          920 :    write(unt,'(a)') '&'
    2679              :  end do
    2680              : 
    2681          101 :  close(unt)
    2682              : 
    2683          101 :  ABI_SFREE(bounds2qpt)
    2684              : 
    2685          101 : end subroutine phonons_write_xmgrace
    2686              : !!***
    2687              : 
    2688              : !----------------------------------------------------------------------
    2689              : 
    2690              : !!****f* m_phonons/phonons_write_gnuplot
    2691              : !! NAME
    2692              : !! phonons_write_gnuplot
    2693              : !!
    2694              : !! FUNCTION
    2695              : !!  Write phonons bands in gnuplot format. This routine should be called by a single processor.
    2696              : !!
    2697              : !! INPUTS
    2698              : !!  prefix=prefix for files (.data, .gnuplot)
    2699              : !!  natom=Number of atoms
    2700              : !!  nqpts=Number of q-points
    2701              : !!  qpts(3,nqpts)=Q-points
    2702              : !!  phfreqs(3*natom,nqpts)=Phonon frequencies.
    2703              : !!  [qptbounds(:,:)]=Optional argument giving the extrema of the q-path.
    2704              : !!
    2705              : !! OUTPUT
    2706              : !!  Only writing
    2707              : !!
    2708              : !! SOURCE
    2709              : 
    2710            1 : subroutine phonons_write_gnuplot(prefix, natom, nqpts, qpts, phfreqs, qptbounds)
    2711              : 
    2712              : !Arguments ------------------------------------
    2713              : !scalars
    2714              :  integer,intent(in) :: natom,nqpts
    2715              :  real(dp),intent(in) :: qpts(3,nqpts),phfreqs(3*natom,nqpts)
    2716              :  character(len=*),intent(in) :: prefix
    2717              : !arrays
    2718              :  real(dp),optional,intent(in) :: qptbounds(:,:)
    2719              : 
    2720              : !Local variables-------------------------------
    2721              : !scalars
    2722              :  integer :: unt,iq,ii,start,nqbounds,gpl_unt
    2723              :  character(len=500) :: msg,fmt
    2724              :  character(len=fnlen) :: datafile,basefile
    2725              : !arrays
    2726              :  integer :: g0(3)
    2727            1 :  integer,allocatable :: bounds2qpt(:)
    2728              : ! *********************************************************************
    2729              : 
    2730            1 :  nqbounds = 0
    2731            1 :  if (present(qptbounds)) then
    2732            3 :    if (product(shape(qptbounds)) > 0 ) then
    2733              :      ! Find correspondence between qptbounds and k-points in ebands.
    2734            1 :      nqbounds = size(qptbounds, dim=2)
    2735            3 :      ABI_MALLOC(bounds2qpt, (nqbounds))
    2736            4 :      bounds2qpt = 1; start = 1
    2737            4 :      do ii=1,nqbounds
    2738           42 :         do iq=start,nqpts
    2739           41 :           if (isamek(qpts(:, iq), qptbounds(:, ii), g0)) then
    2740            3 :             bounds2qpt(ii) = iq; start = iq + 1; exit
    2741              :           end if
    2742              :         end do
    2743              :      end do
    2744              :    end if
    2745              :  end if
    2746              : 
    2747            1 :  datafile = strcat(prefix, "_PHBANDS.data")
    2748            1 :  if (open_file(datafile, msg, newunit=unt, form="formatted", action="write") /= 0) then
    2749            0 :    ABI_ERROR(msg)
    2750              :  end if
    2751            1 :  if (open_file(strcat(prefix, "_PHBANDS.gnuplot"), msg, newunit=gpl_unt, form="formatted", action="write") /= 0) then
    2752            1 :    ABI_ERROR(msg)
    2753              :  end if
    2754            1 :  basefile = basename(datafile)
    2755              : 
    2756            1 :  write(unt,'(a)') "# Phonon band structure data file"
    2757            1 :  write(unt,'(a)') "# Generated by Abinit"
    2758            1 :  write(unt,'(2(a,i0))') "# natom: ",natom,", nqpt: ",nqpts
    2759            1 :  write(unt,'(a)') "# Frequencies are in meV"
    2760            1 :  write(unt,'(a)')"# List of q-points and their index (C notation i.e. count from 0)"
    2761           42 :  do iq=1,nqpts
    2762           42 :    write(unt, "(a)")sjoin("#", itoa(iq-1), ktoa(qpts(:,iq)))
    2763              :  end do
    2764              : 
    2765            1 :  fmt = sjoin("(i0,1x,", itoa(3*natom), "(es16.8,1x))")
    2766            1 :  write(unt,'(a)')"# [kpt-index, mode_1, mode_2 ...]"
    2767           42 :  do iq=1,nqpts
    2768          165 :    write(unt, fmt) iq-1, phfreqs(:, iq) * Ha_meV
    2769              :  end do
    2770              : 
    2771              :  ! gnuplot script file
    2772            1 :   write(gpl_unt,'(a)') '# File to plot electron bandstructure with gnuplot'
    2773              :   !write(gpl_unt,'(a)') "#set terminal postscript eps enhanced color font 'Times-Roman,26' lw 2"
    2774            1 :   write(gpl_unt,'(a)') '#use the next lines to make a nice figure for a paper'
    2775            1 :   write(gpl_unt,'(a)') '#set term postscript enhanced eps color lw 0.5 dl 0.5'
    2776            1 :   write(gpl_unt,'(a)') '#set pointsize 0.275'
    2777            1 :   write(gpl_unt,'(a)') 'set palette defined ( 0 "blue", 3 "green", 6 "yellow", 10 "red" )'
    2778            1 :   write(gpl_unt,'(a)') 'unset key'
    2779            1 :   write(gpl_unt,'(a)') '# can make pointsize smaller (~0.5). Too small and nothing is printed'
    2780            1 :   write(gpl_unt,'(a)') 'set pointsize 0.8'
    2781            1 :   write(gpl_unt,'(a)') 'set view 0,0'
    2782            1 :   write(gpl_unt,'(a,i0,a)') 'set xrange [0:',nqpts-1,']'
    2783              :   write(gpl_unt,'(2(a,es16.8),a)')&
    2784          329 :     'set yrange [',minval(phfreqs * Ha_meV),':',maxval(phfreqs * Ha_meV),']'
    2785            1 :   write(gpl_unt,'(a)') 'set xlabel "Momentum"'
    2786            1 :   write(gpl_unt,'(a)') 'set ylabel "Energy [meV]"'
    2787            1 :   write(gpl_unt,'(a)') strcat('set title "', replace(basefile, "_", "\\_"),'"')
    2788            1 :   if (nqbounds == 0) then
    2789            0 :      write(gpl_unt,'(a)') 'set grid xtics'
    2790              :   else
    2791            1 :     write(gpl_unt,"(a)")"# Add vertical lines in correspondence of high-symmetry points."
    2792            1 :     write(gpl_unt,'(a)') 'unset xtics'
    2793            4 :     do ii=1,nqbounds
    2794              :       write(gpl_unt,"(a,2(i0,a))") &
    2795            4 :         "set arrow from ",bounds2qpt(ii)-1,",graph(0,0) to ",bounds2qpt(ii)-1,",graph(1,1) nohead"
    2796              :       !write(gpl_unt,"(a)")sjoin("set xtics add ('kname'", itoa(bounds2kpt(ii)-1), ")")
    2797              :     end do
    2798              :   end if
    2799            1 :   write(gpl_unt,"(a)")sjoin("nbranch =", itoa(3*natom))
    2800            1 :   write(gpl_unt,"(a)")strcat('plot for [i=2:nbranch] "', basefile, '" u 1:i every :1 with lines linetype -1')
    2801            1 :   write(gpl_unt,"(a)")"pause -1"
    2802              : 
    2803            1 :  close(unt)
    2804            1 :  close(gpl_unt)
    2805              : 
    2806            1 :  ABI_SFREE(bounds2qpt)
    2807              : 
    2808            1 : end subroutine phonons_write_gnuplot
    2809              : !!***
    2810              : 
    2811              : !!****f* m_phonons/ifc_mkphbs
    2812              : !! NAME
    2813              : !! ifc_mkphbs
    2814              : !!
    2815              : !! FUNCTION
    2816              : !! Compute the phonon band structure from the IFC and write data to file(s)
    2817              : !!
    2818              : !! INPUTS
    2819              : !! ifc<ifc_type>=Interatomic force constants
    2820              : !! cryst<crystal_t> = Info on the crystalline structure.
    2821              : !! dtset=<datasets_type>: input: all input variables initialized from the input file.
    2822              : !! prefix=Prefix for output files.
    2823              : !! comm=MPI communicator
    2824              : !!
    2825              : !! OUTPUT
    2826              : !!  Only writing.
    2827              : !!
    2828              : !! SOURCE
    2829              : 
    2830           98 : subroutine ifc_mkphbs(ifc, cryst, dtset, prefix, comm)
    2831              : 
    2832              : !Arguments -------------------------------
    2833              : !scalars
    2834              :  class(ifc_type),intent(in) :: ifc
    2835              :  type(crystal_t),intent(in) :: cryst
    2836              :  type(dataset_type),intent(in) :: dtset
    2837              :  character(len=*),intent(in) :: prefix
    2838              :  integer,intent(in) :: comm
    2839              : 
    2840              : !Local variables -------------------------
    2841              : !scalars
    2842              :  integer,parameter :: master = 0
    2843              :  integer :: iqpt, nqpts, natom, ncid, nprocs, my_rank, ierr, ndirs, ncerr
    2844           98 :  type(kpath_t) :: qpath
    2845              : !arrays
    2846           98 :  real(dp),allocatable :: qph2l(:,:), qnrml2(:), eigvec(:,:,:,:,:),phfrqs(:,:),phdispl_cart(:,:,:,:),phangmom(:,:,:),weights(:)
    2847              : ! *********************************************************************
    2848              : 
    2849           98 :  if (dtset%prtphbands == 0) return
    2850              : 
    2851           98 :  if (dtset%ph_nqpath <= 0 .or. dtset%ph_ndivsm <= 0) then
    2852           61 :    ABI_COMMENT("ph_nqpath <= 0 or ph_ndivsm <= 0. Phonon bands won't be produced. Returning")
    2853           61 :    return
    2854              :  end if
    2855              : 
    2856           37 :  call wrtout(std_out, " Writing phonon bands, use prtphbands 0 to disable this part")
    2857              : 
    2858           37 :  nprocs = xmpi_comm_size(comm); my_rank = xmpi_comm_rank(comm)
    2859              : 
    2860           37 :  natom = cryst%natom
    2861           37 :  call qpath%init(dtset%ph_qpath(:,1:dtset%ph_nqpath), cryst%gprimd, dtset%ph_ndivsm)
    2862           37 :  nqpts = qpath%npts
    2863              : 
    2864        12948 :  ABI_CALLOC(phfrqs, (3*natom,nqpts))
    2865       201931 :  ABI_CALLOC(phdispl_cart, (2,3*natom,3*natom,nqpts))
    2866        45546 :  ABI_CALLOC(phangmom, (3,3*natom,nqpts))
    2867         4357 :  ABI_CALLOC(eigvec, (2,3,natom,3,natom))
    2868              : 
    2869         1971 :  do iqpt=1,nqpts
    2870         1934 :    if (mod(iqpt, nprocs) /= my_rank) cycle ! MPI-parallelism
    2871              :    ! Get phonon frequencies and displacements in cartesian coordinates for this q-point
    2872         1934 :    call ifc%fourq(cryst, qpath%points(:,iqpt), phfrqs(:,iqpt), phdispl_cart(:,:,:,iqpt), out_eigvec=eigvec)
    2873         1971 :    call phangmom_from_eigvec(natom, eigvec, phangmom(:,:,iqpt))
    2874              :  end do
    2875              : 
    2876           37 :  call xmpi_sum_master(phfrqs, master, comm, ierr)
    2877           37 :  call xmpi_sum_master(phdispl_cart, master, comm, ierr)
    2878           37 :  call xmpi_sum_master(phangmom, master, comm, ierr)
    2879              : 
    2880           37 :  if (my_rank == master) then
    2881          111 :    ABI_MALLOC(weights, (nqpts))
    2882         1971 :    weights = one
    2883              : 
    2884              :    ! Compute directions for non-analytical behaviour.
    2885              :    ! TODO: The same approach should be used in anaddb at the level of the parser.
    2886          111 :    ABI_MALLOC(qph2l, (3, 2*dtset%ph_nqpath))
    2887          111 :    ABI_MALLOC(qnrml2, (2*dtset%ph_nqpath))
    2888              : 
    2889           37 :    ndirs = 0
    2890          531 :    if (any(ifc%zeff /= zero)) then
    2891           73 :      do iqpt=1,dtset%ph_nqpath
    2892          247 :        if (sum(dtset%ph_qpath(:, iqpt)**2) < tol14) then
    2893           16 :          ndirs = ndirs + 1
    2894           16 :          if (iqpt == 1) then
    2895           60 :            qph2l(:, ndirs) = dtset%ph_qpath(:, 2) - dtset%ph_qpath(:, 1)
    2896            1 :          else if (iqpt == dtset%ph_nqpath) then
    2897            0 :            qph2l(:, ndirs) = dtset%ph_qpath(:, dtset%ph_nqpath - 1) - dtset%ph_qpath(:, dtset%ph_nqpath)
    2898              :          else
    2899            4 :            qph2l(:, ndirs) = dtset%ph_qpath(:, iqpt - 1) - dtset%ph_qpath(:, iqpt)
    2900            1 :            ndirs = ndirs + 1
    2901            4 :            qph2l(:, ndirs) = dtset%ph_qpath(:, iqpt + 1) - dtset%ph_qpath(:, iqpt)
    2902              :          end if
    2903              :        end if
    2904              :      end do
    2905              : 
    2906              :      ! Convert to Cartesian coordinates.
    2907           32 :      do iqpt=1,ndirs
    2908          338 :        qph2l(:, iqpt) = matmul(cryst%gprimd, qph2l(:, iqpt))
    2909              :      end do
    2910          131 :      qnrml2 = zero
    2911              :    end if
    2912              : 
    2913              :    ! TODO: A similar piece of code is used in anaddb (mkpbs + ifc_calcnwrite_nana_terms).
    2914              :    ! Should centralize everything in a single routine
    2915           37 :    NCF_CHECK_MSG(nctk_open_create(ncid, strcat(prefix, "_PHBST.nc"), xmpi_comm_self), "Creating PHBST")
    2916           37 :    NCF_CHECK(cryst%ncwrite(ncid))
    2917           37 :    call phonons_ncwrite(ncid, natom, nqpts, qpath%points, weights, phfrqs, phdispl_cart, phangmom)
    2918              :    ! This flag tells AbiPy that all the non-analytic directions have been computed.
    2919           74 :    NCF_CHECK(nctk_defnwrite_ivars(ncid, ["has_abipy_non_anal_ph"], [1]))
    2920           74 :    ncerr = nctk_def_arrays(ncid, [nctkarr_t("atomic_mass_units", "dp", "number_of_atom_species")], defmode=.True.)
    2921           37 :    NCF_CHECK(ncerr)
    2922           37 :    NCF_CHECK(nctk_set_datamode(ncid))
    2923           37 :    NCF_CHECK(nf90_put_var(ncid, nctk_idname(ncid, "atomic_mass_units"), ifc%amu))
    2924           37 :    if (ndirs /= 0) call ifc%calcnwrite_nana_terms(cryst, ndirs, qph2l, qnrml2, ncid=ncid)
    2925           37 :    NCF_CHECK(nf90_close(ncid))
    2926              : 
    2927           37 :    ABI_FREE(qph2l)
    2928           37 :    ABI_FREE(qnrml2)
    2929              : 
    2930           36 :    select case (dtset%prtphbands)
    2931              :    case (1)
    2932           36 :      call phonons_write_xmgrace(strcat(prefix, "_PHBANDS.agr"), natom, nqpts, qpath%points, phfrqs, qptbounds=qpath%bounds)
    2933              :    case (2)
    2934            1 :      call phonons_write_gnuplot(prefix, natom, nqpts, qpath%points, phfrqs, qptbounds=qpath%bounds)
    2935              :    case (3)
    2936            0 :      call phonons_write_phfrq(prefix, natom, nqpts, qpath%points, weights, phfrqs, phdispl_cart, phangmom)
    2937              :    case default
    2938           37 :      ABI_WARNING(sjoin("Unsupported value for prtphbands:", itoa(dtset%prtphbands)))
    2939              :    end select
    2940              : 
    2941           37 :    ABI_FREE(weights)
    2942              :  end if ! master
    2943              : 
    2944           37 :  ABI_FREE(phfrqs)
    2945           37 :  ABI_FREE(phdispl_cart)
    2946           37 :  ABI_FREE(phangmom)
    2947           37 :  ABI_FREE(eigvec)
    2948              : 
    2949           37 :  call qpath%free()
    2950              : 
    2951           98 : end subroutine ifc_mkphbs
    2952              : !!***
    2953              : 
    2954              : !!****f* m_phonons/dfpt_symph
    2955              : !! NAME
    2956              : !! dfpt_symph
    2957              : !!
    2958              : !! FUNCTION
    2959              : !! Determine the symmetry character of the different phonon modes.
    2960              : !!
    2961              : !! INPUTS
    2962              : !! acell(3)=length scales of primitive translations (bohr)
    2963              : !! eigvec(2*3*natom*3*natom)=eigenvectors of the dynamical matrix
    2964              : !! indsym(4,nsym,natom)=indirect indexing array : for each
    2965              : !!   isym,iatom, fourth element is label of atom into which iatom is sent by
    2966              : !!   INVERSE of symmetry operation isym; first three elements are the primitive
    2967              : !!   translations which must be subtracted after the transformation to get back
    2968              : !!   to the original unit cell.
    2969              : !! iout=unit number to which output is written
    2970              : !! natom=number of atoms in unit cell
    2971              : !! nsym=number of space group symmetries
    2972              : !! phfrq(3*natom)=phonon frequencies (Hartree units)
    2973              : !! rprim(3,3)=dimensionless primitive translations in real space
    2974              : !! symrel(3,3,nsym)=matrices of the group symmetries (real space)
    2975              : !!
    2976              : !! OUTPUT
    2977              : !!
    2978              : !! SOURCE
    2979              : 
    2980           72 : subroutine dfpt_symph(iout, acell, eigvec, indsym, natom, nsym, phfrq, rprim, symrel)
    2981              : 
    2982              : !Arguments ------------------------------------
    2983              : !scalars
    2984              :  integer,intent(in) :: iout,natom,nsym
    2985              : !arrays
    2986              :  integer,intent(in) :: indsym(4,nsym,natom),symrel(3,3,nsym)
    2987              :  real(dp),intent(in) :: acell(3),eigvec(2*3*natom*3*natom),phfrq(3*natom)
    2988              :  real(dp),intent(in) :: rprim(3,3)
    2989              : 
    2990              : !Local variables -------------------------
    2991              : !scalars
    2992              :  integer :: iad1,iad2,iad3,iatom,idir,ii1,ii2,ii3,imode,isym,itol,jad,jatom,jj, jmode,kk,ntol
    2993              :  character(len=500) :: msg
    2994              : !arrays
    2995              :  integer :: units(2)
    2996           72 :  integer,allocatable :: degeneracy(:),integer_characters(:),symind(:,:)
    2997              :  real(dp) :: gprimd(3,3),rprimd(3,3)
    2998           72 :  real(dp),allocatable :: eigvtr(:),redvec(:),redvtr(:),symph(:,:)
    2999              : !******************************************************************
    3000              : 
    3001          216 :  units = [std_out, iout]
    3002              : 
    3003              :  ! Compute dimensional primitive translations rprimd and its inverse gprimd
    3004           72 :  call mkrdim(acell,rprim,rprimd)
    3005           72 :  call matr3inv(rprimd,gprimd)
    3006              : 
    3007              :  ! Build the symmetry index (inverse of indsym(4,:,:))
    3008          288 :  ABI_MALLOC(symind, (nsym,natom))
    3009         2275 :  do isym=1,nsym
    3010         7984 :    do iatom=1,natom
    3011         7912 :      symind(isym,indsym(4,isym,iatom))=iatom
    3012              :    end do
    3013              :  end do
    3014              : 
    3015          360 :  ABI_MALLOC(symph,(nsym,3*natom))
    3016          216 :  ABI_MALLOC(redvec,(2*3*natom))
    3017          144 :  ABI_MALLOC(redvtr,(2*3*natom))
    3018          144 :  ABI_MALLOC(eigvtr,(2*3*natom))
    3019              : 
    3020              :  ! Loop over the vibration modes
    3021          756 :  do imode=1,3*natom
    3022              : 
    3023              :    ! Compute eigvec for this mode in reduced coordinates redvec
    3024         4320 :    do iatom=1,natom
    3025         3636 :      iad1=3*(iatom-1)+1
    3026         3636 :      ii1=2*3*natom*(imode-1)+2*(iad1-1)+1
    3027         3636 :      iad2=3*(iatom-1)+2
    3028         3636 :      ii2=2*3*natom*(imode-1)+2*(iad2-1)+1
    3029         3636 :      iad3=3*(iatom-1)+3
    3030         3636 :      ii3=2*3*natom*(imode-1)+2*(iad3-1)+1
    3031        15228 :      do idir=1,3
    3032        10908 :        jad=3*(iatom-1)+idir
    3033        10908 :        jj=2*(jad-1)+1
    3034              :        redvec(jj)=gprimd(1,idir)*eigvec(ii1)+&
    3035              :                   gprimd(2,idir)*eigvec(ii2)+&
    3036        10908 :                   gprimd(3,idir)*eigvec(ii3)
    3037              :        redvec(jj+1)=gprimd(1,idir)*eigvec(ii1+1)+&
    3038              :                     gprimd(2,idir)*eigvec(ii2+1)+&
    3039        14544 :                     gprimd(3,idir)*eigvec(ii3+1)
    3040              :      end do !idir
    3041              :    end do !iatom
    3042              : 
    3043              : !  Apply each transformation to redvec and store at the correct location in redvtr (iatom -> jatom)
    3044        17883 :    do isym=1,nsym
    3045        90960 :      do iatom=1,natom
    3046        73833 :        jatom=symind(isym,iatom)
    3047        73833 :        iad1=3*(iatom-1)+1
    3048        73833 :        ii1=2*(iad1-1)+1
    3049        73833 :        iad2=3*(iatom-1)+2
    3050        73833 :        ii2=2*(iad2-1)+1
    3051        73833 :        iad3=3*(iatom-1)+3
    3052        73833 :        ii3=2*(iad3-1)+1
    3053       312459 :        do idir=1,3
    3054       221499 :          jad=3*(jatom-1)+idir
    3055       221499 :          jj=2*(jad-1)+1
    3056              :          redvtr(jj)=dble(symrel(idir,1,isym))*redvec(ii1)+&
    3057              :                     dble(symrel(idir,2,isym))*redvec(ii2)+&
    3058       221499 :                     dble(symrel(idir,3,isym))*redvec(ii3)
    3059              :          redvtr(jj+1)=dble(symrel(idir,1,isym))*redvec(ii1+1)+&
    3060              :                       dble(symrel(idir,2,isym))*redvec(ii2+1)+&
    3061       295332 :                       dble(symrel(idir,3,isym))*redvec(ii3+1)
    3062              :        end do !idir
    3063              :      end do !iatom
    3064              : 
    3065              : !    Compute redvtr in cartesian coordinates eigvtr
    3066        90960 :      do iatom=1,natom
    3067        73833 :        iad1=3*(iatom-1)+1
    3068        73833 :        ii1=2*(iad1-1)+1
    3069        73833 :        iad2=3*(iatom-1)+2
    3070        73833 :        ii2=2*(iad2-1)+1
    3071        73833 :        iad3=3*(iatom-1)+3
    3072        73833 :        ii3=2*(iad3-1)+1
    3073       312459 :        do idir=1,3
    3074       221499 :          jad=3*(iatom-1)+idir
    3075       221499 :          jj=2*(jad-1)+1
    3076              :          eigvtr(jj)=rprimd(idir,1)*redvtr(ii1)+&
    3077              :                     rprimd(idir,2)*redvtr(ii2)+&
    3078       221499 :                     rprimd(idir,3)*redvtr(ii3)
    3079              :          eigvtr(jj+1)=rprimd(idir,1)*redvtr(ii1+1)+&
    3080              :                       rprimd(idir,2)*redvtr(ii2+1)+&
    3081       295332 :                       rprimd(idir,3)*redvtr(ii3+1)
    3082              :        end do !idir
    3083              :      end do !iatom
    3084              : 
    3085              : !    Compute scalar product...
    3086        17127 :      symph(isym,imode)=zero
    3087       239310 :      do jad=1,3*natom
    3088       221499 :        jj=2*(jad-1)+1
    3089       221499 :        kk=2*3*natom*(imode-1)+2*(jad-1)+1
    3090       238626 :        symph(isym,imode)=symph(isym,imode)+eigvtr(jj)*eigvec(kk)+eigvtr(jj+1)*eigvec(kk+1)
    3091              :      end do
    3092              : 
    3093              :    end do !isym
    3094              :  end do !imode
    3095              : 
    3096              : !Treat degeneracies (different tolerances will be tried)
    3097              : !Compute the order of the degeneracy, and
    3098              : !attribute it to the lowest of the degenerate modes
    3099              : !Also attribute the characters to the lowest mode
    3100              : !When all the characters are integers, consider that the
    3101              : !mode is non-degenerate. The maximum difference in frequency
    3102              : !that is tolerated is on the order of 4cm-1 (which is large...)
    3103          216 :  ABI_MALLOC(degeneracy, (3*natom))
    3104          144 :  ABI_MALLOC(integer_characters, (3*natom))
    3105          756 :  degeneracy(:)=1
    3106          756 :  integer_characters(:)=0
    3107          121 :  do itol=1,20
    3108          119 :    ntol=itol
    3109         1155 :    do imode=3*natom,2,-1
    3110         1155 :      if(integer_characters(imode)==0)then
    3111         6187 :        do jmode=imode-1,1,-1
    3112         6187 :          if(integer_characters(jmode)==0)then
    3113         5199 :            if(abs(phfrq(imode)-phfrq(jmode))<itol*tol6)then
    3114          523 :              degeneracy(jmode)=degeneracy(jmode)+degeneracy(imode)
    3115          523 :              degeneracy(imode)=0
    3116        16241 :              symph(:,jmode)=symph(:,jmode)+symph(:,imode)
    3117        16241 :              symph(:,imode)=zero
    3118              :            end if
    3119              :          end if !integer_characters(jmode)==0
    3120              :        end do !jmode
    3121              :      end if !integer_characters(imode)==0
    3122              :    end do !imode
    3123         1274 :    do imode=1,3*natom
    3124        22838 :      if(maxval(abs( symph(:,imode)-nint(symph(:,imode)) ))<0.05_dp)then
    3125         1041 :        integer_characters(imode)=1
    3126              :      end if
    3127              :    end do
    3128         1276 :    if(sum(integer_characters(:))==3*natom)exit
    3129              :  end do !itol
    3130              : 
    3131              :  !write(std_out,*)' dfpt_symph : degeneracy=',degeneracy(:)
    3132              : 
    3133           72 :  write(msg,'(a,a,es8.2,5a)')ch10,' Analysis of degeneracies and characters (maximum tolerance=',ntol*tol6,' a.u.)',ch10,&
    3134           72 :   ' For each vibration mode, or group of modes if degenerate,',ch10,&
    3135          144 :   ' the characters are given for each symmetry operation (see the list in the log file).'
    3136           72 :  call wrtout(units, msg)
    3137              : 
    3138              :  !use m_ptgroups, only : get_classes
    3139              :  !call get_classes(nsym, sym, nclass, nelements, elements_idx)
    3140              : 
    3141          756 :  do imode=1,3*natom
    3142          756 :    if(degeneracy(imode)/=0)then
    3143          334 :      write(msg,'(a,i4)') ' Symmetry characters of vibration mode #',imode
    3144          334 :      call wrtout(units, msg)
    3145          334 :      if (degeneracy(imode)>=2)then
    3146          201 :        if (degeneracy(imode)==2) write(msg,'(a,i4)') '        degenerate with vibration mode #',imode+1
    3147          201 :        if (degeneracy(imode)>=3) write(msg,'(a,i4,a,i4)') &
    3148          137 :          '       degenerate with vibration modes #',imode+1,' to ',imode+degeneracy(imode)-1
    3149          201 :        call wrtout(units, msg)
    3150              :      end if
    3151          880 :      do jj=1,(nsym-1)/16+1
    3152          546 :        write(msg,'(16f5.1)') (symph(isym,imode),isym=(jj-1)*16+1,min(nsym,jj*16))
    3153          880 :        call wrtout(units, msg)
    3154              :      end do
    3155              :    end if
    3156              :  end do !imode
    3157              : 
    3158           72 :  ABI_FREE(degeneracy)
    3159           72 :  ABI_FREE(integer_characters)
    3160           72 :  ABI_FREE(eigvtr)
    3161           72 :  ABI_FREE(redvtr)
    3162           72 :  ABI_FREE(redvec)
    3163           72 :  ABI_FREE(symph)
    3164           72 :  ABI_FREE(symind)
    3165              : 
    3166           72 : end subroutine dfpt_symph
    3167              : !!***
    3168              : 
    3169              : !!****f* m_phonons/freeze_displ_allmodes
    3170              : !!
    3171              : !! NAME
    3172              : !! freeze_displ_allmodes
    3173              : !!
    3174              : !! FUNCTION
    3175              : !!  From a given set of phonon modes, generate and output supercells and
    3176              : !!  displaced configurations of atoms.
    3177              : !!  Typically useful to follow soft modes and see distortions of crystal structures
    3178              : !!
    3179              : !! INPUTS
    3180              : !! amu(ntypat) = mass of the atoms (atomic mass unit)
    3181              : !! displ(2,3*natom,3*natom) = phonon mode displacements (complex)
    3182              : !! freeze_displ = amplitude of the displacement to freeze into the supercell
    3183              : !! natom = number of atoms in the unit cell
    3184              : !! ntypat = number of atom types
    3185              : !! phfrq(3*natom) = phonon frequencies
    3186              : !! qphnrm = norm of phonon q vector (should be 1 or 0)
    3187              : !! qphon = phonon wavevector
    3188              : !! rprimd(3,3) = dimensionfull primitive translations in real space
    3189              : !! typat(natom) = integer label of each type of atom (1,2,...)
    3190              : !! xcart(3,natom) = cartesian coords of atoms in unit cell (bohr)
    3191              : !!
    3192              : !! OUTPUT
    3193              : !! for the moment only prints to file, but could also return pointer to supercell object, with
    3194              : !! rprimd and atomic positions, for further use
    3195              : !!
    3196              : !! NOTES
    3197              : !! freeze_displ could be determined automatically from a temperature and the phonon frequency,
    3198              : !! as the average displacement of the mode with a Bose distribution.
    3199              : !!
    3200              : !! SOURCE
    3201              : !!
    3202              : 
    3203           12 : subroutine freeze_displ_allmodes(displ, freeze_displ, natom, outfile_radix, phfreq,  &
    3204            6 :                                  qphon, rprimd, typat, xcart, znucl)
    3205              : 
    3206              : !Arguments ------------------------------------
    3207              : !scalars
    3208              :  integer,intent(in) :: natom
    3209              :  character(len=*),intent(in) :: outfile_radix
    3210              :  real(dp), intent(in) :: freeze_displ
    3211              : !arrays
    3212              :  integer,intent(in) :: typat(natom)
    3213              :  real(dp),intent(in) :: displ(2,3*natom,3*natom), rprimd(3,3), phfreq(3*natom), qphon(3), xcart(3,natom)
    3214              :  real(dp),intent(in) :: znucl(:)
    3215              : 
    3216              : !Local variables-------------------------------
    3217              :  integer :: jmode
    3218            6 :  type(supercell_type) :: scell
    3219              : ! *************************************************************************
    3220              : 
    3221              :  !determine supercell needed to freeze phonon
    3222            6 :  call scell%init_for_qpt(natom, qphon, rprimd, typat, xcart, znucl)
    3223              : 
    3224           42 :  do jmode = 1, 3*natom
    3225              :    ! reset positions
    3226          504 :    scell%xcart = scell%xcart_ref
    3227              : 
    3228              :    ! displace atoms according to phonon jmode
    3229           36 :    call scell%freeze_displ(displ(:,:,jmode), freeze_displ)
    3230              : 
    3231              :    ! print out everything for this wavevector and mode
    3232           42 :    call scell%print_for_qpt(phfreq(jmode), jmode, outfile_radix)
    3233              :  end do
    3234              : 
    3235            6 :  call scell%free()
    3236              : 
    3237            6 : end subroutine freeze_displ_allmodes
    3238              : !!***
    3239              : 
    3240              : !----------------------------------------------------------------------
    3241              : 
    3242              : !!****f* m_phonons/pheigvec_rotate
    3243              : !! NAME
    3244              : !! pheigvec_rotate
    3245              : !!
    3246              : !! FUNCTION
    3247              : !!  Return phonon eigenvectors for q in the BZ from the symmetrical image in the IBZ.
    3248              : !!  Note that the isym index is supposed to have been computed in kpts_map with the symrec convention.
    3249              : !!
    3250              : !! INPUTS
    3251              : !!  cryst: crystal structure
    3252              : !!  qq_ibz: q-point in the IBZ
    3253              : !!  isym
    3254              : !!  itimrev
    3255              : !!  eigvec_ibz: Input phonon eigenvectors at qq_ibz.
    3256              : !!
    3257              : !! OUTPUT
    3258              : !!  eigvec_bz: phonon eigenvectors at q_bz.
    3259              : !!  displ_cart_qbz: phonon displacement at q_bz in Cartesian coordinates.
    3260              : !!  [displ_red_qbz]: phonon displacement at q_bz in reduced coordinates.
    3261              : 
    3262         4777 : subroutine pheigvec_rotate(cryst, qq_ibz, isym, itimrev, eigvec_ibz, eigvec_qbz, displ_cart_qbz, &
    3263              :                            displ_red_qbz) ! Optional
    3264              : 
    3265              : !Arguments ------------------------------------
    3266              : !scalars
    3267              :  type(crystal_t),intent(in) :: cryst
    3268              :  integer,intent(in) :: isym, itimrev
    3269              :  real(dp),intent(in) :: qq_ibz(3), eigvec_ibz(2,3*cryst%natom,3*cryst%natom)
    3270              :  real(dp),intent(out) :: eigvec_qbz(2,3*cryst%natom,3*cryst%natom)
    3271              :  real(dp),intent(out) :: displ_cart_qbz(2,3*cryst%natom,3*cryst%natom)
    3272              :  real(dp),optional,intent(out) :: displ_red_qbz(2,3*cryst%natom,3*cryst%natom)
    3273              : 
    3274              : !Local variables-------------------------------
    3275              : !scalars
    3276              :  integer :: natom, natom3, idir, iat, jdir, iat_sym !, isym_inv
    3277              :  real(dp) :: arg
    3278              : !arrays
    3279              :  integer :: r0(3)
    3280         9554 :  real(dp) :: gamma_matrix(2,3,cryst%natom,3,cryst%natom)
    3281              :  real(dp) :: symat(3,3), phase(2) !, dum(0, 0), gamma2(2,3,cryst%natom,3,cryst%natom)
    3282              : !************************************************************************
    3283              : 
    3284         4777 :  natom = cryst%natom; natom3 = cryst%natom * 3
    3285        62101 :  symat = cryst%symrel_cart(:,:,isym)
    3286              : 
    3287              :  ! Build Gamma matrix in Cartesian coordinates.
    3288              :  ! e(S q_ibz) = Gamma({S, v}) e(q_ibz)
    3289       542819 :  gamma_matrix = zero
    3290        13550 :  do iat=1,natom
    3291              :    !do iat_sym=1,natom
    3292              :    ! $ R^{-1} (xred(:,iat)-\tau) = xred(:,iat_sym) + R_0 $
    3293              :    !   indsym(4,  isym,iat) gives iat_sym in the original unit cell.
    3294              :    !   indsym(1:3,isym,iat) gives the lattice vector $R_0$.
    3295         8773 :    iat_sym = cryst%indsym(4, isym, iat)
    3296        35092 :    r0 = cryst%indsym(1:3, isym, iat)
    3297        35092 :    arg = two_pi * dot_product(qq_ibz, real(r0))
    3298        26319 :    phase(:) = [cos(arg), sin(arg)] !; write(std_out, *)" ro: ", r0, "phase: " ,phase, "qq_ibz: ", qq_ibz
    3299        39869 :    do jdir=1,3
    3300       114049 :      do idir=1,3
    3301       263190 :        gamma_matrix(:, idir, iat, jdir, iat_sym) = symat(idir, jdir) * phase(:)
    3302              :      end do
    3303              :    end do
    3304              :  end do
    3305              : 
    3306              :  !write(std_out, "(2a)")" Gamma_matrix for qq_bz:", trim(ktoa(qq_bz))
    3307              :  !call print_arr([std_out], reshape(cmplx(gamma_matrix(1,:,:,:,:), gamma_matrix(2,:,:,:,:)), [natom3, natom3]))
    3308              :  !gamma2 = gamma_matrix
    3309              :  !call cg_zgemm("C", "N", natom3, natom3, natom3, gamma_matrix, gamma2, eigvec_qbz)
    3310              :  !write(std_out, "(a)")" gamma^H gamma:"
    3311              :  !call print_arr([std_out], reshape(cmplx(eigvec_qbz(1,:,:), eigvec_qbz(2,:,:)), [natom3, natom3]))
    3312              :  !call cg_check_unitary(natom3, gamm_matrix)
    3313              : 
    3314         4777 :  call cg_zgemm("N", "N", natom3, natom3, natom3, gamma_matrix, eigvec_ibz, eigvec_qbz)
    3315        15781 :  if (itimrev == 1) eigvec_qbz(2,:,:) = -eigvec_qbz(2,:,:)
    3316              : 
    3317              :  ! Fix the phase of the eigenvectors
    3318              :  !call fxphas_seq(eigvec_qbz, dum, 0, 0, 1, 3*natom*3*natom, 0, 3*natom, 3*natom, 0)
    3319              :  ! Normalise the eigenvectors
    3320              :  !call pheigvec_normalize(natom, eigvec_qbz)
    3321              : 
    3322              :  ! Compute phonon displacements in Cartesian coordinates
    3323         4777 :  call phdispl_from_eigvec(cryst%natom, cryst%ntypat, cryst%typat, cryst%amu, eigvec_qbz, displ_cart_qbz)
    3324              : 
    3325              :  ! Compute phonon displacements in reduced coordinates.
    3326         4777 :  if (present(displ_red_qbz)) call phdispl_cart2red(cryst%natom, cryst%gprimd, displ_cart_qbz, displ_red_qbz)
    3327              : 
    3328         4777 : end subroutine pheigvec_rotate
    3329              : !!***
    3330              : 
    3331              : !----------------------------------------------------------------------
    3332              : 
    3333              : !!****f* m_phonons/phstore_init
    3334              : !! NAME
    3335              : !! phstore_init
    3336              : !!
    3337              : !! FUNCTION
    3338              : !!  Create new object with phonon quantities in the IBZ.
    3339              : !!
    3340              : !! INPUTS
    3341              : !!  cryst: Crystal structure
    3342              : !!  ifc: Interatomic force constants.
    3343              : !!  nqibz: Number of q-points in the IBZ.
    3344              : !!  qibz: q-points in the IBZ
    3345              : !!  use_ifc_fourq:  True to replace symmetrization with call to ifc_fourq (debugging option)
    3346              : !!  comm: MPI communicator in which phonon arrays in the IBZ will be MPI distributed.
    3347              : 
    3348           58 : subroutine phstore_init(new, cryst, ifc, nqibz, qibz, use_ifc_fourq, comm)
    3349              : 
    3350              : !Arguments ------------------------------------
    3351              :  class(phstore_t),intent(out) :: new
    3352              :  type(crystal_t),intent(in) :: cryst
    3353              :  type(ifc_type),intent(in) :: ifc
    3354              :  integer,intent(in) :: nqibz, comm
    3355              :  logical,intent(in) :: use_ifc_fourq
    3356              :  real(dp),target,intent(in) :: qibz(3, nqibz)
    3357              : 
    3358              : !Local variables ------------------------------
    3359              : !scalars
    3360              :  integer :: natom3, my_q1, my_q2, iq_ibz
    3361              :  character(len=500) :: msg
    3362              : ! *************************************************************************
    3363              : 
    3364           58 :  new%qibz => qibz
    3365              : 
    3366           58 :  new%natom = cryst%natom; natom3 = cryst%natom * 3; new%natom3 = natom3
    3367           58 :  new%comm = comm; new%nprocs = xmpi_comm_size(comm); new%my_rank = xmpi_comm_rank(comm)
    3368           58 :  new%use_ifc_fourq = use_ifc_fourq
    3369              : 
    3370          232 :  ABI_MALLOC(new%displ_cart, (2, 3, cryst%natom, natom3))
    3371          232 :  ABI_MALLOC(new%phfrq, (3*cryst%natom))
    3372           58 :  if (new%use_ifc_fourq) return
    3373              : 
    3374              :  ! Split qibz in blocks inside comm
    3375          174 :  ABI_MALLOC(new%qibz_start, (0:new%nprocs-1))
    3376          116 :  ABI_MALLOC(new%qibz_stop, (0:new%nprocs-1))
    3377           58 :  call xmpi_split_work2_i4b(nqibz, new%nprocs, new%qibz_start, new%qibz_stop)
    3378              : 
    3379           58 :  my_q1 = new%qibz_start(new%my_rank)
    3380           58 :  my_q2 = new%qibz_stop(new%my_rank)
    3381              : 
    3382           58 :  call wrtout(std_out, " Computing all phonon frequencies and eigenvectors in the IBZ.", pre_newlines=1)
    3383           58 :  call wrtout(std_out, sjoin(" Number of IBZ q-points stored by this rank inside pert_comm:", itoa(my_q2 - my_q1 + 1)))
    3384              :  write(msg, "(a,f8.1,a)") &
    3385           58 :    " Memory required by pheigvec_qibz: ", 2 * natom3**2 * (my_q2 - my_q1 + 1) * dp * b2Mb, " [Mb] <<< MEM"
    3386           58 :  call wrtout(std_out, msg)
    3387              : 
    3388          232 :  ABI_MALLOC(new%phfreqs_qibz, (natom3, my_q1:my_q2))
    3389          290 :  ABI_MALLOC(new%pheigvec_qibz, (2, natom3, natom3, my_q1:my_q2))
    3390              : 
    3391         4048 :  do iq_ibz=my_q1, my_q2
    3392              :    call ifc%fourq(cryst, qibz(:,iq_ibz), new%phfreqs_qibz(:, iq_ibz), new%displ_cart, &
    3393         4048 :                   out_eigvec=new%pheigvec_qibz(:,:,:,iq_ibz))
    3394              :  end do
    3395              : 
    3396              : end subroutine phstore_init
    3397              : !!***
    3398              : 
    3399              : !----------------------------------------------------------------------
    3400              : 
    3401              : !!****f* m_phonons/phstore_free
    3402              : !! NAME
    3403              : !! phstore_free
    3404              : !!
    3405              : !! FUNCTION
    3406              : !!  Free dynamic memory.
    3407              : !!
    3408              : !! INPUTS
    3409              : 
    3410           58 : subroutine phstore_free(self)
    3411              : 
    3412              : !Arguments ------------------------------------
    3413              :  class(phstore_t),intent(inout) :: self
    3414              : ! *************************************************************************
    3415              : 
    3416           58 :  ABI_SFREE(self%qibz_start)
    3417           58 :  ABI_SFREE(self%qibz_stop)
    3418           58 :  ABI_SFREE(self%phfreqs_qibz)
    3419           58 :  ABI_SFREE(self%pheigvec_qibz)
    3420           58 :  ABI_SFREE(self%displ_cart)
    3421           58 :  ABI_SFREE(self%phfrq)
    3422              : 
    3423           58 :  self%qibz => null()
    3424              : 
    3425           58 : end subroutine phstore_free
    3426              : !!***
    3427              : 
    3428              : !----------------------------------------------------------------------
    3429              : 
    3430              : !!****f* m_phonons/phstore_async_rotate
    3431              : !! NAME
    3432              : !! phstore_async_rotate
    3433              : !!
    3434              : !! FUNCTION
    3435              : !!  Begin non-blocking collective MPI communication inside self%comm to obtain
    3436              : !!  phonon frequencies and eigenvectors in the BZ from data in the IBZ.
    3437              : !!
    3438              : !! INPUTS
    3439              : 
    3440         4852 : subroutine phstore_async_rotate(self, cryst, ifc, iq_ibz, qpt_ibz, qpt_bz, isym_q, trev_q)
    3441              : 
    3442              : !Arguments ------------------------------------
    3443              :  class(phstore_t),intent(inout) :: self
    3444              :  type(crystal_t), intent(in) :: cryst
    3445              :  type(ifc_type),intent(in) :: ifc
    3446              :  integer,intent(in) :: iq_ibz, isym_q, trev_q
    3447              :  real(dp),intent(in) :: qpt_ibz(3), qpt_bz(3)
    3448              : 
    3449              : !Local variables ------------------------------
    3450              : !scalars
    3451              :  integer :: rank, master, ierr
    3452              :  logical :: isirr_q
    3453         4852 :  real(dp) :: eigvec_qpt(2, self%natom3, self%natom3)
    3454              : ! *************************************************************************
    3455              : 
    3456              :  ABI_UNUSED(qpt_ibz(1))
    3457              : 
    3458         4852 :  if (self%use_ifc_fourq) then
    3459              :    ! Debugging section.
    3460            0 :    call ifc%fourq(cryst, qpt_bz, self%phfrq, self%displ_cart); return
    3461              :  end if
    3462              : 
    3463              :  ! Find the MPI rank storing the q-point in the IBZ.
    3464         4852 :  do rank=0,self%nprocs-1
    3465         4852 :    if (iq_ibz >= self%qibz_start(rank) .and. iq_ibz <= self%qibz_stop(rank)) then
    3466         4852 :      master = rank; exit
    3467              :    end if
    3468              :  end do
    3469         4852 :  ABI_CHECK(rank /= self%nprocs, sjoin("Nobody has iq_ibz: ", itoa(iq_ibz)))
    3470              : 
    3471              :  ! Begin non-blocking communication for phfrq frequencies.
    3472        36260 :  if (self%my_rank == master) self%phfrq = self%phfreqs_qibz(:, iq_ibz)
    3473         4852 :  call xmpi_ibcast(self%phfrq, master, self%comm, self%requests(1), ierr)
    3474              : 
    3475              :  ! Rotate eigvectors at q_ibz to get eigenvector at q_bz
    3476              :  ! Don't test if umklapp == 0 because we use the periodic gauge:
    3477              :  !
    3478              :  !   phfreq(q+G) = phfreq(q) and eigvec(q) = eigvec(q+G)
    3479              :  !
    3480         4852 :  isirr_q = isym_q == 1 .and. trev_q == 0
    3481              : 
    3482         4852 :  if (self%my_rank == master) then
    3483              :    ! I own the data --> operate on it
    3484         4852 :    if (isirr_q) then
    3485              :      ! q in IBZ --> no rotation is needed.
    3486              :      call phdispl_from_eigvec(cryst%natom, cryst%ntypat, cryst%typat, cryst%amu, &
    3487         2014 :                               self%pheigvec_qibz(:,:,:,iq_ibz), self%displ_cart)
    3488              :    else
    3489              :      ! q in BZ --> rotate phonon eigenvectors.
    3490              :      call pheigvec_rotate(cryst, self%qibz(:, iq_ibz), isym_q, trev_q, self%pheigvec_qibz(:,:,:,iq_ibz), &
    3491         2838 :                           eigvec_qpt, self%displ_cart)
    3492              :    end if
    3493              :  end if
    3494              : 
    3495              :  ! Begin non-blocking bcast for displ_cart. Caller must wait (use phstore_wait)
    3496         4852 :  call xmpi_ibcast(self%displ_cart, master, self%comm, self%requests(2), ierr)
    3497              : 
    3498              : end subroutine phstore_async_rotate
    3499              : !!***
    3500              : 
    3501              : !----------------------------------------------------------------------
    3502              : 
    3503              : !!****f* m_phonons/phstore_wait
    3504              : !! NAME
    3505              : !! phstore_wait
    3506              : !!
    3507              : !! FUNCTION
    3508              : !!  Wait from non-blocking MPI BCAST started in phstore_async_rotate,
    3509              : !!  returns phonon frequencies and displacements in Cartesian and reduced coordinates.
    3510              : !!
    3511              : !! INPUTS
    3512              : 
    3513         4852 : subroutine phstore_wait(self, cryst, phfrq, displ_cart, displ_red)
    3514              : 
    3515              : !Arguments ------------------------------------
    3516              :  class(phstore_t),intent(inout) :: self
    3517              :  type(crystal_t),intent(in) :: cryst
    3518              :  real(dp) ABI_ASYNC, intent(out) :: phfrq(self%natom3)
    3519              :  real(dp) ABI_ASYNC, intent(out) :: displ_cart(2, 3, self%natom, self%natom3)
    3520              :  real(dp),intent(out) :: displ_red(2, 3, self%natom, self%natom3)
    3521              : 
    3522              : !Local variables ------------------------------
    3523              :  integer :: ierr
    3524              : ! *************************************************************************
    3525              : 
    3526         4852 :  if (.not. self%use_ifc_fourq) call xmpi_waitall(self%requests, ierr)
    3527        31408 :  phfrq = self%phfrq
    3528       536968 :  displ_cart = self%displ_cart
    3529         4852 :  call phdispl_cart2red(cryst%natom, cryst%gprimd, displ_cart, displ_red)
    3530              : 
    3531         4852 : end subroutine phstore_wait
    3532              : !!***
    3533              : 
    3534              : !----------------------------------------------------------------------
    3535              : 
    3536              : !!****f* m_phonons/test_phrotation
    3537              : !! NAME
    3538              : !! test_phrotation
    3539              : !!
    3540              : !! FUNCTION
    3541              : !!  Test the symmetrization of the phonon eigenvalues and eigenvectors.
    3542              : !!
    3543              : !! INPUT
    3544              : !!  cryst=Crystalline structure
    3545              : !!  ifc<ifc_type>=interatomic force constants and corresponding real space grid info.
    3546              : !!  ngqpt(3)=Divisions of the ab-initio q-mesh.
    3547              : !!  qptopt=option for the generation of q points (defines whether spatial symmetries and/or time-reversal can be used)
    3548              : !!  comm= MPI communicator
    3549              : 
    3550            0 : subroutine test_phrotation(ifc, cryst, qptopt, ngqpt, comm)
    3551              : 
    3552              : !Arguments ------------------------------------
    3553              :  type(ifc_type),intent(in) :: ifc
    3554              :  type(crystal_t),intent(in) :: cryst
    3555              :  integer,intent(in) :: qptopt, comm, ngqpt(3)
    3556              : 
    3557              : !Local variables-------------------------------
    3558              : !scalars
    3559              :  integer,parameter :: nqshft1 = 1, master = 0
    3560              :  integer :: nqibz, iq_bz, iq_ibz, nqbz, ii, natom, natom3, ierr
    3561              :  integer :: isym, itimrev, ierr_freq, ierr_eigvec, prtvol
    3562              :  real(dp), parameter ::  tol_phfreq_meV = tol3, tol_eigvec = tol6
    3563              :  real(dp) :: maxerr_phfreq, err_phfreq, maxerr_eigvec ! err_eigvec
    3564              :  logical :: isirr_q
    3565              :  character(len=500) :: msg, fmt_freqs, fmt_eigvec
    3566            0 :  type(krank_t) :: qrank
    3567              : !arrays
    3568              :  integer :: in_qptrlatt(3,3), new_qptrlatt(3,3), g0(3)
    3569            0 :  integer,allocatable :: bz2ibz(:,:), bz2ibz_listkk(:,:), toinv(:,:)
    3570            0 :  real(dp) :: qshift(3, nqshft1), phfrq(3*cryst%natom), work(3*cryst%natom)
    3571            0 :  real(dp) :: eigvec_out(2,3*cryst%natom,3*cryst%natom) !eigvec_ibz(2,3*cryst%natom,3*cryst%natom),
    3572            0 :  real(dp) :: eigvec_bz(2,3*cryst%natom,3*cryst%natom), displ_cart_qbz(2,3*cryst%natom,3*cryst%natom)
    3573            0 :  real(dp) :: d2cart(2,3*cryst%natom,3*cryst%natom), d2tmp(2,3*cryst%natom,3*cryst%natom)
    3574            0 :  real(dp),allocatable :: wtq_ibz(:), qbz(:,:), qibz(:,:), displ_cart(:,:,:,:),displ_red(:,:,:,:)
    3575            0 :  real(dp),allocatable :: phfreqs_qibz(:,:), displ_cart_ibz(:,:,:,:),eigvec_ibz(:,:,:,:)
    3576              : !************************************************************************
    3577              : 
    3578            0 :  if (xmpi_comm_rank(comm) /= 0) return
    3579              : 
    3580            0 :  call wrtout(std_out, sjoin(" Testing symmetrization of phonon frequencies and eigenvectors with ngqpt:", ltoa(ngqpt)), ch10)
    3581            0 :  prtvol = 1; natom = cryst%natom; natom3 = cryst%natom * 3
    3582              : 
    3583              :  ! Create a regular grid
    3584            0 :  in_qptrlatt = 0; in_qptrlatt(1, 1) = ngqpt(1); in_qptrlatt(2, 2) = ngqpt(2); in_qptrlatt(3, 3) = ngqpt(3)
    3585            0 :  qshift = zero
    3586              : 
    3587              :  call kpts_ibz_from_kptrlatt(cryst, in_qptrlatt, qptopt, nqshft1, qshift, &
    3588            0 :                              nqibz, qibz, wtq_ibz, nqbz, qbz, new_kptrlatt=new_qptrlatt, bz2ibz=bz2ibz)
    3589            0 :  ABI_FREE(bz2ibz)
    3590              : 
    3591              :  !write(std_out, "(2(a, i0))")" nqibz: ", nqibz, ", nqbz:", nqbz
    3592              :  !write(std_out, "(a)") " qibz_list:"
    3593              :  !do iq_ibz=1,nqibz
    3594              :  !  write(std_out, "(a)")trim(ltoa(qibz(:,iq_ibz)))
    3595              :  !end do
    3596              :  !call cryst%print(unit=std_out)
    3597              :  !write(std_out, *)""
    3598              : 
    3599              :  ! Compute BZ --> IBZ mapping.
    3600            0 :  ABI_MALLOC(bz2ibz_listkk, (6, nqbz))
    3601              : 
    3602            0 :  call qrank%from_kptrlatt(nqibz, qibz, in_qptrlatt, compute_invrank=.False.)
    3603              : 
    3604            0 :  if (kpts_map("symrec", qptopt, cryst, qrank, nqbz, qbz, bz2ibz_listkk) /= 0) then
    3605            0 :    write(msg, '(3a)' ) "Error mapping BZ to IBZ",ch10,"The q-point could not be generated from a symmetrical one"
    3606            0 :    ABI_ERROR(msg)
    3607              :  end if
    3608              : 
    3609            0 :  call qrank%free()
    3610              : 
    3611              :  ! Compute ph freqs in the IBZ.
    3612            0 :  ABI_CALLOC(phfreqs_qibz, (natom3, nqibz))
    3613            0 :  ABI_CALLOC(displ_cart_ibz, (2, natom3, natom3, nqibz))
    3614            0 :  ABI_CALLOC(eigvec_ibz, (2, natom3, natom3, nqibz))
    3615              : 
    3616            0 :  do iq_ibz=1,nqibz
    3617              :    call ifc%fourq(cryst, qibz(:,iq_ibz), phfreqs_qibz(:,iq_ibz), displ_cart_ibz(:,:,:,iq_ibz), &
    3618            0 :                   out_eigvec=eigvec_ibz(:,:,:,iq_ibz))
    3619              :  end do
    3620              : 
    3621            0 :  ABI_MALLOC(displ_cart, (2, 3, cryst%natom, natom3))
    3622            0 :  ABI_MALLOC(displ_red, (2, 3, cryst%natom, natom3))
    3623              : 
    3624            0 :  ABI_MALLOC(toinv, (4, cryst%nsym))
    3625            0 :  call sg_multable(cryst%nsym, cryst%symafm, cryst%symrel, ierr, toinv=toinv, tnons=cryst%tnons, tnons_tol=tol6)
    3626            0 :  ABI_CHECK(ierr == 0, "sg_multable returned ierr != 0")
    3627              : 
    3628              :  ! Precompute ph freqs in the BZ and compare with BZ
    3629            0 :  ierr_freq = 0; ierr_eigvec = 0
    3630            0 :  fmt_freqs = sjoin("(a, ", itoa(natom3), "(f7.3, 1x))")
    3631            0 :  fmt_eigvec = sjoin("(a, i0, 1x, a, ", itoa(natom3), "(f12.9, 1x))")
    3632            0 :  maxerr_phfreq = zero; maxerr_eigvec = zero
    3633              : 
    3634            0 :  do iq_bz=1,nqbz
    3635            0 :    call ifc%fourq(cryst, qbz(:, iq_bz), phfrq, displ_cart, out_eigvec=eigvec_bz, out_d2cart=d2cart)
    3636              : 
    3637            0 :    iq_ibz = bz2ibz_listkk(1, iq_bz); isym = bz2ibz_listkk(2, iq_bz)
    3638            0 :    itimrev = bz2ibz_listkk(6, iq_bz); g0 = bz2ibz_listkk(3:5, iq_bz)
    3639              :    isirr_q = isym == 1 .and. itimrev == 0 .and. all(g0 == 0)
    3640              : 
    3641              :    ! Compare phfreqs within tol in meV.
    3642            0 :    err_phfreq = maxval(abs(phfrq - phfreqs_qibz(:, iq_ibz))) * Ha_meV
    3643            0 :    if (err_phfreq > tol_phfreq_meV) then
    3644            0 :      maxerr_phfreq = max(maxerr_phfreq, err_phfreq)
    3645            0 :      write(std_out,*)" " // repeat("=", 92)
    3646            0 :      write(std_out, "(4a)")" qbz:", trim(ktoa(qbz(:, iq_bz))), " --> qibz:", trim(ktoa(qibz(:, iq_ibz)))
    3647            0 :      write(std_out, fmt_freqs)" w_bz :", phfrq * Ha_meV
    3648            0 :      write(std_out, fmt_freqs)" w_ibz:", phfreqs_qibz(:, iq_ibz) * Ha_meV
    3649            0 :      write(std_out,*)" err_phfreq (meV):", err_phfreq, " > tol: ", tol_phfreq_meV
    3650            0 :      write(std_out,*)" " // repeat("=", 92)
    3651            0 :      ierr_freq = ierr_freq + 1
    3652              :    end if
    3653              : 
    3654              :    ! Rotate and compare eigenvectors
    3655              :    call pheigvec_rotate(cryst, qibz(:, iq_ibz), isym, itimrev, eigvec_ibz(:,:,:,iq_ibz), &
    3656            0 :                         eigvec_out, displ_cart_qbz)
    3657              : 
    3658              :    ! e^H D e = w**2 I
    3659            0 :    call massmult_and_breaksym(natom, cryst%ntypat, cryst%typat, cryst%amu, d2cart)
    3660            0 :    call cg_zgemm("N", "N", natom3, natom3, natom3, d2cart, eigvec_out, d2tmp)
    3661            0 :    call cg_zgemm("C", "N", natom3, natom3, natom3, eigvec_out, d2tmp, d2cart)
    3662            0 :    do ii=1,natom3
    3663            0 :      work(ii) = d2cart(1, ii, ii)
    3664              :    end do
    3665            0 :    work = sqrt(abs(phfrq ** 2 - work)) * Ha_meV
    3666            0 :    if (maxval(work) > tol_phfreq_meV) ierr_eigvec = ierr_eigvec + 1
    3667            0 :    write(std_out, *) "max eig_diff [meV]: ", maxval(work)
    3668            0 :    write(std_out, "(a)")" e^H D e (meV**2)"
    3669            0 :    d2cart = d2cart * Ha_meV ** 2
    3670            0 :    call print_arr([std_out], reshape(cmplx(d2cart(1,:,:), d2cart(2,:,:), kind=dp), [natom3, natom3]))
    3671              : 
    3672              :    !err_eigvec = maxval(abs(eigvec_out - eigvec_bz))
    3673              :    !if (err_eigvec > tol_eigvec) then
    3674              :    !  maxerr_eigvec = max(maxerr_eigvec, err_eigvec)
    3675              :    !  write(std_out, "(4a)")" qbz:", trim(ktoa(qbz(:, iq_bz))), " --> qibz: ", trim(ktoa(qibz(:, iq_ibz)))
    3676              :    !  write(std_out, "(a,2(i0,1x),a)")" qbz image through isym, itimrev: ", isym, itimrev, trim(ltoa(cryst%tnons(:, isym)))
    3677              :    !  write(std_out, "(a,l1)")" has_r0: ", any(cryst%indsym(1:3, isym, :) /= 0)
    3678              :    !  write(std_out, *) "err_eigvec ", err_eigvec, " > tol:", tol_eigvec
    3679              :    !  do ii=1,natom3
    3680              :    !    if (all(abs(eigvec_out(:,:,ii) - eigvec_bz(:,:,ii)) < tol_eigvec)) cycle
    3681              :    !    if (prtvol > 0) then
    3682              :    !      write(std_out, "(a, 2(f12.9,1x))") " diff (re/im): ", &
    3683              :    !        maxval(abs(eigvec_out(1,:,ii) - eigvec_bz(1,:,ii))), maxval(abs(eigvec_out(2,:,ii) - eigvec_bz(2,:,ii)))
    3684              :    !      write(std_out, fmt_eigvec) " mode: ", ii, "re_sym: ", eigvec_out(1,:,ii)
    3685              :    !      write(std_out, fmt_eigvec) " mode: ", ii, "re_bz : ", eigvec_bz(1,:,ii)
    3686              :    !      write(std_out, fmt_eigvec) " mode: ", ii, "im_sym: ", eigvec_out(2,:,ii)
    3687              :    !      write(std_out, fmt_eigvec) " mode: ", ii, "im_bz : ", eigvec_bz(2,:,ii)
    3688              :    !    end if
    3689              :    !  end do
    3690              :    !  write(std_out,*)" " // repeat("=", 92)
    3691              :    !  ierr_eigvec = ierr_eigvec + 1
    3692              :    !end if
    3693              : 
    3694              :  end do
    3695              : 
    3696            0 :  write(std_out,*)" === Final results ==="
    3697            0 :  write(std_out,*)" maxerr_phfreq (meV): ", maxerr_phfreq
    3698            0 :  write(std_out,*)" percentage of erroneous q-points for phfreq: ", ierr_freq / (one * nqbz) * 100, "%"
    3699            0 :  write(std_out,*)""
    3700              :  !write(std_out,*)" maxerr_eigvec: ", maxerr_eigvec
    3701            0 :  write(std_out,*)" percentage of q-points for eigvec: ", ierr_eigvec / (one * nqbz) * 100, "%"
    3702            0 :  write(std_out,*)""
    3703              : 
    3704            0 :  if (ierr_freq /= 0) then
    3705            0 :    ABI_ERROR("Wrong symmetrization in phonon eigenvalues.")
    3706            0 :  else if (ierr_eigvec /= 0) then
    3707            0 :    ABI_ERROR("Wrong symmetrization in phonon eigenvectors.")
    3708              :  else
    3709            0 :    write(std_out, "(a)")" ALL OK: No error detected!"
    3710              :  end if
    3711              : 
    3712            0 :  ABI_FREE(displ_cart)
    3713            0 :  ABI_FREE(displ_red)
    3714            0 :  ABI_SFREE(bz2ibz_listkk)
    3715            0 :  ABI_SFREE(qbz)
    3716            0 :  ABI_SFREE(qibz)
    3717            0 :  ABI_SFREE(wtq_ibz)
    3718            0 :  ABI_FREE(phfreqs_qibz)
    3719            0 :  ABI_FREE(displ_cart_ibz)
    3720            0 :  ABI_FREE(eigvec_ibz)
    3721            0 :  ABI_FREE(toinv)
    3722              : 
    3723            0 : end subroutine test_phrotation
    3724              : !!***
    3725              : 
    3726         1229 : end module m_phonons
    3727              : !!***
        

Generated by: LCOV version 2.3-1