Line data Source code
1 : !!****m* ABINIT/m_lattice_mover
2 : !! NAME
3 : !! m_lattice_mover
4 : !!
5 : !! FUNCTION
6 : !! This module contains the lattice mover.
7 : !!
8 : !!
9 : !! Datatypes:
10 : !!
11 : !! * lattice_mover_t: defines the lattice movers
12 : !!
13 : !! Subroutines:
14 : !! TODO: add this when F2003 doc style is determined.
15 : !!
16 : !!
17 : !! COPYRIGHT
18 : !! Copyright (C) 2001-2026 ABINIT group (hexu)
19 : !! This file is distributed under the terms of the
20 : !! GNU General Public License, see ~abinit/COPYING
21 : !! or http://www.gnu.org/copyleft/gpl.txt .
22 : !! For the initials of contributorsi see ~abinit/doc/developers/contributors.txt .
23 : !!
24 : !! SOURCE
25 :
26 :
27 :
28 : #if defined HAVE_CONFIG_H
29 : #include "config.h"
30 : #endif
31 :
32 : #include "abi_common.h"
33 :
34 : module m_lattice_mover
35 : use defs_basis
36 : use m_abicore
37 : use m_errors
38 : use m_xmpi
39 : use m_multibinit_dataset, only: multibinit_dtset_type
40 : use m_abstract_potential, only: abstract_potential_t
41 : use m_abstract_mover, only: abstract_mover_t
42 : use m_multibinit_cell, only: mbcell_t, mbsupercell_t
43 : use m_random_xoroshiro128plus, only: rng_t
44 : use m_hashtable_strval, only: hash_table_t
45 : use m_lattice_ncfile, only: lattice_ncfile_t
46 : use m_mpi_scheduler, only: init_mpi_info
47 : !!***
48 :
49 : implicit none
50 : private
51 :
52 : type ,public, extends(abstract_mover_t) :: lattice_mover_t
53 : !> This is the abstract lattice mover
54 :
55 : type(multibinit_dtset_type), pointer :: params=>null() ! input parameters
56 : integer :: natom=0 ! number of atoms
57 : real(dp) :: stress(3,3), strain(3,3) ! stress and strain
58 : real(dp), allocatable :: masses(:) ! masses
59 : integer :: latt_dynamics=0 ! type of lattice dynamics
60 :
61 : ! hexu: is xcart needed?
62 : real(dp), allocatable :: current_xcart(:,:) ! xcart of current step
63 : real(dp), allocatable :: current_vcart(:,:) ! vcart of current step
64 : real(dp), allocatable :: forces(:,:) ! forces
65 : real(dp), allocatable :: displacement(:,:) ! displacement
66 : real(dp) :: energy ! total energy
67 : real(dp) :: Ek ! kinetic energy
68 : real(dp) :: T_ob ! observed temperature
69 : logical :: is_null = .True.
70 : !> TODO: hist
71 : !type(lattice_hist_t) :: hist
72 :
73 : real(dp) :: mass_total
74 : type(lattice_ncfile_t) :: ncfile
75 : contains
76 : procedure:: initialize ! perhaps each effpot type should have own
77 : procedure :: finalize
78 : procedure :: set_params
79 : procedure :: prepare_ncfile
80 : procedure :: set_ncfile_name
81 : procedure :: set_initial_state ! initial state
82 : procedure :: set_temperature
83 : procedure :: force_stationary
84 : procedure :: run_one_step
85 : procedure :: run_time
86 : procedure :: run_varT
87 : procedure :: reset ! reset the mover
88 : procedure :: get_T_and_Ek ! calculate temperature and kinetic energy.
89 : procedure :: calc_observables ! call functions to calculate observables
90 : procedure :: write_hist ! write hist file
91 : end type lattice_mover_t
92 :
93 :
94 : contains
95 :
96 : !-------------------------------------------------------------------!
97 : ! Initialize:
98 : !
99 : ! Inputs:
100 : !> params: input parameters
101 : !> supercell: supercell.
102 : !> rng: random number generator
103 : !-------------------------------------------------------------------!
104 5 : subroutine initialize(self, params, supercell, rng)
105 : class(lattice_mover_t), intent(inout) :: self
106 : type(multibinit_dtset_type),target, intent(in) :: params
107 : type(mbsupercell_t),target, intent(in) :: supercell
108 : type(rng_t), target, intent(in) :: rng
109 5 : self%params=>params
110 5 : self%supercell=>supercell
111 5 : self%label="Lattice Mover"
112 5 : self%natom = supercell%lattice%natom
113 15 : ABI_MALLOC(self%masses, (self%natom))
114 15 : ABI_MALLOC(self%displacement, (3, self%natom))
115 15 : ABI_MALLOC(self%current_xcart, (3, self%natom))
116 15 : ABI_MALLOC(self%current_vcart, (3, self%natom))
117 15 : ABI_MALLOC(self%forces, (3,self%natom))
118 5 : self%is_null=.False.
119 65 : self%strain(:,:) = 0.0
120 65 : self%stress(:,:) = 0.0
121 18565 : self%forces(:,:) = 0.0
122 18565 : self%displacement(:,:) = 0.0
123 18565 : self%current_vcart(:,:) = 0.0
124 5 : call self%set_params(params)
125 5 : call self%set_rng(rng)
126 5 : end subroutine initialize
127 :
128 : !-------------------------------------------------------------------!
129 : ! Finalize:
130 : !-------------------------------------------------------------------!
131 5 : subroutine finalize(self)
132 : class(lattice_mover_t), intent(inout) :: self
133 5 : nullify(self%supercell)
134 5 : nullify(self%params)
135 5 : self%label="Destroyed lattice mover"
136 5 : if (.not.self%is_null) then
137 5 : ABI_FREE(self%masses)
138 5 : ABI_FREE(self%current_xcart)
139 5 : ABI_FREE(self%current_vcart)
140 5 : ABI_FREE(self%forces)
141 5 : ABI_FREE(self%displacement)
142 : endif
143 5 : self%is_null=.True.
144 5 : end subroutine finalize
145 :
146 : !-------------------------------------------------------------------!
147 : ! Set the mover using the input parameters
148 : !-------------------------------------------------------------------!
149 5 : subroutine set_params(self, params)
150 : ! set parameters from input file. (something else, like temperature for MvT calculation?)
151 : class(lattice_mover_t), intent(inout) :: self
152 : type(multibinit_dtset_type) :: params
153 5 : self%temperature = params%temperature !TODO: to Hartree ??
154 5 : self%dt =params%dtion
155 4645 : self%masses(:)=self%supercell%lattice%masses(:)
156 4645 : self%mass_total = sum(self%masses)
157 5 : self%total_time = self%dt * params%ntime
158 5 : self%latt_dynamics = params%dynamics
159 5 : end subroutine set_params
160 :
161 : !-------------------------------------------------------------------!
162 : ! Set the mover temperature
163 : !-------------------------------------------------------------------!
164 0 : subroutine set_temperature(self, temperature)
165 : class(lattice_mover_t), intent(inout) :: self
166 : real(dp),intent(in) :: temperature
167 0 : self%temperature = temperature !TODO: to Hartree ??
168 0 : end subroutine set_temperature
169 :
170 :
171 : !-------------------------------------------------------------------!
172 : ! set initial state:
173 : ! Inputs:
174 : ! mode: integer
175 : ! if mode=1, use a Boltzman distribution to init the velocities.
176 : ! if mode=2, ...
177 : !-------------------------------------------------------------------!
178 5 : subroutine set_initial_state(self, mode)
179 : ! set initial positions, spin, etc
180 : class(lattice_mover_t), intent(inout) :: self
181 : integer, optional, intent(in) :: mode
182 10 : real(dp) :: xi(3, self%natom)
183 : integer :: i
184 :
185 :
186 5 : if(mode==1) then ! using a boltzmann distribution.
187 : ! Should only be used for a constant Temperature mover
188 : ! which includes:
189 : ! 102: Langevin
190 : ! 103: Brendesen
191 5 : if (.not.( &
192 : self%latt_dynamics==101 .or. & ! TODO remove
193 : self%latt_dynamics==102 .or. self%latt_dynamics==103 ) ) then
194 0 : ABI_ERROR("Only set lattice initial state with a Boltzmann distribution in a constant T mover.")
195 : end if
196 5 : call self%rng%rand_normal_array(xi, 3*self%natom)
197 4645 : do i=1, self%natom
198 18565 : self%current_vcart(:,i) = xi(:, i) *sqrt(self%temperature/self%masses(i))
199 : end do
200 5 : call self%force_stationary()
201 5 : call self%get_T_and_Ek()
202 18565 : self%current_xcart(:, :) = self%supercell%lattice%xcart(:,:)
203 0 : else if(mode==2) then ! Use reference structure and 0 velocity.
204 : ! other modes.
205 0 : if(self%latt_dynamics==102 .or. self%latt_dynamics==103 ) then
206 0 : ABI_ERROR("Displacement and velocity set to zero in a NVT mover.")
207 : end if
208 0 : do i=1, self%natom
209 0 : self%current_vcart(:,i) = 0.0
210 : end do
211 0 : self%current_xcart(:, :) = self%supercell%lattice%xcart(:,:)
212 0 : call self%get_T_and_Ek()
213 : end if
214 :
215 :
216 5 : end subroutine set_initial_state
217 :
218 3 : subroutine prepare_ncfile(self, params, fname)
219 : class(lattice_mover_t), intent(inout) :: self
220 : type(multibinit_dtset_type) :: params
221 : character(len=*), intent(in) :: fname
222 : integer :: master, my_rank, comm, nproc
223 : logical :: iam_master
224 3 : ABI_UNUSED_A(params)
225 3 : call init_mpi_info(master, iam_master, my_rank, comm, nproc)
226 3 : if(iam_master) then
227 3 : call self%ncfile%initialize( trim(fname), 1)
228 3 : call self%ncfile%write_cell(self%supercell)
229 3 : call self%ncfile%def_lattice_var()
230 : end if
231 3 : end subroutine prepare_ncfile
232 :
233 :
234 : !-------------------------------------------------------------------!
235 : !set_ncfile_name :
236 : !-------------------------------------------------------------------!
237 3 : subroutine set_ncfile_name(self, params, fname)
238 : class(lattice_mover_t), intent(inout) :: self
239 : type(multibinit_dtset_type) :: params
240 : character(len=fnlen), intent(in) :: fname
241 : integer :: master, my_rank, comm, nproc
242 : logical :: iam_master
243 3 : call init_mpi_info(master, iam_master, my_rank, comm, nproc)
244 3 : if (iam_master) then
245 3 : call self%prepare_ncfile(params, trim(fname)//'_latthist.nc')
246 3 : call self%ncfile%write_one_step(self%current_xcart, self%current_vcart, self%energy, self%Ek )
247 : endif
248 3 : end subroutine set_ncfile_name
249 :
250 :
251 :
252 : !-------------------------------------------------------------------!
253 : ! Make sure the mass center does not move.
254 : !-------------------------------------------------------------------!
255 2005 : subroutine force_stationary(self)
256 : class(lattice_mover_t), intent(inout) :: self
257 : integer :: i
258 : real(dp) :: p(3), pavg(3)
259 2005 : p(:)=0.0
260 2166645 : do i = 1, self%natom
261 8660565 : p(:)=p(:)+self%current_vcart(:,i) * self%masses(i)
262 : end do
263 8020 : pavg=p/self%mass_total
264 2166645 : do i = 1, self%natom
265 8660565 : self%current_vcart(:, i) = self%current_vcart(:, i) - pavg(:)
266 : end do
267 2005 : end subroutine force_stationary
268 :
269 :
270 : !-------------------------------------------------------------------!
271 : ! Force the temperature strictly.
272 : ! Since the boltzman distribution has some fluctuation
273 : !-------------------------------------------------------------------!
274 : subroutine force_temperature(self)
275 : class(lattice_mover_t), intent(inout) :: self
276 : ABI_UNUSED_A(self)
277 : end subroutine force_temperature
278 :
279 :
280 : !-------------------------------------------------------------------!
281 : ! run_one_step
282 : ! run one step of dynamics.
283 : ! Should be overrided.
284 : ! Inputs:
285 : !> effpot: effective potential
286 : !> displacement: should NOT be provided, since it is already stored.
287 : !> strain: Also should NOT be provided.
288 : !> spin: should be provided only if there is spin-lattice coupling
289 : !> lwf : should be provided only if there is lattice-lwf coupling (unlikely)
290 : !> energy_table: energy_table.
291 : !-------------------------------------------------------------------!
292 0 : subroutine run_one_step(self, effpot, displacement, strain, spin, lwf, energy_table)
293 : ! run one step. (For MC also?)
294 : class(lattice_mover_t), intent(inout) :: self ! array of effective potentials so that there can be multiple of them.
295 : class(abstract_potential_t), intent(inout) :: effpot
296 : real(dp), optional, intent(inout) :: displacement(:,:), strain(:,:), spin(:,:), lwf(:)
297 : type(hash_table_t), optional, intent(inout) :: energy_table
298 :
299 : character(len=40) :: key
300 :
301 0 : if(present(displacement) .or. present(strain)) then
302 0 : ABI_ERROR("displacement and strain should not be input for lattice mover")
303 : end if
304 :
305 0 : ABI_BUG("The abstract lattice mover is used, which should be a bug.")
306 :
307 0 : ABI_UNUSED_A(self)
308 0 : ABI_UNUSED_A(effpot)
309 0 : ABI_UNUSED_A(displacement)
310 0 : ABI_UNUSED_A(strain)
311 0 : ABI_UNUSED_A(spin)
312 0 : ABI_UNUSED_A(lwf)
313 0 : ABI_UNUSED_A(energy_table)
314 :
315 0 : call self%get_T_and_Ek()
316 0 : if (present(energy_table)) then
317 0 : key = 'Lattice kinetic energy'
318 0 : call energy_table%put(key, self%Ek)
319 : end if
320 0 : end subroutine run_one_step
321 :
322 :
323 : !-------------------------------------------------------------------!
324 : !get_temperature_and_kinetic_energy
325 : ! Ek = 1/2 \sum m_i vi^2
326 : ! T = 2/3 Ek/natom (in a.u.)
327 : !-------------------------------------------------------------------!
328 4017 : subroutine get_T_and_Ek(self)
329 : class(lattice_mover_t), intent(inout) :: self
330 : integer :: i
331 4017 : self%Ek=0.0
332 4334017 : do i =1, self%natom
333 : self%Ek = self%Ek+ 0.5* self%masses(i) * &
334 17324017 : &sum(self%current_vcart(:,i)*self%current_vcart(:,i))
335 : end do
336 : ! temperature
337 4017 : self%T_ob = 2.0*self%Ek/(3*self%natom)
338 4017 : end subroutine get_T_and_Ek
339 :
340 :
341 : !-------------------------------------------------------------------!
342 : ! run from begining to end.
343 : !-------------------------------------------------------------------!
344 3 : subroutine run_time(self, effpot, displacement, strain, spin, lwf, energy_table)
345 : class(lattice_mover_t), intent(inout) :: self
346 : ! array of effective potentials so that there can be multiple of them.
347 : class(abstract_potential_t), intent(inout) :: effpot
348 : real(dp), optional, intent(inout) :: displacement(:,:), strain(:,:), spin(:,:), lwf(:)
349 : type(hash_table_t), optional, intent(inout) :: energy_table
350 : integer :: i, nstep
351 : character(len=90) :: msg
352 3 : if(present(displacement) .or. present(strain)) then
353 0 : ABI_ERROR("displacement and strain should not be input for lattice mover")
354 : end if
355 3 : ABI_UNUSED_A(self)
356 3 : ABI_UNUSED_A(effpot)
357 3 : ABI_UNUSED_A(spin)
358 3 : ABI_UNUSED_A(lwf)
359 3 : ABI_UNUSED_A(energy_table)
360 :
361 :
362 3 : msg=repeat("=", 90)
363 3 : call wrtout(std_out,msg,'COLL')
364 3 : call wrtout(ab_out, msg, 'COLL')
365 3 : write(msg, '(A22)') "Lattice dynamic steps:"
366 3 : call wrtout(std_out,msg,'COLL')
367 3 : call wrtout(ab_out, msg, 'COLL')
368 3 : msg=repeat("=", 90)
369 3 : call wrtout(std_out,msg,'COLL')
370 3 : call wrtout(ab_out, msg, 'COLL')
371 :
372 : write(msg, "(A13, 4X, A15, 4X, A15, 4X, A15, 4X, A15)") &
373 3 : & "Iteration", "temperature(K)", "Ekin(Ha/uc)", &
374 6 : & "Epot(Ha/uc)", "ETOT(Ha/uc)"
375 3 : call wrtout(std_out,msg,'COLL')
376 3 : call wrtout(ab_out, msg, 'COLL')
377 :
378 3 : nstep=floor(self%thermal_time/self%dt)
379 3 : do i =1, nstep
380 3 : call self%run_one_step(effpot=effpot, spin=spin, lwf=lwf, energy_table=energy_table)
381 : end do
382 :
383 3 : nstep=floor(self%total_time/self%dt)
384 2013 : do i =1, nstep
385 6030 : call self%run_one_step(effpot=effpot, spin=spin, lwf=lwf, energy_table=energy_table)
386 2013 : if(modulo(i, self%params%nctime)==0) then
387 210 : write(msg, "(I13, 4X, F15.5, 4X, ES15.5, 4X, ES15.5, 4X, ES15.5)") i, self%T_ob*Ha_K, &
388 210 : & self%Ek/self%supercell%ncell, self%energy/self%supercell%ncell, &
389 420 : & (self%Ek+self%energy)/self%supercell%ncell
390 210 : call wrtout(std_out,msg,'COLL')
391 210 : call wrtout(ab_out, msg, 'COLL')
392 877220 : self%current_xcart = self%supercell%lattice%xcart+self%displacement
393 210 : call self%ncfile%write_one_step(self%current_xcart, self%current_vcart, self%energy, self%Ek)
394 : end if
395 : !TODO: output, observables
396 : end do
397 :
398 3 : msg=repeat("=", 90)
399 3 : call wrtout(std_out,msg,'COLL')
400 3 : call wrtout(ab_out, msg, 'COLL')
401 :
402 3 : end subroutine run_time
403 :
404 :
405 : !-------------------------------------------------------------------!
406 : ! Reset:
407 : ! It reset the counter of steps, but does not set the initial state
408 : ! again.
409 : !-------------------------------------------------------------------!
410 0 : subroutine reset(self)
411 : ! reset the state of mover (e.g. counter->0)
412 : ! so it can be reused.
413 : class(lattice_mover_t), intent(inout) :: self
414 0 : ABI_UNUSED_A(self)
415 0 : end subroutine reset
416 :
417 : !-------------------------------------------------------------------!
418 : !Calc_observables
419 : !
420 : !-------------------------------------------------------------------!
421 0 : subroutine calc_observables(self)
422 : ! call functions to calculate observables.
423 : class(lattice_mover_t), intent(inout) :: self
424 0 : ABI_UNUSED_A(self)
425 0 : end subroutine calc_observables
426 :
427 : !-------------------------------------------------------------------!
428 : ! Write_hist: write to hist file
429 : !-------------------------------------------------------------------!
430 0 : subroutine write_hist(self)
431 : ! write to hist file
432 : class(lattice_mover_t), intent(inout) :: self
433 0 : ABI_UNUSED_A(self)
434 :
435 0 : end subroutine write_hist
436 :
437 : !-------------------------------------------------------------------!
438 : ! Get_state: get the current state
439 : !-------------------------------------------------------------------!
440 : subroutine get_state(self, displacement, strain, spin, lwf, ihist)
441 : ! get the state of the ihist(th) step. ihist can be 0 (current), -1 (last), ... -maxhist..
442 : class(lattice_mover_t), intent(in):: self
443 : real(dp), optional, intent(inout) :: displacement, strain, spin, lwf
444 : integer, optional, intent(in):: ihist
445 : ABI_UNUSED_A(self)
446 : ABI_UNUSED_A(displacement)
447 : ABI_UNUSED_A(strain)
448 : ABI_UNUSED_A(spin)
449 : ABI_UNUSED_A(lwf)
450 : ABI_UNUSED_A(ihist)
451 : end subroutine get_state
452 :
453 :
454 : !!****f* m_lwf_mover/run_varT
455 : !!
456 : !! NAME
457 : !! run_varT
458 : !!
459 : !! FUNCTION
460 : !! run M vs Temperature
461 : !!
462 : !! INPUTS
463 : !! pot: potential
464 : !! T_start, Tend, T_nstep
465 : !u
466 : !! OUTPUT
467 : !!
468 : !! SOURCE
469 0 : subroutine run_varT(self, pot, ncfile_prefix, displacement, strain, spin, lwf, energy_table)
470 : class(lattice_mover_t), intent(inout) :: self
471 : class(abstract_potential_t), intent(inout) :: pot
472 : real(dp), optional, intent(inout) :: displacement(:,:), strain(:,:), lwf(:), spin(:,:)
473 : character(fnlen), intent(inout) :: ncfile_prefix
474 : type(hash_table_t), optional, intent(inout) :: energy_table
475 : real(dp) :: T_start, T_end
476 : integer :: T_nstep
477 : !type(lwf_ncfile_t) :: lwf_ncfile
478 : character(len=4) :: post_fname
479 : real(dp) :: T, T_step
480 : integer :: i
481 : !integer :: Tfile, iostat
482 : character(len=90) :: msg
483 : !character(len=4200) :: Tmsg ! to write to var T file
484 : !character(len=150) :: iomsg
485 : !character(fnlen) :: Tfname ! file name for output various T calculation
486 : !real(dp), allocatable :: Tlist(:), chi_list(:), Cv_list(:), binderU4_list(:)
487 : !real(dp), allocatable :: Mst_sub_norm_list(:, :)
488 : !real(dp), allocatable :: Mst_norm_total_list(:)
489 :
490 : integer :: master, my_rank, comm, nproc, ierr
491 : logical :: iam_master
492 0 : call init_mpi_info(master, iam_master, my_rank, comm, nproc)
493 :
494 0 : ABI_UNUSED_A(displacement)
495 0 : ABI_UNUSED_A(strain)
496 :
497 0 : if (iam_master) then
498 0 : T_start=self%params%latt_temperature_start
499 0 : T_end=self%params%latt_temperature_end
500 0 : T_nstep=self%params%latt_temperature_nstep
501 : !Tfile=get_unit()
502 : !Tfname = trim(ncfile_prefix)//'.varT'
503 : !iostat=open_file(file=Tfname, unit=Tfile, iomsg=iomsg )
504 0 : if (T_nstep<=1) then
505 : T_step=0.0
506 : else
507 0 : T_step=(T_end-T_start)/(T_nstep-1)
508 : endif
509 : write(msg, "(A52, ES13.5, A11, ES13.5, A1)") &
510 0 : & "Starting temperature dependent calculations. T from ", &
511 0 : & T_start*Ha_K, "K to ", T_end*Ha_K, " K."
512 0 : call wrtout(std_out, msg, "COLL")
513 0 : call wrtout(ab_out, msg, "COLL")
514 : end if
515 :
516 0 : call xmpi_bcast(T_nstep, 0, comm, ierr)
517 0 : do i=1, T_nstep
518 0 : if(iam_master) then
519 0 : T=T_start+(i-1)*T_step
520 0 : msg=repeat("=", 79)
521 0 : call wrtout(std_out, msg, "COLL")
522 0 : call wrtout(ab_out, msg, "COLL")
523 :
524 0 : write(msg, "(A13, 5X, ES13.5, A3)") "Temperature: ", T*Ha_K, " K."
525 0 : call wrtout(std_out, msg, "COLL")
526 0 : call wrtout(ab_out, msg, "COLL")
527 :
528 : ! set temperature
529 : ! TODO make this into a subroutine set_params
530 : endif
531 0 : call self%set_temperature(temperature=T)
532 0 : if(iam_master) then
533 0 : if(i==1) then
534 0 : call self%set_initial_state(mode=1)
535 : endif
536 :
537 0 : write(post_fname, "(I4.4)") i
538 : call self%prepare_ncfile( self%params, &
539 0 : & trim(ncfile_prefix)//'_T'//post_fname//'_latthist.nc')
540 0 : call self%ncfile%write_one_step(self%current_xcart, self%current_vcart, self%energy, self%Ek )
541 : endif
542 :
543 : call self%run_time(pot, spin=spin, &
544 0 : & lwf=lwf, energy_table=energy_table)
545 :
546 0 : if(iam_master) then
547 0 : call self%ncfile%finalize()
548 : endif
549 : end do
550 :
551 0 : end subroutine run_varT
552 : !!***
553 :
554 :
555 :
556 15 : end module m_lattice_mover
557 :
|