LCOV - code coverage report
Current view: top level - src/78_effpot - m_spin_observables.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 92.9 % 98 91
Test Date: 2026-09-20 18:56:22 Functions: 63.6 % 11 7

            Line data    Source code
       1              : !!****m* ABINIT/m_spin_observables
       2              : !! NAME
       3              : !! m_spin_observables
       4              : !!
       5              : !! FUNCTION
       6              : !! This module contains the subroutines to calculate the observables of spin dynamics
       7              : !!
       8              : !!
       9              : !! Datatypes:
      10              : !! spin_observable_t: store data to calculate observables
      11              : !! Subroutines:
      12              : !!
      13              : !!
      14              : !! COPYRIGHT
      15              : !! Copyright (C) 2001-2026 ABINIT group (hexu)
      16              : !! This file is distributed under the terms of the
      17              : !! GNU General Public License, see ~abinit/COPYING
      18              : !! or http://www.gnu.org/copyleft/gpl.txt .
      19              : !! For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
      20              : !!
      21              : !! SOURCE
      22              : 
      23              : 
      24              : #if defined HAVE_CONFIG_H
      25              : #include "config.h"
      26              : #endif
      27              : 
      28              : #include "abi_common.h"
      29              : 
      30              : module m_spin_observables
      31              : 
      32              :   use defs_basis
      33              :   use m_abicore
      34              :   use m_errors
      35              :   use m_xmpi
      36              :   use m_spin_potential, only: spin_potential_t
      37              :   use m_multibinit_cell, only: mbsupercell_t
      38              :   use m_multibinit_dataset, only: multibinit_dtset_type
      39              : 
      40              :   implicit none
      41              : 
      42              :   private
      43              :   !!***
      44              : 
      45              :   !-----------------------------------------------------------------------
      46              :   !> @brief spin_observale_t: observables for spin dynamics.
      47              :   !-----------------------------------------------------------------------
      48              :   type, public :: spin_observable_t
      49              :      ! Switches for what kind of obs should be calculated
      50              :      logical :: calc_thermo_obs    ! theromostatic obs: susceptibility, specific heat...
      51              :      logical :: calc_correlation_obs ! correlation function related
      52              :      logical :: calc_traj_obs       ! trajectory related (winding number, etc)
      53              : 
      54              :      integer :: nspin, nsublatt, ntime, nscell
      55              :      ! nspin: number of spin
      56              :      ! nsublatt: number of sublattice (currently each spin in primitive cell is one sublattice)
      57              :      ! ntime: number of time steps
      58              :      ! nscell: number of cell in supercell
      59              : 
      60              :      real(dp) :: temperature
      61              :      integer, allocatable :: isublatt(:), nspin_sub(:)
      62              :      ! isublatt: index of sublattice for each spin
      63              :      ! nspin_sub:
      64              : 
      65              :      real(dp) :: energy
      66              :      real(dp), allocatable :: S(:,:), Snorm(:)
      67              :      real(dp), allocatable ::  Ms_coeff(:),  Mst_sub(:, :), Mst_sub_norm(:)
      68              :      ! Ms_coeff: coefficient to calcualte staggered Mst.
      69              :      ! Staggerd means a phase factor is multiplied to each spin.
      70              :      ! Ms_coeff is that phase factor. Mst_coeff = e ^{iq R}
      71              : 
      72              :      ! Mst_sub: M staggered for sublattice: \sum_(i in sublattice)\S_i phase_i
      73              :      ! Mst_sub : norm of Mst_sub
      74              : 
      75              :      real(dp) ::  M_total(3), Mst_total(3), M_total_norm,  Mst_norm_total, Snorm_total
      76              :      ! M_total: M total    \sum M_i where M_i =|S_i|
      77              :      ! Mst_total: staggerd M total  |sum M_I|
      78              :      ! Mst_norm : ||Mst_total||
      79              :      real(dp), allocatable :: Avg_Mst_sub_norm(:)
      80              :      real(dp) :: Avg_Mst_norm_total
      81              : 
      82              :      real(dp) :: binderU4, chi, Cv
      83              :      ! binderU4: binder U4
      84              :      ! chi: susceptibility
      85              :      ! Cv: specific heat
      86              : 
      87              :      ! variables for calculate Cv
      88              :      real(dp) :: avg_E_t   ! average energy E over time
      89              :      real(dp) :: avg_E2_t  ! average E^2 over time
      90              :      real(dp) :: avg_m_t   ! average of sum(Mst_norm_total) over t
      91              :      real(dp) :: avg_m2_t  ! average of sum(Mst_norm_total**2) over t
      92              :      real(dp) :: avg_m4_t  !average of sum(Mst_sub_total_norm**4) over t
      93              :    contains
      94              :      procedure :: initialize
      95              :      procedure :: finalize
      96              :      procedure :: reset
      97              :      procedure :: get_staggered_M
      98              :      procedure :: get_thermo_obs
      99              :      procedure :: get_correlation_obs
     100              :      procedure :: get_traj_obs
     101              :      procedure :: get_observables
     102              : 
     103              :   end type spin_observable_t
     104              : 
     105              : contains
     106              : 
     107              : 
     108            2 :   subroutine initialize(self, supercell, params)
     109              : 
     110              :     class(spin_observable_t) :: self
     111              :     type(mbsupercell_t) :: supercell
     112              :     type(multibinit_dtset_type) :: params
     113              :     integer i
     114              :     complex(dp) :: i2pi = (0.0, two_pi)
     115              : 
     116            2 :     self%calc_thermo_obs=  (params%spin_calc_thermo_obs ==1)
     117              :     !self%calc_traj_obs= (params%spin_calc_traj_obs ==1)
     118              :     !self%calc_correlation_obs=(params%spin_calc_correlation_obs ==1)
     119            2 :     self%calc_traj_obs= .False.
     120            2 :     self%calc_correlation_obs= .False.
     121              : 
     122              : 
     123            2 :     self%nspin=supercell%spin%nspin
     124          434 :     self%nsublatt=maxval(supercell%spin%ispin_prim)
     125              : 
     126            6 :     ABI_MALLOC(self%S, (3, self%nspin))
     127            6 :     ABI_MALLOC(self%Snorm, (self%nspin))
     128              : 
     129            6 :     ABI_MALLOC(self%isublatt,(self%nspin) )
     130          434 :     self%isublatt(:)=supercell%spin%ispin_prim(:)
     131              : 
     132            6 :     ABI_MALLOC(self%nspin_sub, (self%nsublatt))
     133            4 :     self%nspin_sub(:)=0
     134          434 :     do i =1, self%nspin
     135          434 :        self%nspin_sub(self%isublatt(i)) = self%nspin_sub(self%isublatt(i)) + 1
     136              :     end do
     137              : 
     138            6 :     ABI_MALLOC(self%Ms_coeff,(self%nspin))
     139            6 :     ABI_MALLOC(self%Mst_sub, (3, self%nsublatt))
     140            6 :     ABI_MALLOC(self%Mst_sub_norm, (self%nsublatt))
     141            4 :     ABI_MALLOC(self%Avg_Mst_sub_norm, (self%nsublatt))
     142              : 
     143          434 :     do i = 1, self%nspin
     144         1730 :       self%Ms_coeff(i) = real(exp(i2pi * dot_product(params%spin_projection_qpoint, supercell%spin%rvec(:,i))))
     145              :     end do
     146              : 
     147            2 :     call reset(self, params)
     148            2 :   end subroutine initialize
     149              : 
     150            4 :   subroutine reset(self, params)
     151              :     ! set values to zeros.
     152              :     class(spin_observable_t), intent(inout) :: self
     153              :     class(multibinit_dtset_type), optional, intent(in) :: params
     154            4 :     if (present(params)) then
     155            2 :        self%temperature=params%spin_temperature
     156            8 :        self%nscell = product(params%ncell)
     157              :     end if
     158            4 :     self%ntime=0
     159            4 :     self%Cv=0.0
     160            4 :     self%binderU4=0.0
     161           16 :     self%M_total(:) =0.0
     162            4 :     self%M_total_norm=0.0
     163            4 :     self%Mst_norm_total=0.0
     164           20 :     self%Mst_sub(:,:)=0.0
     165            8 :     self%Mst_sub_norm(:)=0.0
     166            8 :     self%Avg_Mst_sub_norm(:) =0.0
     167            4 :     self%Avg_Mst_norm_total = 0.0
     168            4 :     self%avg_e_t=0.0
     169            4 :     self%avg_e2_t=0.0
     170              : 
     171            4 :     self%avg_m_t=0.0
     172            4 :     self%avg_m2_t=0.0
     173            4 :     self%avg_m4_t=0.0
     174            4 :   end subroutine reset
     175              : 
     176              :   !-----------------------------------------------------------------------
     177              :   !> @brief finalize
     178              :   !-----------------------------------------------------------------------
     179            4 :   subroutine finalize(self)
     180              :     class(spin_observable_t) :: self
     181              : 
     182            4 :     ABI_SFREE(self%isublatt)
     183            4 :     ABI_SFREE(self%nspin_sub)
     184            4 :     ABI_SFREE(self%S)
     185            4 :     ABI_SFREE(self%Snorm)
     186            4 :     ABI_SFREE(self%Ms_coeff)
     187            4 :     ABI_SFREE(self%Mst_sub)
     188            4 :     ABI_SFREE(self%Mst_sub_norm)
     189            4 :     ABI_SFREE(self%Avg_Mst_sub_norm)
     190              : 
     191            4 :   end subroutine finalize
     192              : 
     193              :   !-----------------------------------------------------------------------
     194              :   !> @brief set S, Snorm, and energy (after one dynamics step)
     195              :   !> @param [in]  S
     196              :   !> @param [in] Snorm
     197              :   !> @param [in] energy
     198              :   !-----------------------------------------------------------------------
     199         2002 :   subroutine update(self, S, Snorm, energy)
     200              :     class(spin_observable_t), intent(inout) :: self
     201              :     real(dp), intent(in):: S(3,self%nspin), Snorm(self%nspin), energy
     202      1733732 :     self%S=S
     203       436436 :     self%Snorm=Snorm
     204         2002 :     self%energy=energy
     205         2002 :   end subroutine update
     206              : 
     207              :   !-----------------------------------------------------------------------
     208              :   !> @brief Calculate M in each sublattice
     209              :   !>  sum of S * phase factor in each sublattice
     210              :   !-----------------------------------------------------------------------
     211         2002 :   subroutine get_staggered_M(self)
     212              : 
     213              :     class(spin_observable_t), intent(inout) :: self
     214              :     integer :: i, isub
     215        10010 :     self%Mst_sub(:,:)=0.0
     216         8008 :     self%M_total(:)=0.0
     217       434434 :     do i = 1, self%nspin
     218       432432 :        isub=self%isublatt(i)
     219      1729728 :        self%Mst_sub(:, isub) = self%Mst_sub(:, isub) +  self%S(:, i)* self%Ms_coeff(i) * self%Snorm(i)
     220      1731730 :        self%M_total(:) = self%M_total + self%S(:, i)*self%Snorm(i)
     221              :     end do
     222              : 
     223        10010 :     self%Mst_sub_norm(:) =sqrt(sum(self%Mst_sub**2, dim=1))/self%nscell
     224         4004 :     self%Mst_norm_total= sum(self%Mst_sub_norm(:))
     225              :     !self%M_total_norm = sqrt(sum(self%M_total**2))/self%nscell
     226       434434 :     self%Snorm_total = sum(self%Snorm)/self%nscell
     227              : 
     228         4004 :     self%avg_Mst_sub_norm(:)=(self%avg_Mst_sub_norm(:)*self%ntime + self%Mst_sub_norm(:))/(self%ntime+1)
     229         2002 :     self%avg_Mst_norm_total=(self%avg_Mst_norm_total*self%ntime + self%Mst_norm_total)/(self%ntime+1)
     230         2002 :   end subroutine get_staggered_M
     231              : 
     232              :   !-----------------------------------------------------------------------
     233              :   !> @brief calculate observable related to the topology of trajectory
     234              :   !> like the winding number
     235              :   !-----------------------------------------------------------------------
     236            0 :   subroutine get_traj_obs(self)
     237              :     class(spin_observable_t) :: self
     238              :     ABI_UNUSED(self%nspin)
     239            0 :   end subroutine get_traj_obs
     240              : 
     241              :   !-----------------------------------------------------------------------
     242              :   !> @brief calculate thermostatistic observables
     243              :   !> Cv, binderU4 and chi
     244              :   !-----------------------------------------------------------------------
     245         2002 :   subroutine get_thermo_obs(self )
     246              :     class(spin_observable_t) :: self
     247              :     real(dp) :: avgm
     248              :     ABI_UNUSED(self%nspin)
     249              :     ! Cv
     250         2002 :     self%avg_E_t = (self%avg_E_t*self%ntime + self%energy)/(self%ntime+1)
     251         2002 :     self%avg_E2_t = (self%avg_E2_t*self%ntime + self%energy**2)/(self%ntime+1)
     252         2002 :     if(self%temperature<1d-12) then
     253            0 :        self%Cv=0.0d0
     254              :     else
     255         2002 :        self%Cv = (self%avg_E2_t-self%avg_E_t**2)/self%temperature**2
     256              :     end if
     257              : 
     258              :     !
     259         2002 :     avgm=self%Mst_norm_total
     260              :     !avgm=self%M_total_norm
     261              : 
     262         2002 :     self%avg_m_t =  (self%avg_m_t*self%ntime + avgm)/(self%ntime+1)
     263         2002 :     self%avg_m2_t = (self%avg_m2_t*self%ntime + avgm**2)/(self%ntime+1)
     264         2002 :     self%avg_m4_t = (self%avg_m4_t*self%ntime + avgm**4)/(self%ntime+1)
     265              : 
     266         2002 :     self%binderU4 = 1.0-self%avg_m4_t/self%avg_m2_t**2/3.0
     267              : 
     268         2002 :     if(self%temperature<1d-12) then
     269            0 :        self%chi=(self%avg_m2_t-self%avg_m_t**2)
     270              :     else
     271         2002 :        self%chi = (self%avg_m2_t-self%avg_m_t**2)/self%temperature
     272              :     endif
     273         2002 :   end subroutine get_thermo_obs
     274              : 
     275              : 
     276              :   !-----------------------------------------------------------------------
     277              :   !> @brief calculate correlation function relatated observables
     278              :   !-----------------------------------------------------------------------
     279            0 :   subroutine get_correlation_obs(self)
     280              :     class(spin_observable_t) :: self
     281              :     ABI_UNUSED(self%nspin)
     282            0 :   end subroutine get_correlation_obs
     283              : 
     284              :   !-----------------------------------------------------------------------
     285              :   !> @brief calculate all observables from input
     286              :   !> @param [in] S
     287              :   !> @param [in] Snorm
     288              :   !> @param [in] energy
     289              :   !-----------------------------------------------------------------------
     290         2002 :   subroutine get_observables(self, S, Snorm, energy)
     291              : 
     292              :     class(spin_observable_t) :: self
     293              :     real(dp), intent(in) :: S(3,self%nspin), Snorm(self%nspin), energy
     294         2002 :     call update(self, S, Snorm, energy)
     295         2002 :     call get_staggered_M(self)
     296         2002 :     if(self%calc_traj_obs) then
     297              :        call get_traj_obs(self)
     298              :     end if
     299         2002 :     if(self%calc_thermo_obs) then
     300         2002 :        call get_thermo_obs(self)
     301              :     end if
     302              :     if(self%calc_correlation_obs) then
     303              :        call get_correlation_obs(self)
     304              :     endif
     305         2002 :     self%ntime=self%ntime+1
     306              : 
     307         2002 :   end subroutine get_observables
     308              : 
     309              : 
     310            0 : end module m_spin_observables
        

Generated by: LCOV version 2.3-1