LCOV - code coverage report
Current view: top level - src/78_effpot - m_slc_potential.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 72.2 % 198 143
Test Date: 2026-09-19 17:42:43 Functions: 83.3 % 12 10

            Line data    Source code
       1              : !!****m* ABINIT/m_slc_potential
       2              : !! NAME
       3              : !! m_slc_potential
       4              : !!
       5              : !! FUNCTION
       6              : !! This module contains the spin-lattice coupling, and the methods for
       7              : !! calculating effective magnetic field (torque), force, and total_energy
       8              : !!
       9              : !!
      10              : !! COPYRIGHT
      11              : !! Copyright (C) 2001-2026 ABINIT group (TO, hexu, NH)
      12              : !! This file is distributed under the terms of the
      13              : !! GNU General Public License, 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              : #include "abi_common.h"
      23              : module  m_slc_potential
      24              :   use defs_basis
      25              :   use m_errors
      26              :   use m_abicore
      27              :   use m_xmpi
      28              : 
      29              :   use m_hashtable_strval, only: hash_table_t
      30              :   use m_abstract_potential, only : abstract_potential_t
      31              :   !use m_dynamic_array, only: int2d_array_type
      32              :   use m_mpi_scheduler, only: mb_mpi_info_t, init_mpi_info, mpi_scheduler_t
      33              :   use m_multibinit_cell, only: mbcell_t, mbsupercell_t
      34              :   use m_multibinit_dataset, only: multibinit_dtset_type
      35              :   use m_spmat_ndcoo, only: ndcoo_mat_t
      36              : 
      37              :   implicit none
      38              :   private
      39              : 
      40              :   type, public, extends(abstract_potential_t) :: slc_potential_t
      41              :      integer :: nspin=0
      42              :      integer :: natom=0
      43              :      logical :: has_bilin=.False.   ! bilinear coupling term, i.e. liu
      44              :      logical :: has_linquad=.False. ! spin first then lattice, i.e. niuv
      45              :      logical :: has_quadlin=.False. ! spin first then lattice, i.e. oiju
      46              :      logical :: has_biquad=.False.  ! biquadratic coupling term, i.e. tijuv
      47              : 
      48              :      type(ndcoo_mat_t) :: liu_sc           ! parameter values bilin term
      49              :      type(ndcoo_mat_t) :: niuv_sc          ! parameter values linquad term
      50              :      type(ndcoo_mat_t) :: oiju_sc          ! parameter values quadlin term
      51              :      type(ndcoo_mat_t) :: tijuv_sc         ! parameter values biquad term
      52              :      type(ndcoo_mat_t) :: tuvij_sc         ! same as tijuv but sorted differently
      53              : 
      54              :      ! magnetic moments
      55              :      real(dp), allocatable :: ms(:)
      56              : 
      57              :      ! precalculated things for reference spin structure
      58              :      real(dp), allocatable :: luref(:) ! force from liu for reference spin structure
      59              :      real(dp), allocatable :: ouref(:) ! force from oiju for reference spin structure
      60              :      type(ndcoo_mat_t) :: nuvref    ! matrix in u and v from niuv for reference spin structure
      61              :      type(ndcoo_mat_t) :: tuvref    ! matrix in u and v from tijuv for reference spin structure
      62              : 
      63              :      ! mpi
      64              :      type(mb_mpi_info_t) :: mpiinfo
      65              :      type(mpi_scheduler_t) :: mpsspin
      66              :      type(mpi_scheduler_t) :: mpslatt
      67              : 
      68              :    CONTAINS
      69              :      procedure :: initialize
      70              :      procedure :: finalize
      71              :      procedure :: set_params
      72              :      procedure :: set_supercell
      73              :      procedure :: calculate_ref
      74              :      procedure :: add_liu_term
      75              :      procedure :: add_niuv_term
      76              :      procedure :: add_oiju_term
      77              :      procedure :: add_tijuv_term
      78              :      procedure :: calculate
      79              :   end type slc_potential_t
      80              : 
      81              : contains
      82              : 
      83            2 :   subroutine initialize(self, nspin, natom)
      84              :     !Arguments ------------------------------------
      85              :     !scalars
      86              :     class(slc_potential_t), intent(inout) :: self
      87              :     integer,                intent(in)    :: nspin
      88              :     integer,                intent(in)    :: natom
      89              : 
      90              :     integer :: master, my_rank, comm, nproc, ierr
      91              :     logical :: iam_master
      92              : 
      93            2 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
      94            2 :     call self%mpsspin%initialize(ntasks=nspin, master=master, comm=comm)
      95            2 :     call self%mpslatt%initialize(ntasks=natom, master=master, comm=comm)
      96            2 :     self%label="SLCPotential"
      97            2 :     self%has_spin=.True.
      98            2 :     self%has_displacement=.True.
      99            2 :     self%has_strain=.False.
     100            2 :     self%is_null=.False.
     101            2 :     self%nspin=nspin
     102            2 :     self%natom=natom
     103              : 
     104            2 :     call xmpi_bcast(self%nspin, master, comm, ierr)
     105            2 :     call xmpi_bcast(self%natom, master, comm, ierr)
     106            6 :     ABI_MALLOC(self%ms, (self%nspin))
     107            2 :   end subroutine initialize
     108              : 
     109            2 :   subroutine finalize(self)
     110              : 
     111              :     class(slc_potential_t), intent(inout):: self
     112            2 :     ABI_SFREE(self%ms)
     113              : 
     114            2 :     if(self%has_bilin) then
     115            0 :       call self%liu_sc%finalize()
     116            0 :       ABI_SFREE(self%luref)
     117              :     endif
     118            2 :     if(self%has_quadlin) then
     119            2 :       call self%oiju_sc%finalize()
     120            2 :       ABI_SFREE(self%ouref)
     121              :     endif
     122            2 :     if(self%has_linquad) then
     123            0 :       call self%niuv_sc%finalize()
     124            0 :       call self%nuvref%finalize()
     125              :     endif
     126            2 :     if(self%has_biquad) then
     127            1 :       call self%tijuv_sc%finalize()
     128            1 :       call self%tuvij_sc%finalize()
     129            1 :       call self%tuvref%finalize()
     130              :     endif
     131              : 
     132            2 :     call self%mpsspin%finalize()
     133            2 :     call self%mpslatt%finalize()
     134              : 
     135            2 :   end subroutine finalize
     136              : 
     137              :   !-------------------------------------------------------------------!
     138              :   !set_params: which coupling terms are used
     139              :   !-------------------------------------------------------------------!
     140            4 :   subroutine set_params(self, params)
     141              :     class(slc_potential_t), intent(inout) :: self
     142              :     type(multibinit_dtset_type), intent(inout) :: params
     143              : 
     144              :     integer :: master, my_rank, comm, nproc, ierr, coupling
     145              :     logical :: iam_master
     146              : 
     147            4 :     coupling = params%slc_coupling
     148              : 
     149            4 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     150              : 
     151            4 :     if(iam_master) then
     152            4 :       if(coupling .ge. 1000) then
     153            2 :         self%has_biquad=.True.
     154            2 :         call xmpi_bcast(self%has_biquad, master, comm, ierr)
     155            2 :         coupling=coupling - 1000
     156              :       endif
     157              : 
     158            4 :       if(coupling .ge. 100) then
     159            0 :         self%has_linquad=.True.
     160            0 :         call xmpi_bcast(self%has_linquad, master, comm, ierr)
     161            0 :         coupling=coupling - 100
     162              :       endif
     163              : 
     164            4 :       if(coupling .ge. 10) then
     165            4 :         self%has_quadlin=.True.
     166            4 :         call xmpi_bcast(self%has_quadlin, master, comm, ierr)
     167            4 :         coupling=coupling - 10
     168              :       endif
     169              : 
     170            4 :       if(coupling .ge. 1) then
     171            0 :         self%has_bilin=.True.
     172            0 :         call xmpi_bcast(self%has_bilin, master, comm, ierr)
     173              :       endif
     174              :     endif
     175            4 :   end subroutine set_params
     176              : 
     177              :   !-------------------------------------------------------------------!
     178              :   !set_supercell: use the same supercell for all terms
     179              :   !               copy magnetic moments
     180              :   !-------------------------------------------------------------------!
     181            8 :   subroutine set_supercell(self, supercell)
     182              :     class(slc_potential_t),      intent(inout) :: self
     183              :     type(mbsupercell_t), target, intent(inout) :: supercell
     184              :     integer :: master, my_rank, comm, nproc, ierr
     185              :     logical :: iam_master
     186              : 
     187            4 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     188              : 
     189            4 :     self%supercell=>supercell
     190          868 :     self%ms(:)=supercell%spin%ms(:)
     191              : 
     192            4 :     call xmpi_bcast(self%ms, master, comm, ierr)
     193            4 :   end subroutine set_supercell
     194              : 
     195              :   !-------------------------------------------------------------------!
     196              :   !calculate ref: calculates terms necessary for the force and energy
     197              :   !               of the reference structure terms
     198              :   !-------------------------------------------------------------------!
     199              : 
     200            2 :   subroutine calculate_ref(self)
     201              :     class(slc_potential_t), intent(inout) :: self
     202              : 
     203            4 :     real(dp) :: spref(1:3*self%nspin), beta
     204              :     real(dp), allocatable :: force(:)
     205              : 
     206              :     integer :: master, my_rank, comm, nproc
     207              :     logical :: iam_master
     208              : 
     209            4 :     spref(:) = reshape(self%supercell%spin%Sref, (/ 3*self%nspin/))
     210              : 
     211            2 :     beta = 0.5_dp
     212              : 
     213            6 :     ABI_MALLOC(force, (3*self%natom))
     214            2 :     if(self%has_bilin) then
     215            0 :       ABI_MALLOC(self%luref, (3*self%natom))
     216            0 :       self%luref=0.0d0
     217            0 :       force = 0.0d0
     218            0 :       call self%liu_sc%vec_product2d(1, spref, 2, force)
     219            0 :       self%luref(:) = - force(:)
     220              :     endif
     221            2 :     if(self%has_quadlin) then
     222            6 :       ABI_MALLOC(self%ouref, (3*self%natom))
     223         6482 :       force = 0.0d0
     224            2 :       call self%oiju_sc%vec_product(1, spref, 2, spref, 3, force)
     225         6482 :       self%ouref(:) = - beta*force(:)
     226              :     endif
     227            2 :     ABI_SFREE(force)
     228              : 
     229            2 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     230              : 
     231            2 :     if(self%has_linquad) then
     232            0 :       if(iam_master) then
     233            0 :         call self%nuvref%initialize(mshape=[self%natom*3, self%natom*3])
     234              :       endif
     235            0 :       call self%niuv_sc%mv1vec(spref, 1, self%nuvref)
     236              :     endif
     237              : 
     238            2 :     if(self%has_biquad) then
     239            1 :       if(iam_master) then
     240            3 :          call self%tuvref%initialize(mshape=[self%natom*3, self%natom*3])
     241              :       endif
     242          649 :       call self%tijuv_sc%mv2vec(0.5*spref, spref, 1, 2, self%tuvref)
     243              :     endif
     244              : 
     245            2 :   end subroutine calculate_ref
     246              : 
     247              :   !-----------------------------------------------------
     248              :   ! add different coupling terms to supercell potential
     249              :   ! TODO: test liu, niuv, tijuv
     250              :   !-----------------------------------------------------
     251            0 :   subroutine add_liu_term(self, i, u, val)
     252              :     class(slc_potential_t), intent(inout) :: self
     253              :     integer,                intent(in)    :: i, u
     254              :     real(dp),               intent(in)    :: val
     255              : 
     256              :     integer :: master, my_rank, comm, nproc
     257              :     logical :: iam_master
     258              : 
     259            0 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     260            0 :     if(iam_master) then
     261            0 :        call self%liu_sc%add_entry(ind=[i,u],val=val)
     262              :     endif
     263            0 :   end subroutine add_liu_term
     264              : 
     265              : 
     266            0 :   subroutine add_niuv_term(self, i, u, v,  val)
     267              :     class(slc_potential_t), intent(inout) :: self
     268              :     integer,                intent(in)    :: i, u, v
     269              :     real(dp),               intent(in)    :: val
     270              : 
     271              :     integer :: master, my_rank, comm, nproc
     272              :     logical :: iam_master
     273              : 
     274            0 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     275            0 :     if(iam_master) then
     276            0 :        call self%niuv_sc%add_entry(ind=[i,u,v],val=val)
     277              :     endif
     278            0 :   end subroutine add_niuv_term
     279              : 
     280              : 
     281              : 
     282      1041984 :   subroutine add_oiju_term(self, i,j,u, val)
     283              :     class(slc_potential_t), intent(inout) :: self
     284              :     integer,                intent(in)    :: i, j, u
     285              :     real(dp),               intent(in)    :: val
     286              : 
     287              :     integer :: master, my_rank, comm, nproc
     288              :     logical :: iam_master
     289              : 
     290      1041984 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     291      1041984 :     if(iam_master) then
     292      4167936 :        call self%oiju_sc%add_entry(ind=[i,j,u],val=val)
     293              :     endif
     294      1041984 :   end subroutine add_oiju_term
     295              : 
     296        69984 :   subroutine add_tijuv_term(self, i,j,u,v, val)
     297              :     class(slc_potential_t), intent(inout) :: self
     298              :     integer,                intent(in)    :: i, j, u, v
     299              :     real(dp),               intent(in)    :: val
     300              : 
     301              :     integer :: master, my_rank, comm, nproc
     302              :     logical :: iam_master
     303              : 
     304        69984 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     305        69984 :     if(iam_master) then
     306       349920 :        call self%tijuv_sc%add_entry(ind=[i,j,u,v],val=val)
     307       349920 :        call self%tuvij_sc%add_entry(ind=[u,v,i,j],val=val)
     308              :     endif
     309        69984 :   end subroutine add_tijuv_term
     310              : 
     311              : 
     312              :   !-----------------------------------------------------------------
     313              :   ! Calculate forces, magnetic fields and energy for coupling terms
     314              :   ! TODO: precalculate terms containing Sref?
     315              :   !-----------------------------------------------------------------
     316              : 
     317         8008 :   subroutine calculate(self, displacement, strain, spin, lwf, &
     318         8008 :        force, stress, bfield, lwf_force, energy, energy_table)
     319              :     class(slc_potential_t), intent(inout) :: self
     320              :     real(dp), optional, intent(inout) :: displacement(:,:), strain(:,:), spin(:,:), lwf(:)
     321              :     real(dp), optional, intent(inout) :: force(:,:), stress(:,:), bfield(:,:), lwf_force(:), energy
     322              :     type(hash_table_t),optional, intent(inout) :: energy_table
     323              : 
     324              :     integer :: ii
     325              :     character(len=80) :: label
     326              :     real(dp) :: eslc, beta, eterm
     327        16016 :     real(dp) :: disp(1:3*self%natom), sp(1:3*self%nspin), spref(1:3*self%nspin)
     328        16016 :     real(dp) :: f1(1:3*self%natom), b1(1:3*self%nspin)
     329        16016 :     real(dp) :: btmp(3, self%nspin), bslc(1:3*self%nspin), fslc(1:3*self%natom)
     330              : 
     331              :     ABI_UNUSED(lwf)
     332              :     ABI_UNUSED(strain)
     333              :     ABI_UNUSED(lwf_force)
     334              :     ABI_UNUSED(stress)
     335              : 
     336        16016 :     sp(:) = reshape(spin, (/ 3*self%nspin /))
     337        16016 :     spref(:) = reshape(self%supercell%spin%Sref, (/ 3*self%nspin/))
     338        16016 :     disp(:) = reshape(displacement, (/ 3*self%natom /))
     339              : 
     340         8008 :     beta = 0.5_dp
     341              : 
     342              :     ! Magnetic field
     343         8008 :     if(present(bfield)) then
     344      2598596 :       bslc(:) = 0.0d0
     345         4004 :       if(self%has_bilin) then
     346            0 :         b1(:) = 0.0d0
     347            0 :         call self%liu_sc%vec_product2d(2, disp, 1, b1)
     348            0 :         bslc(:) = bslc(:) + b1(:)
     349              :       endif
     350         4004 :       if(self%has_linquad) then
     351            0 :         b1(:) = 0.0d0
     352            0 :         call self%niuv_sc%vec_product(2, disp, 3, disp, 1, b1)
     353            0 :         bslc(:) = bslc(:) + beta*b1(:)
     354              :       endif
     355         4004 :       if(self%has_quadlin) then
     356      2598596 :         b1(:) = 0.0d0
     357         4004 :         call self%oiju_sc%vec_product(1, sp, 3, disp, 2, b1)
     358      2598596 :         bslc(:) = bslc(:) + 2.0d0*beta*b1(:)
     359              :       endif
     360         4004 :       if(self%has_biquad) then
     361      1299298 :         b1(:) = 0.0d0
     362         2002 :         call self%tuvij_sc%vec_product4d(disp, disp, 3, sp, 4, b1)
     363      1299298 :         bslc(:) = bslc(:) + beta*b1(:)
     364              :       endif
     365        12012 :       btmp = reshape(bslc, (/ 3, self%nspin /))
     366       868868 :       do ii = 1, self%nspin
     367      3463460 :         btmp(:,ii) = btmp(:,ii)/self%ms(ii)
     368              :       enddo
     369      3463460 :       bfield(:,:) = bfield(:,:) + btmp(:,:)
     370              : 
     371              :       ! TESTING: write magnetic fields to a file
     372              :       !write(201,*) 'Magnetic fields are'
     373              :       !do ii = 1, self%nspin
     374              :       !  write(201,*) ii, btmp(:,ii)
     375              :       !enddo
     376              :     endif
     377              : 
     378              :     ! Force and energy
     379         8008 :     if(present(force) .or. present(energy) .or. present(energy_table)) then
     380     25953928 :       fslc(:) = 0.0d0
     381         8008 :       eslc = 0.0d0
     382         8008 :       if(self%has_bilin) then
     383              :         eterm = 0.0d0
     384            0 :         f1(:) = 0.0d0
     385            0 :         call self%liu_sc%vec_product2d(1, sp, 2, f1)
     386            0 :         fslc(:) = fslc(:) + f1(:)
     387            0 :         eterm =  - dot_product(f1, disp)
     388              :         ! add contributions from reference spin structure
     389            0 :         fslc(:) = fslc(:) + self%luref(:)
     390            0 :         eterm = eterm - dot_product(self%luref, disp)
     391            0 :         if(present(energy_table)) then
     392            0 :           label=trim(self%label)//'_Liu'
     393            0 :           call energy_table%put(label, eterm)
     394              :         endif
     395            0 :         eslc = eslc + eterm
     396              :       endif
     397         8008 :       if(self%has_linquad) then
     398            0 :         f1(:) = 0.0d0
     399              :         eterm = 0.0d0
     400            0 :         call self%niuv_sc%vec_product(1, sp, 2, disp, 3, f1)
     401            0 :         fslc(:) = fslc(:) + 2.0d0*beta*f1(:)
     402            0 :         eterm =  - beta*dot_product(f1, disp)
     403              :         ! add contributions from reference spin structure
     404            0 :         f1(:) = 0.0d0
     405            0 :         call self%nuvref%vec_product2d(1, disp, 2, f1)
     406            0 :         fslc(:)= fslc+2.0*beta*f1(:)
     407            0 :         eterm = eterm + beta*dot_product(f1, disp)
     408            0 :         if(present(energy_table)) then
     409            0 :           label=trim(self%label)//'_Niuv'
     410            0 :           call energy_table%put(label, eterm)
     411              :         endif
     412            0 :         eslc = eslc + eterm
     413              :       endif
     414         8008 :       if(self%has_quadlin) then
     415     25953928 :         f1(:) = 0.0d0
     416              :         eterm = 0.0d0
     417         8008 :         call self%oiju_sc%vec_product(1, sp, 2, sp, 3, f1)
     418     25953928 :         fslc(:) = fslc(:) + beta*f1(:)
     419     25953928 :         eterm = - beta*dot_product(f1, disp)
     420              :         ! add contributions from reference spin structure
     421     25953928 :         fslc(:) = fslc(:) + self%ouref(:)
     422     25953928 :         eterm = eterm - dot_product(self%ouref, disp)
     423         8008 :         if(present(energy_table)) then
     424         8008 :           label=trim(self%label)//'_Oiju'
     425         8008 :           call energy_table%put(label, eterm)
     426              :         endif
     427         8008 :         eslc = eslc + eterm
     428              :       endif
     429         8008 :       if(self%has_biquad) then
     430     12976964 :         f1(:) = 0.0d0
     431              :         eterm = 0.0d0
     432         4004 :         call self%tijuv_sc%vec_product4d(sp, sp, 3, disp, 4, f1)
     433     12976964 :         fslc(:) = fslc(:) + beta*f1(:)
     434     12976964 :         eterm = - 0.5_dp*beta*dot_product(f1, disp)
     435              :         ! add contributions from reference spin structure
     436     12976964 :         f1(:) = 0.0d0
     437         4004 :         call self%tuvref%vec_product2d(1, disp, 2, f1)
     438     12976964 :         fslc(:)= fslc+2.0*beta*f1(:)
     439     12976964 :         eterm = eterm + beta*dot_product(f1, disp)
     440         4004 :         if(present(energy_table)) then
     441         4004 :           label=trim(self%label)//'_Tijuv'
     442         4004 :           call energy_table%put(label, eterm)
     443              :         endif
     444         4004 :         eslc = eslc + eterm
     445              :       endif
     446              :     endif !energy or force
     447              : 
     448         8008 :     if(present(force)) then
     449     17309292 :       force(:,:) = force(:,:) + reshape(fslc, (/3, self%natom /))
     450              :       !TESTING write forces to file
     451              :       !write(200,*) 'Forces are'
     452              :       !do ii = 1, self%natom
     453              :       !  write(200,*) ii, force(:,ii)
     454              :       !enddo
     455              :     endif
     456              : 
     457         8008 :     if(present(energy)) energy =  energy + eslc
     458              : 
     459              : 
     460         8008 :     ABI_UNUSED_A(strain)
     461         8008 :     ABI_UNUSED_A(lwf)
     462         8008 :     ABI_UNUSED_A(stress)
     463         8008 :     ABI_UNUSED_A(lwf_force)
     464              : 
     465         8008 :   end subroutine calculate
     466              : 
     467              :   !!***
     468            6 : end module m_slc_potential
        

Generated by: LCOV version 2.3-1