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
|