From 86978a320e326df3bfb28314d69d50b70fe3eaa9 Mon Sep 17 00:00:00 2001
From: Nicolas Essing <n.essing@fz-juelich.de>
Date: Fri, 8 Oct 2021 13:05:51 +0200
Subject: [PATCH] Fixed a bug concerning the initialization of the magnetic
 fields.

In CalculationData, the bfield is read for every atom and then initialized
only for the locally treated atoms. Thus, the bfield has to be accessed
differently from the other quantities that are only allocated for the
local atoms.

Also, I restructured and commented lots of code while looking for the bug.
---
 source/KKRnano/source/CalculationData_mod.F90 |   8 +-
 source/KKRnano/source/bfield/bfield.f90       | 223 ++++++++----------
 2 files changed, 102 insertions(+), 129 deletions(-)

diff --git a/source/KKRnano/source/CalculationData_mod.F90 b/source/KKRnano/source/CalculationData_mod.F90
index 9b7d382f8..325be4c22 100644
--- a/source/KKRnano/source/CalculationData_mod.F90
+++ b/source/KKRnano/source/CalculationData_mod.F90
@@ -387,12 +387,12 @@ module CalculationData_mod
     ! For the local atoms, initialize some fields
     do ila = 1, self%num_local_atoms
       atom_id = self%atom_ids(ila)
+      ! Beware: self%bfields is allocated and saved for all atoms
       call init_bfield(params%noncobfield, params%constr_field, self%bfields(atom_id), dims%lmaxd, &
-                       self%cheb_mesh_a(atom_id)%npan_tot, self%cheb_mesh_a(atom_id)%npan_lognew, &
-                       self%cheb_mesh_a(atom_id)%npan_eqnew, self%cheb_mesh_a(atom_id)%ncheb, &
-                       self%cheb_mesh_a(atom_id)%ipan_intervall, self%cheb_mesh_a(atom_id)%thetasnew, &
+                       self%cheb_mesh_a(ila)%npan_lognew, self%cheb_mesh_a(ila)%npan_eqnew, &
+                       self%cheb_mesh_a(ila)%ipan_intervall, self%cheb_mesh_a(ila)%thetasnew, &
                        self%gaunts%iend, self%gaunts%icleb,  self%gaunts%cleb(:,1), &
-                       self%cell_a(atom_id)%ifunm)
+                       self%cell_a(ila)%ifunm)
     end do
 
   endsubroutine ! constructEverything
diff --git a/source/KKRnano/source/bfield/bfield.f90 b/source/KKRnano/source/bfield/bfield.f90
index 48382a9a7..5c62f2273 100644
--- a/source/KKRnano/source/bfield/bfield.f90
+++ b/source/KKRnano/source/bfield/bfield.f90
@@ -7,9 +7,8 @@
 !------------------------------------------------------------------------------------
 !> Summary: Module storing the run options and the paramters for bfields and constraining fields
 !> 
-!> Written by Sascha Brinker, ported from KKRhost to KKRnano by the other two.
-!> 
 !> Author: Sascha Brinker, Eduardo Mendive, Nicolas Essing
+!>   Written by Sascha Brinker, ported from KKRhost to KKRnano by the other two.
 !------------------------------------------------------------------------------------
 module mod_bfield
   
@@ -58,16 +57,14 @@ contains
   end subroutine
 
   subroutine init_bfield(lbfield, lbfield_constr, bfield, lmax, &
-                         npan_tot, npan_log, npan_eq, ncheb, ipan_intervall, thetasnew, &
+                         npan_log, npan_eq, ipan_intervall, thetasnew, &
                          iend, icleb, cleb, ifunm)
     logical, intent(in) :: lbfield        !! Whether to use external nonco bfields
     logical, intent(in) :: lbfield_constr !! Whether to use constraint bfields
     type(bfield_data), intent(inout) :: bfield !! The bfield data
     integer, intent(in) :: lmax     !! Angular momentum cutoff
-    integer, intent(in) :: npan_tot !! Chebyshev mesh resolution
     integer, intent(in) :: npan_log !! Chebyshev mesh resolution
     integer, intent(in) :: npan_eq  !! Chebyshev mesh resolution
-    integer, intent(in) :: ncheb    !! Chebyshev mesh resolution
     integer, dimension(:), intent(in) :: ipan_intervall !! Indices for important radial points in the mesh
     double precision, dimension(:,:), intent(in)  :: thetasnew !! Interpolated shape function in Chebychev mesh
     integer,                          intent (in) :: iend      !! Number of nonzero gaunt coefficients
@@ -79,9 +76,7 @@ contains
     integer :: i_stat
 
     ncleb = size(cleb)
-    irmdnew = npan_tot*(ncheb+1)
-
-    !TODO? assert size(theasnew) == irmdnew
+    irmdnew = size(thetasnew, 1) ! npan_tot*(ncheb+1)
 
     ! Calculate the LL' expansion of the shape function in the new mesh which
     ! is needed to convolute the magnetic field (only done once and stored to
@@ -90,7 +85,7 @@ contains
     if(lbfield .or. lbfield_constr) then
       allocate (bfield%thetallmat((lmax+1)**2,(lmax+1)**2,irmdnew), stat=i_stat)
       call calc_thetallmat(bfield%thetallmat, lmax, ipan_intervall(npan_log+npan_eq) + 1, &
-                            iend, irmdnew, thetasnew, ifunm, icleb, cleb)
+                           iend, irmdnew, thetasnew, ifunm, icleb, cleb)
     end if
 
   end subroutine init_bfield
@@ -99,11 +94,11 @@ contains
 
   !-------------------------------------------------------------------------------
   !> Summary: Reads the atom-wise constraining field from bconstr.dat
-  !> Author: MdSD
-  !> Category: KKRhost, bfield
-  !> Deprecated: False
-  !> the file has the format:
-  !> bz  by  bz (in Ry), mspin (in muB)
+  !> Author: MdSD, Nicolas Essing
+  !>
+  !> The file should contain three floating point values in each line and a line
+  !> for each atom. The first line is treated as a header and skipped.
+  !> Intepreted as initial constraining bfield in cartesian coordinates, in Ry.
   !-------------------------------------------------------------------------------
   subroutine read_bconstr(bfields, number_of_atoms)
     type(bfield_data), dimension(:), intent(inout) :: bfields
@@ -111,24 +106,23 @@ contains
     
     integer :: iatom
     integer :: iostat
-   
-    open(unit=57493215, file='bconstr_in.dat', iostat=iostat)
-    if (iostat == 0) then 
-      read(57493215,*)  ! skip header
+    logical :: file_exists
+
+    inquire(file='bconstr_in.dat', exist=file_exists)
+
+    if (file_exists) then
+      open(unit=57493215, file='bconstr_in.dat', iostat=iostat)
+      read(57493215, *)  ! skip header
       do iatom = 1, number_of_atoms
-        read(57493215,*,iostat=iostat) bfields(iatom)%bfield_constr(:)
-        if (iostat > 0) then
-          ! iostat should be zero normally and negative in last line, positive is error
+        read(57493215, *, iostat=iostat) bfields(iatom)%bfield_constr(:)
+        if (iostat /= 0) then
           write(*,*) "Error reading bconstr_in.dat"
           stop
         end if
       end do
-      if (iostat >= 0) then
-        ! still zero means there are more lines
-        write(*,*) "Error reading bconstr_in.dat or file too long"
-      end if
       close(57493215)
     else
+      ! No 'bconstr_in.dat' given, use default 0
       do iatom = 1, number_of_atoms
         bfields(iatom)%bfield_constr(:) = 0.
       end do
@@ -146,11 +140,12 @@ contains
 
   !-------------------------------------------------------------------------------
   !> Summary: Reads the atom-wise magnetic field from bfield.dat
-  !> Author: Sascha Brinker
-  !> Category: KKRhost, bfield
-  !> Deprecated: False
-  !> the file has the format:
-  !> theta  phi  bfield_strength (in Tesla)
+  !> Author: Nicolas Essing
+  !> 
+  !> The file should contain three numbers per line and one line per atom.
+  !> Read as 'theta  phi  bfield_strength' with the angles in degrees and the
+  !> strength in Ry ( ! might get changed to Tesla ! )
+  !> Lines can be commented out with a # as first character.
   !-------------------------------------------------------------------------------
   subroutine read_bfield(bfields,number_of_atoms)
     type(bfield_data), dimension(:), intent(inout) :: bfields
@@ -166,22 +161,23 @@ contains
       stop
     end if
 
+    ! manual loop over iatom because comments are allowed
     iatom = 1
-    do while ((iostat == 0) .and. (iatom <= number_of_atoms))
-      read(57493215, *, iostat=iostat) linebuffer
-      if (linebuffer(1:1) == '#') continue ! input line commented out, get next line
+    do while (iatom <= number_of_atoms)
+      read(57493215, '(A)', iostat=iostat) linebuffer
+      if (iostat /= 0) then
+        write(*,*) "I/O Error while reading bfield.dat"
+        stop
+      end if
+      if (linebuffer(1:1) == '#') cycle ! input line commented out
       read(linebuffer, *, iostat=iostat) bfields(iatom)%theta, bfields(iatom)%phi, bfields(iatom)%bfield_strength
       if (iostat /= 0) then
-        write(*,*) "Malformatted line in bfield.dat."
+        write(*,*) "Error parsing a line in bfield.dat"
         stop
       end if
       iatom = iatom + 1
     end do
     close(57493215)
-    if (iatom <= number_of_atoms + 1) then
-      write(*,*) "Error reading bfield.dat or file too short"
-      stop
-    end if
 
     write(*,*) '  ###############################################'
     write(*,*) '  external non-collinear magnetic fields'
@@ -194,7 +190,7 @@ contains
       bfields(iatom)%bfield(1)       = bfields(iatom)%bfield_strength*cos(bfields(iatom)%phi)*sin(bfields(iatom)%theta)
       bfields(iatom)%bfield(2)       = bfields(iatom)%bfield_strength*sin(bfields(iatom)%phi)*sin(bfields(iatom)%theta)
       bfields(iatom)%bfield(3)       = bfields(iatom)%bfield_strength*cos(bfields(iatom)%theta)
-      write(*,'("  ",i4,3es16.8)') iatom, bfields(iatom)%theta, bfields(iatom)%phi, bfields(iatom)%bfield_strength
+      write(*,'(2X,I4,3(E16.8))') iatom, bfields(iatom)%theta, bfields(iatom)%phi, bfields(iatom)%bfield_strength
    end do
   end subroutine read_bfield
   
@@ -211,99 +207,76 @@ contains
   !> @endnote
   !------------------------------------------------------------------------------------
   subroutine calc_thetallmat(thetansll, lmax, imt1, iend, irmdnew, thetasnew, ifunm, icleb, cleb)
-    integer                     , intent (in) :: lmax    !! Angular momentum cut-off
-    integer                     , intent (in) :: imt1    !! index muffin-tin radius
-    integer                     , intent (in) :: iend    !! Number of nonzero gaunt coefficients
-    integer                     , intent (in) :: irmdnew !! number of radials point on the Cheby mesh
-    integer, dimension(:,:), intent (in) :: icleb   !! Pointer array
-    double precision, dimension(:),    intent (in)  :: cleb    !! GAUNT coefficients (GAUNT)
-    double precision, dimension(:, :), intent (in)  :: thetasnew !! shapefun on the Cheby mesh
-    integer, dimension(1:(2*lmax+1)**2)                , intent (in)  :: ifunm     !! pointer array for shapefun     ! Check index and dimensions!!!!!
-    double precision, dimension((lmax+1)**2,(lmax+1)**2,irmdnew), intent (out) :: thetansll !! LL' expansion of the shapefunction 
-    !------------------------------------------------------------------------------------
-    double precision,parameter                                  :: rfpi=3.5449077018110318
-    double precision,dimension(1:irmdnew,1:(2*lmax+1)**2)       :: shapefun_mod  
-    double precision                                            :: c0ll
-    integer                                                     :: lmmax
-    integer                                                     :: lmmax2
-    integer                                                     :: ilm
-    integer                                                     :: ifun
-    integer                                                     :: ir
-    integer                                                     :: lm1
-    integer                                                     :: lm2
-    integer                                                     :: lm3
-    integer                                                     :: j
-            
-    c0ll                                                    = 1d0/rfpi
-    lmmax                                                   = (lmax+1)**2
-    lmmax2                                                  = (2*lmax+1)**2
-    shapefun_mod(:,:)                                       = 0.d0
-    shapefun_mod(1:imt1,1)                                  = rfpi ! is multipled by C_LL^0
-    shapefun_mod(imt1+1:irmdnew,1)                          = thetasnew(imt1+1:irmdnew,1)
-    ! convert from pointer to real ilmdo ilm=2,lmmax2
-    do ilm=2,lmmax2
-      ifun = ifunm(ilm)
-      if(.not. ifun == 0) then !shapefun%lmused(ilm)==1) then
-        !if(myrank==master .and. debug) write(17*iatom**2,'(" converted ifun= ",i4," to ilm= ",i4)') ifun, ilm
-        shapefun_mod(imt1+1:irmdnew,ilm)           = thetasnew(imt1+1:irmdnew,ifun)
+    double precision, dimension(:,:,:), intent (out) :: thetansll !! LL' expansion of the shapefunction
+    integer,                            intent (in)  :: lmax      !! Angular momentum cut-off
+    integer,                            intent (in)  :: imt1      !! index muffin-tin radius
+    integer,                            intent (in)  :: iend      !! Number of nonzero gaunt coefficients
+    integer,          dimension(:,:),   intent (in)  :: icleb     !! l and m values for the Gaunt coefficients
+    double precision, dimension(:),     intent (in)  :: cleb      !! Gaunt coefficients
+    double precision, dimension(:, :),  intent (in)  :: thetasnew !! shapefun on the Cheby mesh
+    integer         , dimension(:),     intent (in)  :: ifunm     !! pointer array for shapefun
+
+    double precision, parameter :: rfpi = 3.5449077018110318
+    double precision, parameter :: c0ll = 1.d0 / rfpi
+    integer :: irmdnew       ! number of radials point on the Chebyshev mesh
+    integer :: lmmax         ! number of angular momentum entries
+    integer :: lmmax2
+    integer :: ifun
+    integer :: lm1, lm2, lm3 ! angular momentum indices
+    integer :: ir            ! radial index
+    integer :: j             ! index for loop over Gaunt coeffs
+    double precision, dimension(:,:), allocatable :: shapefun_mod
+
+    lmmax   = (lmax+1)**2
+    lmmax2  = (2*lmax+1)**2
+    irmdnew = size(thetasnew, 1) ! npan_tot*(ncheb+1)
+
+    allocate(shapefun_mod(irmdnew, lmmax2))
+
+    shapefun_mod(:,:)        = 0.d0
+    shapefun_mod(1:imt1,1)   = rfpi ! is multipled by C_LL^0
+    shapefun_mod(imt1+1:, 1) = thetasnew(imt1+1:irmdnew,1)
+
+    ! convert from pointer to real l,m
+    do lm1 = 2, lmmax2
+      ifun = ifunm(lm1)
+      if(.not. ifun == 0) then !shapefun%lmused(lm1)==1) then
+        shapefun_mod(imt1+1:, lm1) = thetasnew(imt1+1:, ifun)
       end if
     end do
-    !if(myrank==master .and. debug) write(17*iatom**2,'("  cellnew%shapefun = ")')
-    !do ilm= 1,lmmax2
-    !  if(sum(abs(thetasnew(:,ilm)))>1e-8) then
-    !    if(myrank==master .and. debug) write(17*iatom**2,'(i4,1000es16.8)') ilm, thetasnew(:,ilm)
-    !  end if
-    !end do
-    !if(myrank==master .and. debug) write(17*iatom**2,'("  shapefun_mod = ")')
-    !do ilm= 1,lmmax2
-    !  if(sum(abs(shapefun_mod(:,ilm)))>1e-8) then
-    !    if(myrank==master .and. debug) write(17*iatom**2,'(i4,1000es16.8)') ilm, shapefun_mod(:,ilm)
-    !  end if
-    !end do
     
-    thetansll(:,:,:)=0.d0
+    thetansll(:,:,:) = 0.d0
+
     ! diagonal part (not contained in gaunt-coeff)
-    do ilm = 1,lmmax
-      thetansll(ilm,ilm,:) = shapefun_mod(:,1)*c0ll
+    do lm1 = 1, lmmax
+      thetansll(lm1,lm1,:) = shapefun_mod(:,1) * c0ll
     end do
-    !write(*,'("ifunm=",1000i4)') ifunm(:)
-    do j = 1,iend !gauntcoeff%iend
-      lm1 = icleb(j, 1)! gauntcoeff%icleb(j,1) ! lmax
-      lm2 = icleb(j, 2)! gauntcoeff%icleb(j,2) ! lmax
-      lm3 = icleb(j, 3)! gauntcoeff%icleb(j,3) ! 2*lmax
-      !write(17*iatom**2,'("lm1,lm2,lm3 =",3i4,2es16.8)') lm1,lm2,lm3,cleb(j)
-      if(lm1<= lmmax .and. lm2 <= lmmax .and. lm3<= lmmax2) then
-        ifun = ifunm(lm3)
-        if(.not. ifun == 0) then !shapefun%lmused(ilm)==1) then
-          !write(*,'("lm1,lm2,lm3,cleb",3i4,10e16.8)') lm1,lm2,lm3,cleb(j)
-          do ir = 1,irmdnew!cellnew%nrmaxnew
-            thetansll(lm1,lm2,ir) = thetansll(lm1,lm2,ir)+cleb(j)*shapefun_mod(ir,lm3)
-            thetansll(lm2,lm1,ir) = thetansll(lm2,lm1,ir)+cleb(j)*shapefun_mod(ir,lm3)
-          end do
-        end if
+
+    ! This is effectivly a summation over one angular momentum index lm3 for each
+    ! combination of lm1, lm2. Iterate instead over the flattened array cleb
+    ! containing the Gaunt coefficients and extract the angular momentum
+    ! indices for each j.
+    do j = 1, iend
+      lm1 = icleb(j, 1)
+      lm2 = icleb(j, 2)
+      lm3 = icleb(j, 3)
+      if (lm1 > lmmax .or. lm2 > lmmax .or. lm3 > lmmax2) then
+        ! I think this should not happen, as icleb should only map to valid values
+        ! for lm1,lm2,lm3.
+        !TODO check that and remove this warning
+        write(*,*) "Warning from calc_thetallmat in bfield.F90: Maybe invalid values in icleb:"
+        write(*,*) j, lm1, lm2, lm3, lmmax, lmmax2
+        cycle ! Gaunt coeffs zero
+      end if
+      if (ifunm(lm3) == 0) then
+        cycle ! Some symmetry related to shapefunction, I think
       end if
+      do ir = 1, irmdnew
+        thetansll(lm1,lm2,ir) = thetansll(lm1,lm2,ir) + cleb(j) * shapefun_mod(ir,lm3)
+        thetansll(lm2,lm1,ir) = thetansll(lm2,lm1,ir) + cleb(j) * shapefun_mod(ir,lm3)
+      end do
     end do
-    !do lm1 = 1,lmmax
-    !  do lm2 = 1,lm1-1
-    !    do ir = 1, irmdnew
-    !      thetansll(ir,lm2,lm1) = thetansll(ir,lm1,lm2)
-    !    end do
-    !  end do
-    !end do
-    !if(myrank==master .and. debug) write(17*iatom**2,'("  thetansll = ")')
-    !do lm1=1,lmmax
-    !  do lm2=1,lmmax
-    !    if(sum(abs(thetansll(:,lm1,lm2)))>1e-8) then
-    !      if(myrank==master .and. debug) write(17*iatom**2,'(2i4,1000es16.8)') lm1, lm2, thetansll(:,lm1,lm2)
-    !    end if
-    !  end do
-    !end do
-              
+
   end subroutine calc_thetallmat
 
 end module mod_bfield
-
-
-
-
-
-- 
GitLab