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
|