LCOV - code coverage report
Current view: top level - src/78_effpot - m_spin_potential.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 77.2 % 162 125
Test Date: 2026-09-20 15:27:41 Functions: 76.5 % 17 13

            Line data    Source code
       1              : !!****m* ABINIT/m_spin_potential
       2              : !! NAME
       3              : !! m_spin_potential
       4              : !!
       5              : !! FUNCTION
       6              : !! This module contains the spin hamiltonian, and the methods for
       7              : !! calculating effective magnetic field (torque), dS/dt, and total_energy
       8              : !!
       9              : !!
      10              : !! Datatypes:
      11              : !!
      12              : !! * spin_potential_t
      13              : !!
      14              : !! Subroutines:
      15              : !!
      16              : !! * spin_potential_t_initialize
      17              : !! * spin_potential_t_finalize
      18              : !! * spin_potential_t_total_Heff : calculate total Heff (no Langevin term)
      19              : !! * spin_potential_t_Heff_to_dSdt:
      20              : !!  * spin_potential_t_get_dSdt : dSdt, Langevin term is an input.
      21              : !!  * spin_potential_t_get_Langevin_Heff
      22              : 
      23              : !!
      24              : !!
      25              : !! COPYRIGHT
      26              : !! Copyright (C) 2001-2026 ABINIT group (TO, hexu)
      27              : !! This file is distributed under the terms of the
      28              : !! GNU General Public License, see ~abinit/COPYING
      29              : !! or http://www.gnu.org/copyleft/gpl.txt .
      30              : !! For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
      31              : !!
      32              : !! SOURCE
      33              : 
      34              : #if defined HAVE_CONFIG_H
      35              : #include "config.h"
      36              : #endif
      37              : #include "abi_common.h"
      38              : module  m_spin_potential
      39              :   use defs_basis
      40              :   use m_errors
      41              :   use m_abicore
      42              :   use m_xmpi
      43              :   use m_mpi_scheduler, only: mb_mpi_info_t, init_mpi_info, mpi_scheduler_t
      44              :   use m_multibinit_dataset, only: multibinit_dtset_type
      45              :   use m_multibinit_cell, only: mbcell_t, mbsupercell_t
      46              :   use m_spmat_coo, only: coo_mat_t
      47              :   use m_spmat_lil, only: lil_mat_t
      48              :   use m_spmat_csr, only : CSR_mat_t
      49              :   use m_spmat_convert, only : spmat_convert
      50              :   use m_abstract_potential, only : abstract_potential_t
      51              :   use m_hashtable_strval, only: hash_table_t
      52              :   implicit none
      53              :   !!***
      54              :   private
      55              : 
      56              :   type, public, extends(abstract_potential_t) :: spin_potential_t
      57              :      integer :: nspin=0
      58              :      logical :: has_external_hfield, has_dipdip
      59              :      real(dp) :: eref !energy of reference spin state
      60              : 
      61              :      ! Array or scalar?
      62              :      real(dp), allocatable :: external_hfield(:,:)
      63              : 
      64              :      ! Exchange/DMI/dipdip stored like COO sparse matrix form.
      65              :      type(lil_mat_t) :: coeff_coo
      66              :      logical :: csr_mat_ready= .False.
      67              :      logical :: has_onsite = .False.
      68              :      type(CSR_mat_t) :: bilinear_csr_mat
      69              :      ! 3, 3, ninit
      70              : 
      71              :      ! vector for calculating effective field
      72              :      real(dp), allocatable :: Htmp(:, :)
      73              :      real(dp), allocatable :: ms(:)
      74              :      real(dp), allocatable :: onsite_bilinear_term(:, :, :)
      75              :      type(mb_mpi_info_t) :: mpiinfo
      76              :      type(mpi_scheduler_t) :: mps
      77              :    CONTAINS
      78              :      procedure :: initialize
      79              :      procedure :: finalize
      80              :      procedure :: set_params
      81              :      procedure :: set_supercell
      82              :      procedure :: get_Heff => spin_potential_t_total_Heff
      83              :      procedure :: set_external_hfield
      84              :      procedure :: calc_bilinear_term_Heff
      85              :      procedure :: calc_external_Heff
      86              :      procedure :: calculate => spin_potential_t_calculate
      87              :      !procedure :: get_energy => spin_potential_t_get_energy
      88              :      procedure :: get_delta_E => spin_potential_t_get_delta_E
      89              :      procedure :: add_bilinear_term
      90              :      procedure :: add_bilinear_term_spin_block
      91              :      procedure :: set_bilinear_term
      92              :      procedure, private :: prepare_csr_matrix
      93              :      procedure :: set_terms
      94              :   end type spin_potential_t
      95              : 
      96              : contains
      97              : 
      98            2 :   subroutine initialize(self, nspin)
      99              :     !Arguments ------------------------------------
     100              :     !scalars
     101              :     class(spin_potential_t), intent(inout) :: self
     102              :     integer :: nspin
     103              : 
     104              :     integer :: master, my_rank, comm, nproc, ierr
     105              :     logical :: iam_master
     106            2 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     107            2 :     call self%mps%initialize(ntasks=nspin, master=master, comm=comm)
     108            2 :     self%label="SpinPotential"
     109            2 :     self%has_spin=.True.
     110            2 :     self%has_displacement=.False.
     111            2 :     self%has_strain=.False.
     112            2 :     self%is_null=.False.
     113            2 :     self%nspin=nspin
     114            2 :     call xmpi_bcast(self%nspin, master, comm, ierr)
     115            6 :     ABI_MALLOC( self%ms, (self%nspin))
     116            2 :     if(iam_master) then
     117            6 :        call self%coeff_coo%initialize([self%nspin*3, self%nspin*3])
     118              :     endif
     119            6 :     ABI_MALLOC(self%external_hfield, (3,self%nspin))
     120         1730 :     self%external_hfield=0.0_dp
     121            4 :     ABI_MALLOC( self%Htmp, (3, self%nspin))
     122         1730 :     self%Htmp(:,:)=0.0_dp
     123              : 
     124            2 :     self%has_external_hfield=.False.
     125            2 :     self%has_dipdip=.False.
     126            6 :     ABI_MALLOC(self%onsite_bilinear_term, (3, 3, self%nspin))
     127         5618 :     self%onsite_bilinear_term = 0.0_dp
     128            2 :   end subroutine initialize
     129              : 
     130            8 :   subroutine set_supercell(self, supercell)
     131              :     class(spin_potential_t), intent(inout) :: self
     132              :     type(mbsupercell_t), target, intent(inout) :: supercell
     133              :     integer :: master, my_rank, comm, nproc, ierr
     134              :     logical :: iam_master
     135            4 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     136              : 
     137            4 :     self%supercell=>supercell
     138          868 :     self%ms(:)=supercell%spin%ms(:)
     139            4 :     call xmpi_bcast(self%ms, master, comm, ierr)
     140            4 :   end subroutine set_supercell
     141              : 
     142            0 :   subroutine set_terms(self, &
     143            0 :        &     external_hfield, &
     144            0 :        & bilinear_i, bilinear_j, bilinear_val)
     145              : 
     146              :     !Arguments ------------------------------------
     147              :     !scalars
     148              :     !arrays
     149              :     class(spin_potential_t), intent(inout) :: self
     150              : 
     151              :     ! Terms.
     152              :     real(dp), optional, intent(in) :: external_hfield(:,:)
     153              :     integer, optional, intent(in) :: bilinear_i(:), bilinear_j(:)
     154              :     real(dp), optional,intent(in) :: bilinear_val(:)
     155              : 
     156              :     !Local variables-------------------------------
     157              :     ! *************************************************************************
     158              : 
     159              : 
     160            0 :     if(present(external_hfield)) then
     161            0 :        call self%set_external_hfield( external_hfield)
     162              :     end if
     163              : 
     164            0 :     if ( present(bilinear_i) .and. present( bilinear_j) .and. present(bilinear_val) ) then
     165            0 :        call self%set_bilinear_term(bilinear_i, bilinear_j, bilinear_val)
     166              :     endif
     167              : 
     168            0 :   end subroutine set_terms
     169              : 
     170              :   !-------------------------------------------------------------------!
     171              :   !set_params :
     172              :   !-------------------------------------------------------------------!
     173            2 :   subroutine set_params(self, params)
     174              :     class(spin_potential_t), intent(inout) :: self
     175              :     type(multibinit_dtset_type), intent(inout) :: params
     176            4 :     real(dp) :: tmp(3, self%nspin)
     177              :     integer :: i
     178              :     integer :: master, my_rank, comm, nproc
     179              :     logical :: iam_master
     180            2 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     181            2 :     if(iam_master) then
     182          434 :        do i=1, self%nspin
     183         1730 :           tmp(:, i)=params%spin_mag_field(:)
     184              :        end do
     185              :     endif
     186            2 :     call self%set_external_hfield(tmp)
     187            2 :   end subroutine set_params
     188              : 
     189              : 
     190            2 :   subroutine set_external_hfield(self, external_hfield)
     191              :     class(spin_potential_t), intent(inout) :: self
     192              :     real(dp), intent(in) :: external_hfield(:,:)
     193              :     integer :: master, my_rank, comm, nproc, ierr
     194              :     logical :: iam_master
     195            2 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     196            2 :     self%has_external_hfield = .true.
     197         1732 :     self%external_hfield = external_hfield
     198            2 :     call xmpi_bcast(self%has_external_hfield, master, comm, ierr)
     199            2 :     call xmpi_bcast(self%external_hfield, master, comm, ierr)
     200            2 :   end subroutine set_external_hfield
     201              : 
     202         4006 :   subroutine calc_external_Heff(self, Heff)
     203              :     class(spin_potential_t), intent(inout) :: self
     204              :     real(dp), intent(out) :: Heff(:,:)
     205              :     integer :: i
     206       869302 :     do i= self%mps%istart, self%mps%iend
     207      3465190 :       Heff(:, i)= self%external_hfield(:, i)
     208              :     end do
     209         4006 :   end subroutine calc_external_Heff
     210              : 
     211        33696 :   subroutine add_bilinear_term(self, i,j, val)
     212              :     class(spin_potential_t), intent(inout) :: self
     213              :     integer, intent(in) :: i, j
     214              :     real(dp), intent(in) :: val
     215              :     integer :: master, my_rank, comm, nproc
     216              :     logical :: iam_master
     217        33696 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     218        33696 :     if(iam_master) then
     219       101088 :        call self%coeff_coo%add_entry(ind=[i,j],val=val)
     220              :     endif
     221        33696 :   end subroutine add_bilinear_term
     222              : 
     223            0 :   subroutine set_bilinear_term(self, ilist,jlist, vallist)
     224              :     class(spin_potential_t), intent(inout) :: self
     225              :     integer, intent(in) :: ilist(:), jlist(:)
     226              :     real(dp), intent(in) :: vallist(:)
     227              :     integer :: i
     228              :     integer :: master, my_rank, comm, nproc
     229              :     logical :: iam_master
     230            0 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     231              : 
     232            0 :     if (iam_master) then
     233            0 :        do i = 1, size(ilist)
     234            0 :           call self%coeff_coo%add_entry(ind=[ilist(i),jlist(i)],val=vallist(i))
     235              :        end do
     236              :     endif
     237            0 :   end subroutine set_bilinear_term
     238              : 
     239              : 
     240            0 :   subroutine add_bilinear_term_spin_block(self, ispin, jspin, val)
     241              : 
     242              :     class(spin_potential_t), intent(inout) :: self
     243              :     integer, intent(in) :: ispin, jspin
     244              :     real(dp), intent(in) :: val(:,:)
     245              :     integer :: ia, ib
     246              : 
     247              :     integer :: master, my_rank, comm, nproc
     248              :     logical :: iam_master
     249            0 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     250              : 
     251            0 :     if(iam_master) then
     252            0 :        do ia = 1, 3, 1
     253            0 :           do ib=1, 3, 1
     254              :              call self%coeff_coo%add_entry([(ispin-1)*3+ia, &
     255            0 :                   (jspin-1)*3+ib], val=val(ia,ib))
     256              :           end do
     257              :        end do
     258            0 :        if(ispin==jspin) then
     259            0 :            self%has_onsite = .True.
     260            0 :            self%onsite_bilinear_term(:, :, ispin) = val(:, :)
     261              :        end if
     262              :     endif
     263            0 :   end subroutine add_bilinear_term_spin_block
     264              : 
     265         4006 :   subroutine prepare_csr_matrix(self)
     266              :     class(spin_potential_t), intent(inout) :: self
     267              :     integer :: master, my_rank, comm, nproc, ierr
     268              :     logical :: iam_master
     269              :     integer :: ispin
     270              : 
     271         4006 :     if (.not. self%csr_mat_ready) then
     272            2 :        call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     273            2 :        if(iam_master) then
     274            2 :           call spmat_convert(self%coeff_coo, self%bilinear_csr_mat)
     275              :        endif
     276            2 :        call self%coeff_coo%finalize()
     277            2 :        call self%bilinear_csr_mat%sync(master=master, comm=comm, nblock=1)
     278            2 :        self%csr_mat_ready=.True.
     279            2 :        call xmpi_bcast(self%csr_mat_ready, master, comm, ierr)
     280              :        ! also broadcast the onsite terms.
     281          434 :        do ispin=1, self%nspin
     282          434 :            call self%bilinear_csr_mat%get_block(ispin*3-2, ispin*3-2, 3, 3, self%onsite_bilinear_term(:, :, ispin))
     283              :            !print *, "ispin:", ispin
     284              :            !print *, "oniste",  self%onsite_bilinear_term(:, :, ispin)
     285              :        end do
     286            2 :        self%has_onsite=.True.
     287              : 
     288            2 :        call xmpi_bcast(self%has_onsite, master, comm, ierr)
     289            2 :        call xmpi_bcast(self%onsite_bilinear_term, master, comm, ierr)
     290              :     endif
     291         4006 :   end subroutine prepare_csr_matrix
     292              : 
     293              : 
     294         4006 :   subroutine calc_bilinear_term_Heff(self, S, Heff)
     295              :     class(spin_potential_t), intent(inout) :: self
     296              :     real(dp), intent(inout) :: S(:,:)
     297              :     real(dp), intent(out) :: Heff(3,self%nspin)
     298              :     integer :: i
     299              :     !call self%bilinear_csr_mat%mv(S ,Heff)
     300         4006 :     call self%prepare_csr_matrix()
     301         4006 :     call self%bilinear_csr_mat%mv_mpi(x=S ,b=Heff, bcastx=.False., syncb=.False.)
     302       869302 :     do i= self%mps%istart, self%mps%iend
     303      3465190 :        Heff(:, i)=Heff(:,i)/self%ms(i)*2.0_dp
     304              :     end do
     305         4006 :   end subroutine calc_bilinear_term_Heff
     306              : 
     307         8008 :   subroutine spin_potential_t_calculate(self, displacement, strain, spin, lwf, &
     308         8008 :        force, stress, bfield, lwf_force, energy, energy_table)
     309              :     class(spin_potential_t), intent(inout) :: self
     310              :     real(dp), optional, intent(inout) :: displacement(:,:), strain(:,:), spin(:,:), lwf(:)
     311              :     real(dp), optional, intent(inout) :: force(:,:), stress(:,:), bfield(:,:), lwf_force(:), energy
     312              :     type(hash_table_t),optional, intent(inout) :: energy_table
     313              :     real(dp) :: etmp
     314              :     ! if present in input
     315              :     ! calculate if required
     316              : 
     317         8008 :     if (present(bfield) ) then
     318         4004 :        call self%get_Heff(spin, bfield, etmp)
     319              : 
     320              :        ! subtract enery of reference spin configuration
     321         4004 :        etmp = etmp - self%eref
     322              : 
     323              :        ! only update energy when bfield is asked for.
     324         4004 :        if ( present(energy)) then
     325         4004 :           energy=energy+etmp
     326              :        end if
     327         4004 :        if (present(energy_table)) then
     328         4004 :           call energy_table%put(self%label, etmp)
     329              :        end if
     330              :     end if
     331              : 
     332              : 
     333         8008 :     ABI_UNUSED_A(self)
     334         8008 :     ABI_UNUSED_A(displacement)
     335         8008 :     ABI_UNUSED_A(strain)
     336         8008 :     ABI_UNUSED_A(spin)
     337         8008 :     ABI_UNUSED_A(lwf)
     338         8008 :     ABI_UNUSED_A(force)
     339         8008 :     ABI_UNUSED_A(stress)
     340         8008 :     ABI_UNUSED_A(bfield)
     341         8008 :     ABI_UNUSED_A(lwf_force)
     342         8008 :     ABI_UNUSED_A(energy)
     343         8008 :   end subroutine spin_potential_t_calculate
     344              : 
     345              : 
     346         4006 :   subroutine spin_potential_t_total_Heff(self,S, Heff, energy)
     347              :     class(spin_potential_t), intent(inout) :: self
     348              :     real(dp), intent(inout):: S(3,self%nspin)
     349              :     real(dp), intent(inout):: Heff(3,self%nspin)
     350              :     real(dp), intent(inout) :: energy
     351              :     real(dp) :: etmp
     352              :     integer :: i, j
     353              :     integer :: master, my_rank, comm, nproc, ierr
     354              :     logical :: iam_master
     355         4006 :     call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     356              : 
     357      3465190 :     self%Htmp(:,:)=0.0_dp
     358         4006 :     etmp=0.0_dp
     359              : 
     360              : 
     361              :     ! calculate energy from bilinear terms (all the above ones)
     362         4006 :     call self%calc_bilinear_term_Heff(S,self%Htmp)
     363       869302 :     do i= self%mps%istart, self%mps%iend
     364      3461184 :        Heff(:,i)=Heff(:,i)+self%Htmp(:,i)
     365      3465190 :        do j=1, 3
     366      3461184 :           etmp=etmp-(self%Htmp(j, i)*S(j,i)*self%ms(i))*0.5_dp
     367              :        end do
     368              :     enddo
     369              :     if(iam_master) then
     370              :        if (self%has_dipdip) then
     371              :           continue
     372              :           ! TODO implement dipdip and add it
     373              :        endif
     374              :     endif
     375              : 
     376              :     ! linear terms
     377         4006 :     if (self%has_external_hfield) then
     378      3465190 :         self%Htmp(:,:)= 0.0_dp
     379         4006 :        call self%calc_external_Heff(self%Htmp)
     380       869302 :          do i= self%mps%istart, self%mps%iend
     381      3461184 :            Heff(:,i)=Heff(:,i)+self%Htmp(:,i)
     382      3465190 :            do j=1, 3
     383      3461184 :               etmp=etmp-(self%Htmp(j, i)*S(j,i)*self%ms(i))
     384              :            end do
     385              :          enddo
     386              :     endif
     387              : 
     388         4006 :     call xmpi_sum_master(etmp, 0, xmpi_world, ierr )
     389              :     ! NOTE: here energy is not added to input energy.
     390         4006 :     energy=etmp
     391              : 
     392         4006 :   end subroutine spin_potential_t_total_Heff
     393              : 
     394              : !  subroutine spin_potential_t_get_energy(self, S, energy)
     395              : !    class(spin_potential_t), intent(inout) :: self
     396              : !    real(dp), intent(inout):: S(3,self%nspin)
     397              : !    real(dp), intent(inout) :: energy
     398              : !    integer :: i, j
     399              : !    integer :: master, my_rank, comm, nproc, ierr
     400              : !    logical :: iam_master
     401              : !    call init_mpi_info(master, iam_master, my_rank, comm, nproc)
     402              : !
     403              : !
     404              : !    if (.not. self%csr_mat_ready) then
     405              : !       call spmat_convert(self%coeff_coo, self%bilinear_csr_mat)
     406              : !       call self%bilinear_csr_mat%sync()
     407              : !       self%csr_mat_ready=.True.
     408              : !    endif
     409              : !    call self%bilinear_csr_mat%mv_mpi(S ,self%Htmp)
     410              : !    if(iam_master) then
     411              : !       energy=energy - sum(sum(self%Htmp* S, dim=1))
     412              : !    endif
     413              : !    if(iam_master) then
     414              : !       if (self%has_external_hfield) then
     415              : !          call self%calc_external_Heff(self%Htmp)
     416              : !          do i=1, self%nspin
     417              : !             do j=1, 3
     418              : !                energy= energy- self%Htmp(j, i)*S(j, i)*self%ms(i)
     419              : !             end do
     420              : !          end do
     421              : !       endif
     422              : !    end if
     423              : !  end subroutine spin_potential_t_get_energy
     424              : 
     425              : 
     426            0 :   subroutine spin_potential_t_get_delta_E(self, S, ispin, Snew, deltaE)
     427              :     class(spin_potential_t), intent(inout) :: self
     428              :     real(dp), intent(inout):: S(:,:), Snew(:)
     429              :     integer, intent(in) :: ispin
     430              :     real(dp), intent(inout) ::deltaE
     431              :     !real(dp) ::  Eold, Enew
     432              :     real(dp) :: tmp(3), dS(3)
     433              : 
     434              :     ! naive implementation, for test only
     435              :     !real(dp) :: Stmp(3,self%nspin), Enew, Eold
     436              :     !call self%get_Heff(S, self%Htmp, Eold)
     437              :     !call self%bilinear_csr_mat%mv(S, self%Htmp)
     438              :     !Eold=-sum(sum(self%Htmp(:,:)*S(:,:), dim=1))
     439              : 
     440              :     !Stmp(:,:)=S(:,:)
     441              :     !Stmp(:, ispin)= Snew(:)
     442              :     !call self%get_Heff(Stmp, self%Htmp, Enew)
     443              :     !deltaE=Enew-Eold
     444              :     !print *, "naive deltaE", deltaE
     445              : 
     446              :     ! more efficient one
     447            0 :     dS(:)=Snew(:)-S(:, ispin)
     448              :     !S(:, ispin)= S(:, ispin)+ dS
     449              : 
     450            0 :     deltaE=0.0_dp
     451              : 
     452            0 :     call self%prepare_csr_matrix()
     453            0 :     call self%bilinear_csr_mat%mv_select_row(3, [3*ispin-2, 3*ispin-1, 3*ispin], S, tmp)
     454            0 :     deltaE=deltaE-dot_product(tmp, dS ) *2.0
     455            0 :     if (self%has_onsite) then
     456            0 :        tmp=matmul(self%onsite_bilinear_term(:, :, ispin), dS)
     457            0 :        deltaE = deltaE -  dot_product(tmp, dS)
     458              :        !print *, "dS K dS:", dot_product(tmp, dS)
     459              :     end if
     460              :     !print *, "smart deltaE", deltaE
     461              :     !S(:, ispin)=S(:,ispin)-dS
     462              :     !print *, "diff for ispin:", ispin, Enew-Eold-deltaE
     463              : 
     464              : 
     465            0 :     if(self%has_external_hfield) then
     466            0 :        deltaE=deltaE - dot_product(self%external_hfield(:, ispin), dS)*self%ms(ispin)
     467              :     end if
     468            0 :   end subroutine spin_potential_t_get_delta_E
     469              : 
     470              : 
     471            2 :   subroutine finalize(self)
     472              :     class(spin_potential_t), intent(inout):: self
     473            2 :     if (allocated(self%ms)) then
     474            2 :        ABI_FREE(self%ms)
     475              :     end if
     476              : 
     477            2 :     ABI_SFREE(self%onsite_bilinear_term)
     478              : 
     479            2 :     if (allocated(self%Htmp)) then
     480            2 :        ABI_FREE(self%Htmp)
     481              :     endif
     482              : 
     483            2 :     if (allocated(self%external_hfield)) then
     484            2 :        ABI_FREE(self%external_hfield)
     485              :     endif
     486              : 
     487            2 :     call self%bilinear_csr_mat%finalize()
     488            2 :     if (.not. self%csr_mat_ready) then
     489            0 :        call self%coeff_coo%finalize()
     490              :     end if
     491              : 
     492            2 :     call self%mps%finalize()
     493              : 
     494            2 :   end subroutine finalize
     495              : 
     496            6 : end module m_spin_potential
        

Generated by: LCOV version 2.3-1