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
|