Octopus
ions.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!! Copyright (C) 2021 M. Oliveira
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21
22module ions_oct_m
23 use atom_oct_m
24 use blas_oct_m
27 use comm_oct_m
28 use iso_c_binding
29 use debug_oct_m
31 use global_oct_m
35 use io_oct_m
37 use, intrinsic :: iso_fortran_env
41 use math_oct_m
44 use mpi_oct_m
46 use parser_oct_m
51 use space_oct_m
54 use spglib_f08
57 use system_oct_m
59 use unit_oct_m
63
64 implicit none
65
66 private
67 public :: ions_t
68
69 type, extends(charged_particles_t) :: ions_t
70 ! Components are public by default
71
72 type(lattice_vectors_t) :: latt
73
74 integer :: natoms
75 type(atom_t), allocatable :: atom(:)
76
77 type(symmetries_t) :: symm
78
79 type(distributed_t) :: atoms_dist
80
81 integer, allocatable :: map_symm_atoms(:,:)
82 integer, allocatable :: inv_map_symm_atoms(:,:)
83
84 real(real64), allocatable :: equilibrium_pos(:,:)
85 ! !! for multitrajectory runs.
86 ! !!
87 ! !! This is necessary, as the displacements are added before we
88 ! !! generate the grid, but the grid should always be constructed
89 ! !! for the equilibrium positions.
90 real(real64), allocatable :: pos_displacements(:,:)
91 real(real64), allocatable :: vel_displacements(:,:)
92
93 ! Information about the species
94 integer :: nspecies
95 class(species_wrapper_t), allocatable :: species(:)
96 logical :: only_user_def
97 logical, private :: species_time_dependent
98
99 logical :: force_total_enforce
100 type(ion_interaction_t) :: ion_interaction
101
103 logical, private :: apply_global_force
104 type(tdf_t), private :: global_force_function
105 contains
106 procedure :: copy => ions_copy
107 generic :: assignment(=) => copy
108 procedure :: partition => ions_partition
109 procedure :: init_interaction => ions_init_interaction
110 procedure :: initialize => ions_initialize
111 procedure :: update_quantity => ions_update_quantity
112 procedure :: init_interaction_as_partner => ions_init_interaction_as_partner
113 procedure :: copy_quantities_to_interaction => ions_copy_quantities_to_interaction
114 procedure :: fold_atoms_into_cell => ions_fold_atoms_into_cell
115 procedure :: min_distance => ions_min_distance
116 procedure :: has_time_dependent_species => ions_has_time_dependent_species
117 procedure :: val_charge => ions_val_charge
118 procedure :: dipole => ions_dipole
119 procedure :: translate => ions_translate
120 procedure :: rotate => ions_rotate
121 procedure :: global_force => ions_global_force
122 procedure :: write_xyz => ions_write_xyz
123 procedure :: read_xyz => ions_read_xyz
124 procedure :: write_poscar => ions_write_poscar
125 procedure :: write_crystal => ions_write_crystal
126 procedure :: write_bild_forces_file => ions_write_bild_forces_file
127 procedure :: write_vtk_geometry => ions_write_vtk_geometry
128 procedure :: update_lattice_vectors => ions_update_lattice_vectors
129 procedure :: symmetrize_atomic_coord => ions_symmetrize_atomic_coord
130 procedure :: generate_mapping_symmetric_atoms => ions_generate_mapping_symmetric_atoms
131 procedure :: print_spacegroup => ions_print_spacegroup
132 procedure :: current => ions_current
133 procedure :: abs_current => ions_abs_current
134 procedure :: init_random_displacements => ions_init_random_displacements
135 procedure :: single_mode_displacements => ions_single_mode_displacements
136 final :: ions_finalize
137 end type ions_t
138
139 interface ions_t
140 procedure ions_constructor
141 end interface ions_t
142
143contains
144
145 ! ---------------------------------------------------------
146 function ions_constructor(namespace, grp, print_info, latt_inp) result(ions)
147 type(namespace_t), intent(in) :: namespace
148 type(mpi_grp_t), optional, intent(in) :: grp
149 logical, optional, intent(in) :: print_info
150 type(lattice_vectors_t), optional, intent(out) :: latt_inp
151 class(ions_t), pointer :: ions
152
153 type(read_coords_info) :: xyz
154 integer :: ia, ierr, idir
155 character(len=100) :: function_name
156 real(real64) :: mindist
157 real(real64), allocatable :: factor(:)
158 integer, allocatable :: site_type(:)
159 logical, allocatable :: spherical_site(:)
160 real(real64), parameter :: threshold = 1e-5_real64
161 type(species_factory_t) :: factory
162 real(real64) :: T
163 integer :: displacement_mode
164 real(real64) :: amp_pos, amp_vel
165
166 logical :: in_ensemble
168 push_sub_with_profile(ions_constructor)
170 allocate(ions)
171
172 ions%namespace = namespace
173 ions%space = space_t(namespace)
175 ! In the legacy framework, electrons and ions share the same namespace
176 ! We use an optional group in this case to avoid double entry in the hashmap
177 if (present(grp)) then
178 call system_init_parallelization(ions, grp)
179 else
180 ions%grp = mpi_world
181 end if
182
183 in_ensemble = parse_is_defined(namespace, "NumberOfReplicas")
184
185 call species_factory_init(factory, namespace)
187 ! initialize geometry
188 call read_coords_init(xyz)
190 ! load positions of the atoms
191 call read_coords_read('Coordinates', xyz, ions%space, namespace)
193 if (xyz%n < 1) then
194 message(1) = "Coordinates have not been defined."
195 call messages_fatal(1, namespace=namespace)
196 end if
197
198 ! Initialize parent class
199 call charged_particles_init(ions, xyz%n)
200
201 ! copy information from xyz to ions
202 ions%natoms = xyz%n
203 safe_allocate(ions%atom(1:ions%natoms))
204 do ia = 1, ions%natoms
205 call atom_init(ions%atom(ia), ions%space%dim, xyz%atom(ia)%label)
206 ions%pos(:,ia) = xyz%atom(ia)%x(1:ions%space%dim)
207 if (bitand(xyz%flags, xyz_flags_move) /= 0) then
208 ions%fixed(ia) = .not. xyz%atom(ia)%move
209 end if
210 end do
212 if (allocated(xyz%latvec)) then
213 ! Build lattice vectors from the XSF input
214 ions%latt = lattice_vectors_t(namespace, ions%space, xyz%latvec)
215 else
216 ! Build lattice vectors from input file
217 ions%latt = lattice_vectors_t(namespace, ions%space)
218 end if
220 ! Convert coordinates to Cartesian in case we have reduced coordinates
221 if (xyz%source == read_coords_reduced) then
222 do ia = 1, ions%natoms
223 ions%pos(:, ia) = ions%latt%red_to_cart(ions%pos(:, ia))
224 end do
225 end if
229 ! Save the positions read from the input before applying deviations from the initial_input
230 safe_allocate_source_a(ions%equilibrium_pos, ions%pos)
232 call ions_init_species(ions, factory, print_info=print_info)
233
234 ! Set the masses and charges. This needs to be done after initializing the species.
235 do ia = 1, ions%natoms
236 ions%mass(ia) = ions%atom(ia)%species%get_mass()
237 ions%charge(ia) = ions%atom(ia)%species%get_zval()
238 end do
239
240 !%Variable InitialDisplacementMode
241 !%Type integer
242 !%Default 0
243 !%Section System
244 !%Description
245 !% When this variable is set and non-zero, the phonon modes file will be read, and ionic
246 !% positions and velocities will be displaced according to a single phonon mode.
247 !% The amplitudes can be defined with the variables:
248 !% - InitialDisplacementsAmplitudePos
249 !% - InitialDisplacementsAmplitudeVel
250 !%End
251 call parse_variable(namespace, 'InitialDisplacementMode', 0, displacement_mode)
252
253 !%Variable InitialDisplacementAmplitudePos
254 !%Type float
255 !%Default 1.0
256 !%Section System
257 !%Description
258 !% Amplitude for initial position displacement according to the phonon mode, defined by InitialDisplacementMode.
259 !% The value corresponds to the normal distributed canonical coordinates.
260 !%End
261 call parse_variable(namespace, 'InitialDisplacementAmplitudePos', 1.0_real64, amp_pos)
262
263 !%Variable InitialDisplacementAmplitudeVel
264 !%Type float
265 !%Default 1.0
266 !%Section System
267 !%Description
268 !% Amplitude for initial velocity displacement according to the phonon mode, defined by InitialDisplacementMode.
269 !% The value corresponds to the normal distributed canonical coordinates.
270 !%End
271 call parse_variable(namespace, 'InitialDisplacementAmplitudeVel', 1.0_real64, amp_vel)
272
273 if (displacement_mode>0) then
274
275 message(1) = "Random displacements are disabled when InitialDisplacementMode > 0."
276 call messages_warning(1, namespace=namespace)
277
278 safe_allocate(ions%pos_displacements(1:ions%space%dim, 1:ions%natoms))
279 safe_allocate(ions%vel_displacements(1:ions%space%dim, 1:ions%natoms))
280
281 call ions%single_mode_displacements(displacement_mode, amp_pos, amp_vel)
282
283 ! apply initial displacements
284 ions%pos = ions%pos + ions%pos_displacements
285
286 ! note that the velocity displacement needs to be applied in ions%initialize()
287
288 end if
289
290 ! Now we can apply displacements to the positions, if requested.
291 ! The random displacements are disabled if a single mode is specified,
292 if(in_ensemble .and. displacement_mode==0) then
293
294 !%Variable EnsembleTemperature
295 !%Type float
296 !%Default 0
297 !%Section Multi-Trajectory
298 !%Description
299 !% The temperature for an ensemble calculation in Kelvin
300 !%End
301 call parse_variable(namespace, 'EnsembleTemperature', 0.0_real64, t)
302
303 safe_allocate(ions%pos_displacements(1:ions%space%dim, 1:ions%natoms))
304 safe_allocate(ions%vel_displacements(1:ions%space%dim, 1:ions%natoms))
305 call ions%init_random_displacements(t)
306
307 ! apply initial displacements
308 ions%pos = ions%pos + ions%pos_displacements
309
310 ! note that the velocity displacement needs to be applied in ions%initialize()
311
312 end if
313
315 call distributed_nullify(ions%atoms_dist, ions%natoms)
316
317 call messages_obsolete_variable(namespace, 'PDBClassical')
318
319 if (present(latt_inp)) then
320 ! The lattice as read from the input might be needed by some other part of the code, so we save it
321 latt_inp = ions%latt
322 end if
323
324 ! Now that we have processed the atomic coordinates, we renormalize the
325 ! lattice parameters along the non-periodic dimensions
326 if (ions%space%has_mixed_periodicity()) then
327 safe_allocate(factor(ions%space%dim))
328 do idir = 1, ions%space%periodic_dim
329 factor(idir) = m_one
330 end do
331 do idir = ions%space%periodic_dim + 1, ions%space%dim
332 factor(idir) = m_one/norm2(ions%latt%rlattice(1:ions%space%dim, idir))
333 end do
334 call ions%latt%scale(factor)
335 safe_deallocate_a(factor)
336 end if
337
338 ! Check that atoms are not too close
339 if (ions%natoms > 1) then
340 mindist = ions_min_distance(ions, real_atoms_only = .false.)
341 if (mindist < threshold) then
342 write(message(1), '(a)') "Some of the atoms seem to sit too close to each other."
343 write(message(2), '(a)') "Please review your input files and the output geometry (in 'static/')."
344 write(message(3), '(a, f12.6, 1x, a)') "Minimum distance = ", &
345 units_from_atomic(units_out%length, mindist), trim(units_abbrev(units_out%length))
346 call messages_warning(3, namespace=namespace)
347
348 ! then write out the geometry, whether asked for or not in Output variable
349 call io_mkdir(static_dir, namespace)
350 call ions%write_xyz(trim(static_dir)//'/geometry')
351 end if
352
353 if (ions_min_distance(ions, real_atoms_only = .true.) < threshold) then
354 message(1) = "It cannot be correct to run with physical atoms so close."
355 call messages_fatal(1, namespace=namespace)
356 end if
357 end if
358
359 !Initialize symmetries
360 safe_allocate(spherical_site(1:ions%natoms))
361 safe_allocate(site_type(1:ions%natoms))
362 do ia = 1, ions%natoms
363 select type(spec => ions%atom(ia)%species)
364 type is(jellium_slab_t)
365 spherical_site(ia) = .false.
367 spherical_site(ia) = .false.
368 type is(species_from_file_t)
369 spherical_site(ia) = .false.
371 spherical_site(ia) = .false.
372 class default
373 spherical_site(ia) = .true.
374 end select
375
376 site_type(ia) = ions%atom(ia)%species%get_index()
377 end do
378
379 ions%symm = symmetries_t(ions%namespace, ions%space, ions%latt, ions%natoms, ions%pos, site_type, spherical_site)
380
381 safe_deallocate_a(spherical_site)
382 safe_deallocate_a(site_type)
383
384 ! Generate the mapping of symmetric atoms
385 call ions%generate_mapping_symmetric_atoms()
386
387 call ion_interaction_init(ions%ion_interaction, namespace, ions%space, ions%natoms)
388
389 !%Variable ForceTotalEnforce
390 !%Type logical
391 !%Default no
392 !%Section Hamiltonian
393 !%Description
394 !% (Experimental) If this variable is set to "yes", then the sum
395 !% of the total forces will be enforced to be zero.
396 !%End
397 call parse_variable(namespace, 'ForceTotalEnforce', .false., ions%force_total_enforce)
398 if (ions%force_total_enforce) call messages_experimental('ForceTotalEnforce', namespace=namespace)
399
400 !%Variable TDGlobalForce
401 !%Type string
402 !%Section Time-Dependent
403 !%Description
404 !% If this variable is set, a global time-dependent force will be
405 !% applied to the ions in the x direction during a time-dependent
406 !% run. This variable defines the base name of the force, that
407 !% should be defined in the <tt>TDFunctions</tt> block. This force
408 !% does not affect the electrons directly.
409 !%End
410
411 if (parse_is_defined(namespace, 'TDGlobalForce')) then
412
413 ions%apply_global_force = .true.
414
415 call parse_variable(namespace, 'TDGlobalForce', 'none', function_name)
416 call tdf_read(ions%global_force_function, namespace, trim(function_name), ierr)
417
418 if (ierr /= 0) then
419 call messages_write("You have enabled the GlobalForce option but Octopus could not find")
420 call messages_write("the '"//trim(function_name)//"' function in the TDFunctions block.")
421 call messages_fatal(namespace=namespace)
422 end if
423
424 else
425
426 ions%apply_global_force = .false.
427
428 end if
429
430 call species_factory_end(factory)
431
432 pop_sub_with_profile(ions_constructor)
433 end function ions_constructor
434
435 ! ---------------------------------------------------------
436 subroutine ions_init_species(ions, factory, print_info)
437 type(ions_t), intent(inout) :: ions
438 type(species_factory_t), intent(in) :: factory
439 logical, optional, intent(in) :: print_info
440
441 logical :: print_info_, spec_user_defined
442 integer :: i, j, k, ispin
443 class(species_t), pointer :: spec
444
445 push_sub_with_profile(ions_init_species)
446
447 print_info_ = .true.
448 if (present(print_info)) then
449 print_info_ = print_info
450 end if
451 ! First, count the species
452 ions%nspecies = 0
453 atoms1: do i = 1, ions%natoms
454 do j = 1, i - 1
455 if (atom_same_species(ions%atom(j), ions%atom(i))) cycle atoms1
456 end do
457 ions%nspecies = ions%nspecies + 1
458 end do atoms1
459
460 ! Allocate the species structure.
461 allocate(ions%species(1:ions%nspecies))
462
463 ! Now, read the data.
464 k = 0
465 ions%only_user_def = .true.
466 atoms2: do i = 1, ions%natoms
467 do j = 1, i - 1
468 if (atom_same_species(ions%atom(j), ions%atom(i))) cycle atoms2
469 end do
470 k = k + 1
471 ions%species(k)%s => factory%create_from_input(ions%namespace, ions%atom(j)%get_label(), k)
472 ! these are the species which do not represent atoms
473 select type(spec => ions%species(k)%s)
474 class is(jellium_t)
475
476 class default
477 ions%only_user_def = .false.
478 end select
479
480 select type(spec => ions%species(k)%s)
481 type is(pseudopotential_t)
482 if (ions%space%dim /= 3) then
483 message(1) = "Pseudopotentials may only be used with Dimensions = 3."
484 call messages_fatal(1, namespace=ions%namespace)
485 end if
486
487 type is(jellium_slab_t)
488 if (ions%space%is_periodic() .and. ions%space%periodic_dim /= 2) then
489 message(1) = "Periodic jelium slab can only be used if PeriodicDim = 2"
490 call messages_fatal(1, namespace=ions%namespace)
491 end if
492 end select
493
494 end do atoms2
495
496 ! Reads the spin components. This is read here, as well as in states_init,
497 ! to be able to pass it to the pseudopotential initializations subroutine.
498 call parse_variable(ions%namespace, 'SpinComponents', 1, ispin)
499 if (.not. varinfo_valid_option('SpinComponents', ispin)) call messages_input_error(ions%namespace, 'SpinComponents')
500 ispin = min(2, ispin)
501
502 if (print_info_) then
503 call messages_print_with_emphasis(msg="Species", namespace=ions%namespace)
504 end if
505 do i = 1, ions%nspecies
506 spec => ions%species(i)%s
507 call spec%build(ions%namespace, ispin, ions%space%dim, print_info=print_info_)
508 end do
509 if (print_info_) then
510 call messages_print_with_emphasis(namespace=ions%namespace)
511 end if
512
513 !%Variable SpeciesTimeDependent
514 !%Type logical
515 !%Default no
516 !%Section System::Species
517 !%Description
518 !% When this variable is set, the potential defined in the block <tt>Species</tt> is calculated
519 !% and applied to the Hamiltonian at each time step. You must have at least one <tt>species_user_defined</tt>
520 !% type of species to use this.
521 !%End
522 call parse_variable(ions%namespace, 'SpeciesTimeDependent', .false., ions%species_time_dependent)
523 ! we must have at least one user defined species in order to have time dependency
524 spec_user_defined = .false.
525 do i = 1,ions%nspecies
526 select type(spec=>ions%species(i)%s)
528 spec_user_defined = .true.
529 end select
530 end do
531 if (ions%species_time_dependent .and. .not. spec_user_defined) then
532 call messages_input_error(ions%namespace, 'SpeciesTimeDependent')
533 end if
534
535 ! assign species
536 do i = 1, ions%natoms
537 do j = 1, ions%nspecies
538 if (atom_same_species(ions%atom(i), ions%species(j)%s)) then
539 call atom_set_species(ions%atom(i), ions%species(j)%s)
540 exit
541 end if
542 end do
543 end do
544
545 pop_sub_with_profile(ions_init_species)
546 end subroutine ions_init_species
547
548 !--------------------------------------------------------------
549 subroutine ions_copy(ions_out, ions_in)
550 class(ions_t), intent(out) :: ions_out
551 class(ions_t), intent(in) :: ions_in
552
553 push_sub(ions_copy)
554
555 call charged_particles_copy(ions_out, ions_in)
556
557 ions_out%latt = ions_in%latt
558
559 ions_out%natoms = ions_in%natoms
560 safe_allocate(ions_out%atom(1:ions_out%natoms))
561 ions_out%atom = ions_in%atom
562
563 ions_out%nspecies = ions_in%nspecies
564 allocate(ions_out%species(1:ions_out%nspecies))
565 ions_out%species = ions_in%species
566
567 ions_out%only_user_def = ions_in%only_user_def
568
569 call distributed_copy(ions_in%atoms_dist, ions_out%atoms_dist)
570
571 safe_allocate(ions_out%map_symm_atoms(1:ions_in%natoms, 1:ions_in%symm%nops + ions_in%symm%nops_nonsymmorphic))
572 ions_out%map_symm_atoms = ions_in%map_symm_atoms
573 safe_allocate(ions_out%inv_map_symm_atoms(1:ions_in%natoms, 1:ions_in%symm%nops + ions_in%symm%nops_nonsymmorphic))
574 ions_out%inv_map_symm_atoms = ions_in%inv_map_symm_atoms
575
576
577 pop_sub(ions_copy)
578 end subroutine ions_copy
579
580 ! ---------------------------------------------------------
581 subroutine ions_partition(this, mc)
582 class(ions_t), intent(inout) :: this
583 type(multicomm_t), intent(in) :: mc
584
585 push_sub(ions_partition)
586
587 call distributed_init(this%atoms_dist, this%natoms, mc%group_comm(p_strategy_states), "atoms")
588
589 call ion_interaction_init_parallelization(this%ion_interaction, this%natoms, mc)
590
591 call mpi_grp_init(this%grp, mc%master_comm)
592
593 pop_sub(ions_partition)
594 end subroutine ions_partition
595
596 ! ---------------------------------------------------------
597 subroutine ions_init_interaction(this, interaction)
598 class(ions_t), target, intent(inout) :: this
599 class(interaction_t), intent(inout) :: interaction
600
601 push_sub(ions_init_interaction)
602
603 select type (interaction)
604 class default
605 call charged_particles_init_interaction(this, interaction)
606 end select
607
608 pop_sub(ions_init_interaction)
609 end subroutine ions_init_interaction
610
611 ! ---------------------------------------------------------
619 !
620 subroutine ions_init_random_displacements(ions, T)
621 class(ions_t), intent(inout) :: ions
622 real(real64), intent(in) :: T
623
624
625 integer(int64) :: seed
626
627 type(phonon_modes_t) :: phonons
628 type(wigner_distribution_t) :: wigner
629
630 real(real64), allocatable :: sigmaP(:)
631 real(real64), allocatable :: muP(:)
632 real(real64), allocatable :: sigmaQ(:)
633 real(real64), allocatable :: muQ(:)
634 real(real64), allocatable :: genP(:)
635 real(real64), allocatable :: genQ(:)
636
637
638 real(real64) :: beta_half
639 real(real64), allocatable :: pos_displacements(:), vel_displacements(:)
640 real(real64), allocatable :: l_m(:), prefactor(:)
641
642 integer :: imode, idim, iatom, i, iline
643 integer :: num_real_modes
645 real(real64), parameter :: low_temperature_tolerance = 1.0e-6_real64
646
647
649
650 ! create a unique seed for each replica, based on a hash of the full (unique) namespace string.
651 seed = ions%namespace%get_hash32()
652
653 message(1) = "Create initial random displacements for ions."
654 call messages_info(1, namespace = ions%namespace)
655
656 write(message(1), '("namespace = ",A)') ions%namespace%get()
657 write(message(2), '("seed = ",I0)') seed
658 call messages_info(2, namespace = ions%namespace, debug_only=.true.)
659
660
661 ! initialize the phonons (read info from file)
662 call phonons%init(ions%namespace, ions%space%dim, ions%natoms, ions%space%periodic_dim > 0)
663
664 num_real_modes = phonons%num_modes
665
666 if (num_real_modes == 0) then
667
668 ions%pos_displacements = m_zero
669 ions%vel_displacements = m_zero
670
672 return
673 end if
674
675
676 ! initialize wigner distribution for phonons%num_modes modes and with given seed
677 call wigner%init(num_real_modes, seed)
678
679 ! set up widths and shifts for the Wigner function
680 safe_allocate(sigmap(1:num_real_modes))
681 safe_allocate(sigmaq(1:num_real_modes))
682 safe_allocate(mup(1:num_real_modes))
683 safe_allocate(muq(1:num_real_modes))
684 safe_allocate(genp(1:num_real_modes))
685 safe_allocate(genq(1:num_real_modes))
686
687 safe_allocate(pos_displacements(1:(ions%space%dim*ions%natoms)))
688 safe_allocate(vel_displacements(1:(ions%space%dim*ions%natoms)))
689 safe_allocate(prefactor(1:(ions%space%dim*ions%natoms)))
690
691 safe_allocate(l_m(1:num_real_modes))
693
694 if (t < low_temperature_tolerance) then
695 ! TODO: Implement proper low T limit
696 sigmap = 1.0_real64/2.0_real64
697 sigmaq = 1.0_real64/2.0_real64
698 else
699 beta_half = m_one / (2 * p_kb * t)
700 sigmap = 1.0_real64/sqrt((2.0_real64 * tanh(beta_half * phonons%frequencies)))
701 sigmaq = 1.0_real64/sqrt((2.0_real64 * tanh(beta_half * phonons%frequencies)))
702 end if
703 mup = m_zero
704 muq = m_zero
705
706 l_m = sqrt(2.0_real64/(unit_amu%factor * phonons%frequencies)) * phonons%alpha
707
708 i = 1
709 do iatom = 1, ions%natoms
710 do idim = 1, ions%space%dim
711 prefactor(i) = 1.0_real64/sqrt(ions%mass(iatom)/unit_amu%factor * phonons%num_super)
712 i=i+1
713 end do
714 end do
716 ! get generalized mode coords from Wigner function
717 genq = wigner%get(sigmaq, muq, wigner_q) * l_m
718 genp = wigner%get(sigmap, mup, wigner_p) / (l_m * unit_amu%factor) ! TODO: Check!!
719
720 ! construct initial displacements and velocities for ions (velocities must be applied later)
721
722 ions%pos_displacements = m_zero
723 ions%vel_displacements = m_zero
724
725 ! implement eq.(9) from Kevins paper for periodic systems
726
727 do imode = 1, num_real_modes
728
729 pos_displacements = prefactor(:) * phonons%eigenvectors(:, imode) * genq(imode)
730 vel_displacements = prefactor(:) * phonons%eigenvectors(:, imode) * genp(imode)
731
732 iline = 1
733 write(message(iline), '("Displacements for mode ",I4)') imode
734 do iatom=1, ions%natoms
735 iline = iline+1
736 write(message(iline), '(3E15.5)') pos_displacements((iatom-1)*ions%space%dim+1:(iatom-1)*ions%space%dim+ions%space%dim)
737 end do
738 iline = iline+1
739 write(message(iline), '("Velocities for mode ",I4)') imode
740 do iatom=1, ions%natoms
741 iline = iline+1
742 write(message(iline), '(3E15.5)') vel_displacements((iatom-1)*ions%space%dim+1:(iatom-1)*ions%space%dim+ions%space%dim)
743 end do
744
745 call messages_info(iline, namespace=ions%namespace)
746
747 call blas_axpy(phonons%dim, 1.0_real64, pos_displacements(1), 1, ions%pos_displacements(1,1), 1)
748 call blas_axpy(phonons%dim, 1.0_real64, vel_displacements(1), 1, ions%vel_displacements(1,1), 1)
749
750 end do
751
752 safe_deallocate_a(sigmap)
753 safe_deallocate_a(sigmaq)
754 safe_deallocate_a(mup)
755 safe_deallocate_a(muq)
756 safe_deallocate_a(genp)
757 safe_deallocate_a(genq)
758 safe_deallocate_a(l_m)
759
760 safe_deallocate_a(prefactor)
761 safe_deallocate_a(pos_displacements)
762 safe_deallocate_a(vel_displacements)
763
765
766 end subroutine ions_init_random_displacements
767
768
772 !
773 subroutine ions_single_mode_displacements(ions, mode, amplitude_pos, amplitude_vel)
774 class(ions_t), intent(inout) :: ions
775 integer, intent(in) :: mode
776 real(real64), optional, intent(in) :: amplitude_pos
777 real(real64), optional, intent(in) :: amplitude_vel
778
779 type(phonon_modes_t) :: phonons
780 integer :: num_real_modes, i, iatom, idim
781 real(real64) :: l_m, genP, genQ
782
783 real(real64), allocatable :: pos_displacements(:), vel_displacements(:), prefactor(:)
784
785
787
788 write(message(1), '("Create displacements for mode ", I4)') mode
789 call messages_info(1, namespace=ions%namespace)
790
791 ! initialize the phonons (read info from file)
792 call phonons%init(ions%namespace, ions%space%dim, ions%natoms, ions%space%periodic_dim > 0)
793
794 num_real_modes = phonons%num_modes
795
796 if (mode > num_real_modes) then
797 write(message(1), '("Requested mode ",I0," exceeds number of available modes (",I0,")")') mode, num_real_modes
798 call messages_fatal(1, namespace=ions%namespace)
799
801 return
802 end if
803
804 ions%pos_displacements = m_zero
805 ions%vel_displacements = m_zero
806
807 l_m = sqrt(2.0_real64/(unit_amu%factor * phonons%frequencies(mode))) * phonons%alpha(mode)
808
809 safe_allocate(pos_displacements(1:(ions%space%dim*ions%natoms)))
810 safe_allocate(vel_displacements(1:(ions%space%dim*ions%natoms)))
811 safe_allocate(prefactor(1:(ions%space%dim*ions%natoms)))
812
813 i = 1
814 do iatom = 1, ions%natoms
815 do idim=1, ions%space%dim
816 prefactor(i) = 1.0_real64/sqrt(ions%mass(iatom)/unit_amu%factor * phonons%num_super)
817 i=i+1
818 end do
819 end do
820
821 ! get generalized mode coords from Wigner function
822 genq = amplitude_pos * l_m
823 genp = amplitude_vel / (l_m * unit_amu%factor)! TODO: Check!!
824
825 pos_displacements = prefactor(:) * phonons%eigenvectors(:, mode) * genq
826 vel_displacements = prefactor(:) * phonons%eigenvectors(:, mode) * genp
827
828 write(message(1), '("Displacements for mode ",I4)') mode
829 call messages_info(1, namespace=ions%namespace)
830 do iatom=1, ions%natoms
831 write(message(1), '(3E15.5)') pos_displacements((iatom-1)*ions%space%dim+1:(iatom-1)*ions%space%dim+ions%space%dim)
832 call messages_info(1, namespace=ions%namespace)
833 end do
834 write(message(1), '("Velocities for mode ",I4)') mode
835 call messages_info(1, namespace=ions%namespace)
836 do iatom=1, ions%natoms
837 write(message(1), '(3E15.5)') vel_displacements((iatom-1)*ions%space%dim+1:(iatom-1)*ions%space%dim+ions%space%dim)
838 call messages_info(1, namespace=ions%namespace)
839 end do
840
841
842 call blas_axpy(phonons%dim, 1.0_real64, pos_displacements(1), 1, ions%pos_displacements(1,1), 1)
843 call blas_axpy(phonons%dim, 1.0_real64, vel_displacements(1), 1, ions%vel_displacements(1,1), 1)
844
845 safe_deallocate_a(pos_displacements)
846 safe_deallocate_a(vel_displacements)
847 safe_deallocate_a(prefactor)
848
850
851 end subroutine ions_single_mode_displacements
852
853 ! ---------------------------------------------------------
854 subroutine ions_initialize(this)
855 class(ions_t), intent(inout) :: this
856
857 push_sub(ions_initialize)
858
859 ! At this point, we need to apply initial velocities from the random displacements.
860 if(allocated(this%vel_displacements)) then
861 this%vel = this%vel + this%vel_displacements
862 end if
863
864 pop_sub(ions_initialize)
865 end subroutine ions_initialize
866
867 ! ---------------------------------------------------------
868 subroutine ions_update_quantity(this, label)
869 class(ions_t), intent(inout) :: this
870 character(len=*), intent(in) :: label
871
872 push_sub(ions_update_quantity)
873
874 select case (label)
875 case default
876 ! Other quantities should be handled by the parent class
877 call charged_particles_update_quantity(this, label)
878 end select
879
880 pop_sub(ions_update_quantity)
881 end subroutine ions_update_quantity
882
883 ! ---------------------------------------------------------
884 subroutine ions_init_interaction_as_partner(partner, interaction)
885 class(ions_t), intent(in) :: partner
886 class(interaction_surrogate_t), intent(inout) :: interaction
887
889
890 select type (interaction)
891 class default
892 call charged_particles_init_interaction_as_partner(partner, interaction)
893 end select
894
897
898 ! ---------------------------------------------------------
899 subroutine ions_copy_quantities_to_interaction(partner, interaction)
900 class(ions_t), intent(inout) :: partner
901 class(interaction_surrogate_t), intent(inout) :: interaction
902
904
905 select type (interaction)
906 class default
907 ! Other interactions should be handled by the parent class
908 call charged_particles_copy_quantities_to_interaction(partner, interaction)
909 end select
910
913
914 ! ---------------------------------------------------------
915 subroutine ions_fold_atoms_into_cell(this)
916 class(ions_t), intent(inout) :: this
917
918 integer :: iatom
919
921
922 do iatom = 1, this%natoms
923 this%pos(:, iatom) = this%latt%fold_into_cell(this%pos(:, iatom))
924 end do
925
927 end subroutine ions_fold_atoms_into_cell
928
929 ! ---------------------------------------------------------
930 real(real64) function ions_min_distance(this, real_atoms_only) result(rmin)
931 class(ions_t), intent(in) :: this
932 logical, optional, intent(in) :: real_atoms_only
933
934 integer :: iatom, jatom, idir
935 real(real64) :: xx(this%space%dim)
936 logical :: real_atoms_only_
937 class(species_t), pointer :: species
938
939 if (this%natoms == 1 .and. .not. this%space%is_periodic()) then
940 rmin = m_huge
941 return
942 end if
943
944 push_sub(ions_min_distance)
945
946 real_atoms_only_ = optional_default(real_atoms_only, .false.)
947
948 ! Without this line, valgrind complains about a conditional jump on uninitialized variable,
949 ! as atom_get_species has an intent(out) that causes a call to the finalizer (with Ifort)
950 nullify(species)
951
952 rmin = huge(rmin)
953 do iatom = 1, this%natoms
954 call atom_get_species(this%atom(iatom), species)
955 select type(species)
956 class is(jellium_t)
957 if (real_atoms_only_) cycle
958 end select
959 do jatom = iatom + 1, this%natoms
960 call atom_get_species(this%atom(jatom), species)
961 select type(species)
962 class is(jellium_t)
963 if (real_atoms_only_) cycle
964 end select
965 xx = abs(this%pos(:, iatom) - this%pos(:, jatom))
966 if (this%space%is_periodic()) then
967 xx = this%latt%cart_to_red(xx)
968 do idir = 1, this%space%periodic_dim
969 xx(idir) = xx(idir) - floor(xx(idir) + m_half)
970 end do
971 xx = this%latt%red_to_cart(xx)
972 end if
973 rmin = min(norm2(xx), rmin)
974 end do
975 end do
976
977 if (.not. (this%only_user_def .and. real_atoms_only_)) then
978 ! what if the nearest neighbors are periodic images?
979 do idir = 1, this%space%periodic_dim
980 rmin = min(rmin, norm2(this%latt%rlattice(:,idir)))
981 end do
982 end if
983
984 ! To avoid numerical instabilities, we round this to 6 digits only
985 if(rmin < huge(rmin)/1e6_real64) then
986 rmin = anint(rmin*1e6_real64)*1.0e-6_real64
987 end if
988
989 pop_sub(ions_min_distance)
990 end function ions_min_distance
991
992 ! ---------------------------------------------------------
993 logical function ions_has_time_dependent_species(this) result(time_dependent)
994 class(ions_t), intent(in) :: this
995
997
998 time_dependent = this%species_time_dependent
999
1002
1003 ! ---------------------------------------------------------
1004 real(real64) function ions_val_charge(this, mask) result(val_charge)
1005 class(ions_t), intent(in) :: this
1006 logical, optional, intent(in) :: mask(:)
1007
1008 push_sub(ions_val_charge)
1009
1010 if (present(mask)) then
1011 val_charge = -sum(this%charge, mask=mask)
1012 else
1013 val_charge = -sum(this%charge)
1014 end if
1015
1016 pop_sub(ions_val_charge)
1017 end function ions_val_charge
1018
1019 ! ---------------------------------------------------------
1020 function ions_dipole(this, mask) result(dipole)
1021 class(ions_t), intent(in) :: this
1022 logical, optional, intent(in) :: mask(:)
1023 real(real64) :: dipole(this%space%dim)
1024
1025 integer :: ia
1026
1027 push_sub(ions_dipole)
1028
1029 dipole = m_zero
1030 do ia = 1, this%natoms
1031 if (present(mask)) then
1032 if (.not. mask(ia)) cycle
1033 end if
1034 dipole = dipole + this%charge(ia)*this%pos(:, ia)
1035 end do
1036 dipole = p_proton_charge*dipole
1037
1038 pop_sub(ions_dipole)
1039 end function ions_dipole
1040
1041 ! ---------------------------------------------------------
1042 subroutine ions_translate(this, xx)
1043 class(ions_t), intent(inout) :: this
1044 real(real64), intent(in) :: xx(this%space%dim)
1045
1046 integer :: iatom
1047
1048 push_sub(ions_translate)
1049
1050 do iatom = 1, this%natoms
1051 this%pos(:, iatom) = this%pos(:, iatom) - xx
1052 end do
1053
1054 pop_sub(ions_translate)
1055 end subroutine ions_translate
1056
1057 ! ---------------------------------------------------------
1058 subroutine ions_rotate(this, from, from2, to)
1059 class(ions_t), intent(inout) :: this
1060 real(real64), intent(in) :: from(this%space%dim)
1061 real(real64), intent(in) :: from2(this%space%dim)
1062 real(real64), intent(in) :: to(this%space%dim)
1063
1064 integer :: iatom
1065 real(real64) :: m1(3, 3), m2(3, 3)
1066 real(real64) :: m3(3, 3), f2(3), per(3)
1067 real(real64) :: alpha, r
1068
1069 push_sub(ions_rotate)
1070
1071 if (this%space%dim /= 3) then
1072 call messages_not_implemented("ions_rotate in other than 3 dimensions", namespace=this%namespace)
1073 end if
1074
1075 ! initialize matrices
1076 m1 = diagonal_matrix(3, m_one)
1077
1078 ! rotate the to-axis to the z-axis
1079 if (abs(to(2)) > 1d-150) then
1080 alpha = atan2(to(2), to(1))
1081 call rotate(m1, alpha, 3)
1082 end if
1083 alpha = atan2(norm2(to(1:2)), to(3))
1084 call rotate(m1, -alpha, 2)
1085
1086 ! get perpendicular to z and from
1087 f2 = matmul(m1, from)
1088 per(1) = -f2(2)
1089 per(2) = f2(1)
1090 per(3) = m_zero
1091 r = norm2(per)
1092 if (r > m_zero) then
1093 per = per/r
1094 else
1095 per(2) = m_one
1096 end if
1097
1098 ! rotate perpendicular axis to the y-axis
1099 m2 = diagonal_matrix(3, m_one)
1100 alpha = atan2(per(1), per(2))
1101 call rotate(m2, -alpha, 3)
1102
1103 ! rotate from => to (around the y-axis)
1104 m3 = diagonal_matrix(3, m_one)
1105 alpha = acos(min(sum(from*to), m_one))
1106 call rotate(m3, -alpha, 2)
1107
1108 ! join matrices
1109 m2 = matmul(transpose(m2), matmul(m3, m2))
1110
1111 ! rotate around the z-axis to get the second axis
1112 per = matmul(m2, matmul(m1, from2))
1113 alpha = atan2(per(1), per(2))
1114 call rotate(m2, -alpha, 3) ! second axis is now y
1116 ! get combined transformation
1117 m1 = matmul(transpose(m1), matmul(m2, m1))
1118
1119 ! now transform the coordinates
1120 ! it is written in this way to avoid what I consider a bug in the Intel compiler
1121 do iatom = 1, this%natoms
1122 f2 = this%pos(:, iatom)
1123 this%pos(:, iatom) = matmul(m1, f2)
1124 end do
1125
1126 pop_sub(ions_rotate)
1127 contains
1128
1129 ! ---------------------------------------------------------
1130 subroutine rotate(m, angle, dir)
1131 real(real64), intent(inout) :: m(3, 3)
1132 real(real64), intent(in) :: angle
1133 integer, intent(in) :: dir
1134
1135 real(real64) :: aux(3, 3), ca, sa
1136
1138
1139 ca = cos(angle)
1140 sa = sin(angle)
1141
1142 aux = m_zero
1143 select case (dir)
1144 case (1)
1145 aux(1, 1) = m_one
1146 aux(2, 2) = ca
1147 aux(3, 3) = ca
1148 aux(2, 3) = sa
1149 aux(3, 2) = -sa
1150 case (2)
1151 aux(2, 2) = m_one
1152 aux(1, 1) = ca
1153 aux(3, 3) = ca
1154 aux(1, 3) = sa
1155 aux(3, 1) = -sa
1156 case (3)
1157 aux(3, 3) = m_one
1158 aux(1, 1) = ca
1159 aux(2, 2) = ca
1160 aux(1, 2) = sa
1161 aux(2, 1) = -sa
1162 end select
1163
1164 m = matmul(aux, m)
1165
1166 pop_sub(ions_rotate.rotate)
1167 end subroutine rotate
1168
1169 end subroutine ions_rotate
1170
1171 ! ---------------------------------------------------------
1172 function ions_global_force(this, time) result(force)
1173 class(ions_t), intent(in) :: this
1174 real(real64), intent(in) :: time
1175 real(real64) :: force(this%space%dim)
1176
1177 push_sub(ions_global_force)
1178
1179 force = m_zero
1180
1181 if (this%apply_global_force) then
1182 force(1) = tdf(this%global_force_function, time)
1183 end if
1184
1185 pop_sub(ions_global_force)
1186 end function ions_global_force
1187
1188 ! ---------------------------------------------------------
1189 subroutine ions_write_xyz(this, fname, append, comment, reduce_coordinates)
1190 class(ions_t), intent(in) :: this
1191 character(len=*), intent(in) :: fname
1192 logical, optional, intent(in) :: append
1193 character(len=*), optional, intent(in) :: comment
1194 logical, optional, intent(in) :: reduce_coordinates
1195
1196 integer :: iatom, idim, iunit
1197 character(len=6) position
1198 character(len=19) :: frmt
1199 real(real64) :: red(this%space%dim)
1200
1201 if (.not. this%grp%is_root()) return
1202
1203 push_sub(ions_write_xyz)
1204
1205 position = 'asis'
1206 if (present(append)) then
1207 if (append) position = 'append'
1208 end if
1209 if(.not.optional_default(reduce_coordinates, .false.)) then
1210 iunit = io_open(trim(fname)//'.xyz', this%namespace, action='write', position=position)
1211 else
1212 iunit = io_open(trim(fname)//'.xyz_red', this%namespace, action='write', position=position)
1213 end if
1214
1215 write(iunit, '(i4)') this%natoms
1216 if (present(comment)) then
1217 write(iunit, '(1x,a)') comment
1218 else
1219 write(iunit, '(1x,a,a)') 'units: ', trim(units_abbrev(units_out%length_xyz_file))
1220 end if
1221
1222 write(unit=frmt, fmt="(a5,i2.2,a4,i2.2,a6)") "(6x,a", label_len, ",2x,", this%space%dim,"f15.9)"
1223 do iatom = 1, this%natoms
1224 if(.not.optional_default(reduce_coordinates, .false.)) then
1225 write(unit=iunit, fmt=frmt) this%atom(iatom)%label, &
1226 (units_from_atomic(units_out%length_xyz_file, this%pos(idim, iatom)), idim=1, this%space%dim)
1227 else
1228 red = this%latt%cart_to_red(this%pos(:, iatom))
1229 write(unit=iunit, fmt=frmt) this%atom(iatom)%label, (red(idim), idim=1, this%space%dim)
1230 end if
1231 end do
1232 call io_close(iunit)
1233
1234 pop_sub(ions_write_xyz)
1235 end subroutine ions_write_xyz
1236
1241 !
1242 subroutine ions_write_poscar(this, fname, comment)
1243 class(ions_t), intent(in) :: this
1244 character(len=*), optional, intent(in) :: fname
1245 character(len=*), optional, intent(in) :: comment
1246
1247 integer :: iatom, idim, iunit
1248 character(len=:), allocatable :: fname_, comment_
1249 character(len=10) :: format_string
1250
1251 push_sub(ions_write_poscar)
1252
1253 if (.not. this%grp%is_root()) then
1254 pop_sub(ions_write_poscar)
1255 return
1256 end if
1257
1258 comment_ = optional_default(comment, "")
1259 fname_ = optional_default(fname, "POSCAR")
1260
1261 iunit = io_open(trim(fname_), this%namespace, action='write')
1262
1263 write(iunit, '(A)') comment_ ! mandatory comment
1264 write(iunit, '("1.0")') ! scaling factor: we use 1 as lattice vectors are absolute
1265
1266 write(format_string, '("(",I1,A,")")') this%space%dim, 'F15.10'
1267 do idim=1, this%space%dim
1268 write(iunit, format_string) units_from_atomic(unit_angstrom, this%latt%rlattice(:,idim))
1269 end do
1270
1271 do iatom = 1, this%natoms
1272 write(iunit, '(A)', advance='NO') trim(this%atom(iatom)%label)//" "
1273 end do
1274 write(iunit, '("")')
1275 write(iunit, '(A)') repeat("1 ", this%natoms)
1276 write(iunit, '("direct")')
1277 write(format_string, '("(",I1,A,")")') this%space%dim, 'F15.10'
1278 do iatom=1, this%natoms
1279 write(iunit, format_string) this%latt%cart_to_red(this%pos(:, iatom))
1280 end do
1281
1282 call io_close(iunit)
1283
1285 end subroutine ions_write_poscar
1286
1287 ! ---------------------------------------------------------
1288 subroutine ions_read_xyz(this, fname, comment)
1289 use iso_fortran_env, only: real64
1290 class(ions_t), intent(inout) :: this
1291 character(len=*), intent(in) :: fname
1292 character(len=*), optional, intent(in) :: comment
1293
1294 integer :: iatom, idir, iunit, ios
1295 character(len=256) :: line
1296 character(len=LABEL_LEN) :: label
1297 character(len=:), allocatable :: coordstr
1298 real(real64) :: tmp(this%space%dim)
1299
1300 push_sub(ions_read_xyz)
1301
1302 iunit = io_open(trim(fname)//'.xyz', this%namespace, action='read', position='rewind')
1303
1304 ! --- First two lines: natoms + optional comment ------------------------
1305 read(iunit, '(i4)') this%natoms
1306 if (present(comment)) then
1307 read(iunit, *)
1308 else
1309 read(iunit, *)
1310 end if
1311
1312 ! --- Read atom lines ---------------------------------------------------
1313 do iatom = 1, this%natoms
1314
1315 ! Read entire line (safe even if long)
1316 read(iunit,'(A)', iostat=ios) line
1317 if (ios /= 0) then
1318 call io_close(iunit)
1319 message(1) = "Error reading XYZ atom line"
1320 call messages_fatal(1, namespace=this%namespace)
1321 end if
1322
1323 ! Extract fixed-length label (preserves spaces inside)
1324 if (len_trim(line) < label_len) then
1325 call io_close(iunit)
1326 message(1) = "XYZ file: atom line too short for label"
1327 call messages_fatal(1, namespace=this%namespace)
1328 end if
1329 label = line(1:label_len)
1330
1331 ! Extract remaining part for coordinates
1332 if (len(line) > label_len) then
1333 coordstr = adjustl(line(label_len+1:))
1334 else
1335 call io_close(iunit)
1336 message(1) = "XYZ file: no coordinate data after label"
1337 call messages_fatal(1, namespace=this%namespace)
1338 end if
1339
1340 ! Parse coordinates in a flexible (list-directed) way
1341 read(coordstr, *, iostat=ios) (tmp(idir), idir=1, this%space%dim)
1342 if (ios /= 0) then
1343 call io_close(iunit)
1344 message(1) = "XYZ file: malformed coordinate fields"
1345 call messages_fatal(1, namespace=this%namespace)
1346 end if
1347
1348 ! Convert and store
1349 this%pos(:, iatom) = units_to_atomic(units_out%length_xyz_file, tmp)
1350
1351 end do
1352
1353 call io_close(iunit)
1354
1355 pop_sub(ions_read_xyz)
1356 end subroutine ions_read_xyz
1357
1358 ! ----------------------------------------------------------------
1361 subroutine ions_write_crystal(this, dir)
1362 class(ions_t), intent(in) :: this
1363 character(len=*), intent(in) :: dir
1364
1365 type(lattice_iterator_t) :: latt_iter
1366 real(real64) :: radius, pos(this%space%dim)
1367 integer :: iatom, icopy, iunit
1368
1369 push_sub(ions_write_crystal)
1370
1371 radius = maxval(m_half*norm2(this%latt%rlattice(:,1:this%space%periodic_dim), dim=1))*(m_one + m_epsilon)
1372 latt_iter = lattice_iterator_t(this%latt, radius)
1373
1374 if (this%grp%is_root()) then
1375
1376 iunit = io_open(trim(dir)//'/crystal.xyz', this%namespace, action='write')
1377
1378 write(iunit, '(i9)') this%natoms*latt_iter%n_cells
1379 write(iunit, '(a)') '#generated by Octopus'
1380
1381 do iatom = 1, this%natoms
1382 do icopy = 1, latt_iter%n_cells
1383 pos = units_from_atomic(units_out%length, this%pos(:, iatom) + latt_iter%get(icopy))
1384 write(iunit, '(a, 99f12.6)') this%atom(iatom)%label, pos
1385 end do
1386 end do
1387
1388 call io_close(iunit)
1389 end if
1390
1391 pop_sub(ions_write_crystal)
1392 end subroutine ions_write_crystal
1393
1394 ! ---------------------------------------------------------
1395 subroutine ions_write_bild_forces_file(this, dir, fname)
1396 class(ions_t), intent(in) :: this
1397 character(len=*), intent(in) :: dir, fname
1398
1399 integer :: iunit, iatom, idir
1400 real(real64) :: force(this%space%dim), center(this%space%dim)
1401 character(len=20) frmt
1402
1403 if (.not. this%grp%is_root()) return
1404
1406
1407 call io_mkdir(dir, this%namespace)
1408 iunit = io_open(trim(dir)//'/'//trim(fname)//'.bild', this%namespace, action='write', &
1409 position='asis')
1410
1411 write(frmt,'(a,i0,a)')'(a,2(', this%space%dim,'f16.6,1x))'
1412
1413 write(iunit, '(a)')'.comment : force vectors in ['//trim(units_abbrev(units_out%force))//']'
1414 write(iunit, *)
1415 write(iunit, '(a)')'.color red'
1416 write(iunit, *)
1417 do iatom = 1, this%natoms
1418 center = units_from_atomic(units_out%length, this%pos(:, iatom))
1419 force = units_from_atomic(units_out%force, this%tot_force(:, iatom))
1420 write(iunit, '(a,1x,i4,1x,a2,1x,a6,1x,f10.6,a)') '.comment :', iatom, trim(this%atom(iatom)%label), &
1421 'force:', norm2(force), '['//trim(units_abbrev(units_out%force))//']'
1422 write(iunit,fmt=trim(frmt)) '.arrow', (center(idir), idir = 1, this%space%dim), &
1423 (center(idir) + force(idir), idir = 1, this%space%dim)
1424 write(iunit,*)
1425 end do
1426
1427 call io_close(iunit)
1428
1430 end subroutine ions_write_bild_forces_file
1431
1432 ! -----------------------------------------------------
1433 subroutine ions_write_vtk_geometry(this, filename, ascii)
1434 class(ions_t), intent(in) :: this
1435 character(len=*), intent(in) :: filename
1436 logical, optional, intent(in) :: ascii
1437
1438 integer :: iunit, iatom, ierr
1439 logical :: ascii_
1440 real(real64), allocatable :: data(:, :)
1441 integer, allocatable :: idata(:, :)
1442 character(len=MAX_PATH_LEN) :: fullname
1443
1444 push_sub(ions_write_vtk_geometry)
1445
1446 assert(this%space%dim == 3)
1447
1448 ascii_ = optional_default(ascii, .true.)
1449
1450 fullname = trim(filename)//".vtk"
1451
1452 iunit = io_open(trim(fullname), this%namespace, action='write')
1453
1454 write(iunit, '(1a)') '# vtk DataFile Version 3.0 '
1455 write(iunit, '(6a)') 'Generated by octopus ', trim(conf%version), ' - git: ', &
1456 trim(conf%git_commit), " configuration: ", trim(conf%config_time)
1457
1458 if (ascii_) then
1459 write(iunit, '(1a)') 'ASCII'
1460 else
1461 write(iunit, '(1a)') 'BINARY'
1462 end if
1463
1464 write(iunit, '(1a)') 'DATASET POLYDATA'
1465
1466 write(iunit, '(a,i9,a)') 'POINTS ', this%natoms, ' double'
1467
1468 if (ascii_) then
1469 do iatom = 1, this%natoms
1470 write(iunit, '(3f12.6)') this%pos(1:3, iatom)
1471 end do
1472 else
1473 call io_close(iunit)
1474 safe_allocate(data(1:3, 1:this%natoms))
1475 do iatom = 1, this%natoms
1476 data(1:3, iatom) = this%pos(1:3, iatom)
1477 end do
1478 call io_binary_write(io_workpath(fullname, this%namespace), i4_to_i8(3*this%natoms), data, &
1479 ierr, nohead = .true., fendian = io_binary_is_little_endian())
1480 safe_deallocate_a(data)
1481 iunit = io_open(trim(fullname), this%namespace, action='write', position = 'append')
1482 write(iunit, '(1a)') ''
1483 end if
1484
1485 write(iunit, '(a,2i9)') 'VERTICES ', this%natoms, 2*this%natoms
1486
1487 if (ascii_) then
1488 do iatom = 1, this%natoms
1489 write(iunit, '(2i9)') 1, iatom - 1
1490 end do
1491 else
1492 call io_close(iunit)
1493 safe_allocate(idata(1:2, 1:this%natoms))
1494 do iatom = 1, this%natoms
1495 idata(1, iatom) = 1
1496 idata(2, iatom) = iatom - 1
1497 end do
1498 call io_binary_write(io_workpath(fullname, this%namespace), i4_to_i8(2*this%natoms), idata, &
1499 ierr, nohead = .true., fendian = io_binary_is_little_endian())
1500 safe_deallocate_a(idata)
1501 iunit = io_open(trim(fullname), this%namespace, action='write', position = 'append')
1502 write(iunit, '(1a)') ''
1503 end if
1504
1505 write(iunit, '(a,i9)') 'POINT_DATA', this%natoms
1506 write(iunit, '(a)') 'SCALARS element integer'
1507 write(iunit, '(a)') 'LOOKUP_TABLE default'
1508
1509 if (ascii_) then
1510 do iatom = 1, this%natoms
1511 write(iunit, '(i9)') nint(this%atom(iatom)%species%get_z())
1512 end do
1513 else
1514 call io_close(iunit)
1515
1516 safe_allocate(idata(1:this%natoms, 1))
1517
1518 do iatom = 1, this%natoms
1519 idata(iatom, 1) = nint(this%atom(iatom)%species%get_z())
1520 end do
1521
1522 call io_binary_write(io_workpath(fullname, this%namespace), i4_to_i8(this%natoms), idata, &
1523 ierr, nohead = .true., fendian = io_binary_is_little_endian())
1524
1525 safe_deallocate_a(idata)
1526
1527 iunit = io_open(trim(fullname), this%namespace, action='write', position = 'append')
1528 write(iunit, '(1a)') ''
1529 end if
1530
1531 call io_close(iunit)
1532
1534 end subroutine ions_write_vtk_geometry
1535
1536 ! ---------------------------------------------------------
1537 function ions_current(this) result(current)
1538 class(ions_t), intent(in) :: this
1539 real(real64) :: current(this%space%dim)
1540
1541 integer :: iatom
1542
1543 push_sub(ions_current)
1544
1545 current = m_zero
1546 do iatom = this%atoms_dist%start, this%atoms_dist%end
1547 current = current + p_proton_charge*this%atom(iatom)%species%get_zval()*this%vel(:,iatom)
1548 end do
1549
1550 if (this%atoms_dist%parallel) then
1551 call comm_allreduce(this%atoms_dist%mpi_grp, current, dim=this%space%dim)
1552 end if
1553
1554 pop_sub(ions_current)
1555 end function ions_current
1556
1557 ! ---------------------------------------------------------
1558 function ions_abs_current(this) result(abs_current)
1559 class(ions_t), intent(in) :: this
1560 real(real64) :: abs_current(this%space%dim)
1561
1562 integer :: iatom
1563
1564 push_sub(ions_abs_current)
1565
1566 abs_current = m_zero
1567 do iatom = this%atoms_dist%start, this%atoms_dist%end
1568 abs_current = abs_current + abs(p_proton_charge*this%atom(iatom)%species%get_zval()*this%vel(:,iatom))
1569 end do
1570
1571 if (this%atoms_dist%parallel) then
1572 call comm_allreduce(this%atoms_dist%mpi_grp, abs_current, dim=this%space%dim)
1573 end if
1574
1575 pop_sub(ions_abs_current)
1576 end function ions_abs_current
1577
1578 ! ---------------------------------------------------------
1579 subroutine ions_finalize(ions)
1580 type(ions_t), intent(inout) :: ions
1581
1582 integer :: i
1583
1584 push_sub(ions_finalize)
1585
1586 call distributed_end(ions%atoms_dist)
1587
1588 call ion_interaction_end(ions%ion_interaction)
1589
1590 safe_deallocate_a(ions%atom)
1591 ions%natoms=0
1592
1593 if(allocated(ions%species)) then
1594 do i = 1, ions%nspecies
1595 !SAFE_ DEALLOCATE_P(ions%species(i)%s)
1596 if(associated(ions%species(i)%s)) deallocate(ions%species(i)%s)
1597 end do
1598 deallocate(ions%species)
1599 end if
1600 ions%nspecies=0
1601
1602 safe_deallocate_a(ions%pos_displacements)
1603 safe_deallocate_a(ions%vel_displacements)
1604
1605 call charged_particles_end(ions)
1606
1607 safe_deallocate_a(ions%map_symm_atoms)
1608 safe_deallocate_a(ions%inv_map_symm_atoms)
1609
1610
1611 pop_sub(ions_finalize)
1612 end subroutine ions_finalize
1613
1614 !-------------------------------------------------------------------
1617 subroutine ions_update_lattice_vectors(ions, latt, symmetrize)
1618 class(ions_t), intent(inout) :: ions
1619 type(lattice_vectors_t), intent(in) :: latt
1620 logical, intent(in) :: symmetrize
1621
1623
1624 ! Update the lattice vectors
1625 call ions%latt%update(latt%rlattice)
1626
1627 ! Regenerate symmetries in Cartesian space
1628 if (symmetrize) then
1629 call symmetries_update_lattice_vectors(ions%symm, latt, ions%space%dim)
1630 end if
1631
1633 end subroutine ions_update_lattice_vectors
1634
1635 !-------------------------------------------------------------------
1642 class(ions_t), intent(inout) :: ions
1643
1644 integer :: iatom, iop, iatom_symm, dim4symms
1645 real(real64) :: ratom(ions%space%dim)
1646
1648
1649 safe_allocate(ions%map_symm_atoms(1:ions%natoms, 1:ions%symm%nops + ions%symm%nops_nonsymmorphic))
1650 safe_allocate(ions%inv_map_symm_atoms(1:ions%natoms, 1:ions%symm%nops + ions%symm%nops_nonsymmorphic))
1651
1652 ! In the 4D case, symmetries are only defined in 3D, see symmetries.F90
1653 dim4symms = min(3, ions%space%dim)
1654 ratom = m_zero
1655
1656 do iop = 1, ions%symm%nops
1657 do iatom = 1, ions%natoms
1658 !We find the atom that correspond to this one, once symmetry is applied
1659 ratom(1:dim4symms) = symm_op_apply_inv_cart(ions%symm%ops(iop), ions%pos(:, iatom))
1660
1661 ratom(:) = ions%latt%fold_into_cell(ratom(:))
1662
1663 ! find iatom_symm
1664 do iatom_symm = 1, ions%natoms
1665 if (all(abs(ratom(:) - ions%pos(:, iatom_symm)) < symprec)) exit
1666 end do
1667
1668 if (iatom_symm > ions%natoms) then
1669 write(message(1),'(a,i6)') 'Internal error: could not find symmetric partner for atom number', iatom
1670 write(message(2),'(a,i3,a)') 'with symmetry operation number ', iop, '.'
1671 call messages_fatal(2, namespace=ions%namespace)
1672 end if
1673
1674 ions%map_symm_atoms(iatom, iop) = iatom_symm
1675 ions%inv_map_symm_atoms(iatom_symm, iop) = iatom
1676 end do
1677 end do
1678
1679 ! Also build a map for non-symmorphic operations
1680 do iop = 1, ions%symm%nops_nonsymmorphic
1681 do iatom = 1, ions%natoms
1682 !We find the atom that correspond to this one, once symmetry is applied
1683 ratom(1:dim4symms) = symm_op_apply_inv_cart(ions%symm%non_symmorphic_ops(iop), ions%pos(:, iatom))
1684
1685 ratom(:) = ions%latt%fold_into_cell(ratom(:))
1686
1687 ! find iatom_symm
1688 do iatom_symm = 1, ions%natoms
1689 if (all(abs(ratom(:) - ions%pos(:, iatom_symm)) < symprec)) exit
1690 end do
1691
1692 if (iatom_symm > ions%natoms) then
1693 write(message(1),'(a,i6)') 'Internal error: could not find symmetric partner for atom number', iatom
1694 write(message(2),'(a,i3,a)') 'with symmetry operation number ', iop, '.'
1695 call messages_fatal(2, namespace=ions%namespace)
1696 end if
1697
1698 ions%map_symm_atoms(iatom, ions%symm%nops+iop) = iatom_symm
1699 ions%inv_map_symm_atoms(iatom_symm, ions%symm%nops+iop) = iatom
1700 end do
1701 end do
1702
1703
1706
1707 !-------------------------------------------------------------------
1711 subroutine ions_symmetrize_atomic_coord(ions)
1712 class(ions_t), intent(inout) :: ions
1713
1714 integer :: iatom, iop, iatom_sym
1715 real(real64) :: ratom(ions%space%dim)
1716 real(real64), allocatable :: new_pos(:,:)
1717
1719
1720 safe_allocate(new_pos(1:ions%space%dim, 1:ions%natoms))
1721
1722 do iatom = 1, ions%natoms
1723 new_pos(:, iatom) = m_zero
1724
1725 ! Symmorphic operations
1726 do iop = 1, ions%symm%nops
1727 iatom_sym = ions%inv_map_symm_atoms(iatom, iop)
1728 ratom = symm_op_apply_inv_cart(ions%symm%ops(iop), ions%pos(:, iatom_sym))
1729 ratom = ions%latt%fold_into_cell(ratom)
1730 new_pos(:, iatom) = new_pos(:, iatom) + ratom
1731 end do
1732
1733 ! Non-symmorphic operations
1734 do iop = 1, ions%symm%nops_nonsymmorphic
1735 iatom_sym = ions%inv_map_symm_atoms(iatom, iop + ions%symm%nops)
1736 ratom = symm_op_apply_inv_cart(ions%symm%non_symmorphic_ops(iop), ions%pos(:, iatom_sym))
1737 ratom = ions%latt%fold_into_cell(ratom)
1738 new_pos(:, iatom) = new_pos(:, iatom) + ratom
1739 end do
1740
1741 new_pos(:, iatom) = new_pos(:, iatom) / (ions%symm%nops + ions%symm%nops_nonsymmorphic)
1742 end do
1743
1744 ions%pos = new_pos
1745
1747 end subroutine ions_symmetrize_atomic_coord
1748
1750 subroutine ions_print_spacegroup(ions)
1751 class(ions_t), intent(inout) :: ions
1752
1753 type(spglibdataset) :: spg_dataset
1754 character(len=11) :: symbol
1755 integer, allocatable :: site_type(:)
1756 integer :: space_group, ia
1757
1758 if(.not. ions%space%is_periodic()) return
1759
1760 push_sub(ions_print_spacegroup)
1761
1762 safe_allocate(site_type(1:ions%natoms))
1763 do ia = 1, ions%natoms
1764 site_type(ia) = ions%atom(ia)%species%get_index()
1765 end do
1766
1767 spg_dataset = symmetries_get_spg_dataset(ions%namespace, ions%space, ions%latt, ions%natoms, ions%pos, site_type)
1768
1769 safe_deallocate_a(site_type)
1770
1771 if (spg_dataset%spglib_error /= 0) then
1772 pop_sub(ions_print_spacegroup)
1773 return
1774 end if
1775
1776 space_group = spg_dataset%spacegroup_number
1777 symbol = spg_dataset%international_symbol
1778
1779 write(message(1),'(a, i4)') 'Info: Space group No. ', space_group
1780 write(message(2),'(2a)') 'Info: International: ', trim(symbol)
1781 call messages_info(2, namespace=ions%namespace)
1782
1783 pop_sub(ions_print_spacegroup)
1784 end subroutine ions_print_spacegroup
1785
1786end module ions_oct_m
1787
1788!! Local Variables:
1789!! mode: f90
1790!! coding: utf-8
1791!! End:
--------------— axpy ---------------— Constant times a vector plus a vector.
Definition: blas.F90:178
double acos(double __x) __attribute__((__nothrow__
double sin(double __x) __attribute__((__nothrow__
double cos(double __x) __attribute__((__nothrow__
double tanh(double __x) __attribute__((__nothrow__
double atan2(double __y, double __x) __attribute__((__nothrow__
double floor(double __x) __attribute__((__nothrow__
subroutine rotate(m, angle, dir)
Definition: ions.F90:1226
subroutine, public atom_init(this, dim, label, species)
Definition: atom.F90:151
subroutine, public atom_get_species(this, species)
Definition: atom.F90:261
subroutine, public atom_set_species(this, species)
Definition: atom.F90:249
This module contains interfaces for BLAS routines You should not use these routines directly....
Definition: blas.F90:120
This module handles the calculation mode.
integer, parameter, public p_strategy_states
parallelization in states
subroutine, public charged_particles_copy(this, cp_in)
subroutine, public charged_particles_init_interaction_as_partner(partner, interaction)
subroutine, public charged_particles_update_quantity(this, label)
subroutine, public charged_particles_init_interaction(this, interaction)
subroutine, public charged_particles_copy_quantities_to_interaction(partner, interaction)
subroutine, public charged_particles_init(this, np)
The init routine is a module level procedure This has the advantage that different classes can have d...
subroutine, public distributed_nullify(this, total)
subroutine, public distributed_init(this, total, comm, tag, scalapack_compat)
Distribute N instances across M processes of communicator comm
subroutine, public distributed_copy(in, out)
Create a copy of a distributed instance.
real(real64), parameter, public m_huge
Definition: global.F90:218
real(real64), parameter, public m_zero
Definition: global.F90:200
character(len= *), parameter, public static_dir
Definition: global.F90:279
real(real64), parameter, public p_kb
Boltzmann constant in Ha/K.
Definition: global.F90:241
real(real64), parameter, public m_half
Definition: global.F90:206
real(real64), parameter, public m_one
Definition: global.F90:201
This module defines the abstract interaction_t class, and some auxiliary classes for interactions.
Definition: io.F90:116
subroutine, public io_mkdir(fname, namespace, parents)
Definition: io.F90:361
subroutine, public ion_interaction_init_parallelization(this, natoms, mc)
subroutine, public ion_interaction_init(this, namespace, space, natoms)
subroutine ions_init_interaction(this, interaction)
Definition: ions.F90:693
subroutine ions_update_lattice_vectors(ions, latt, symmetrize)
Regenerate the ions information after update of the lattice vectors.
Definition: ions.F90:1713
subroutine ions_fold_atoms_into_cell(this)
Definition: ions.F90:1011
subroutine ions_single_mode_displacements(ions, mode, amplitude_pos, amplitude_vel)
apply initial displacements and velocities corresponding to a given phonon mode
Definition: ions.F90:869
real(real64) function, dimension(this%space%dim) ions_global_force(this, time)
Definition: ions.F90:1268
subroutine ions_copy(ions_out, ions_in)
Definition: ions.F90:645
real(real64) function, dimension(this%space%dim) ions_current(this)
Definition: ions.F90:1633
subroutine ions_update_quantity(this, label)
Definition: ions.F90:964
subroutine ions_generate_mapping_symmetric_atoms(ions)
Given the symmetries of the system, we create a mapping that tell us for each atom and symmetry,...
Definition: ions.F90:1737
subroutine ions_write_xyz(this, fname, append, comment, reduce_coordinates)
Definition: ions.F90:1285
subroutine ions_init_species(ions, factory, print_info)
Definition: ions.F90:532
subroutine ions_finalize(ions)
Definition: ions.F90:1675
subroutine ions_initialize(this)
Definition: ions.F90:950
real(real64) function, dimension(this%space%dim) ions_abs_current(this)
Definition: ions.F90:1654
subroutine ions_read_xyz(this, fname, comment)
Definition: ions.F90:1384
subroutine ions_write_vtk_geometry(this, filename, ascii)
Definition: ions.F90:1529
subroutine ions_rotate(this, from, from2, to)
Definition: ions.F90:1154
subroutine ions_translate(this, xx)
Definition: ions.F90:1138
subroutine ions_symmetrize_atomic_coord(ions)
Symmetrizes atomic coordinates by applying all symmetries.
Definition: ions.F90:1807
subroutine ions_print_spacegroup(ions)
Prints the spacegroup of the system for periodic systems.
Definition: ions.F90:1846
real(real64) function ions_min_distance(this, real_atoms_only)
Definition: ions.F90:1026
subroutine ions_init_random_displacements(ions, T)
create random displacements for positions and velocities
Definition: ions.F90:716
real(real64) function, dimension(this%space%dim) ions_dipole(this, mask)
Definition: ions.F90:1116
subroutine ions_write_bild_forces_file(this, dir, fname)
Definition: ions.F90:1491
subroutine ions_write_crystal(this, dir)
This subroutine creates a crystal by replicating the geometry and writes the result to dir.
Definition: ions.F90:1457
logical function ions_has_time_dependent_species(this)
Definition: ions.F90:1089
subroutine ions_partition(this, mc)
Definition: ions.F90:677
class(ions_t) function, pointer ions_constructor(namespace, grp, print_info, latt_inp)
Definition: ions.F90:242
subroutine ions_init_interaction_as_partner(partner, interaction)
Definition: ions.F90:980
subroutine ions_copy_quantities_to_interaction(partner, interaction)
Definition: ions.F90:995
subroutine ions_write_poscar(this, fname, comment)
Writes the positions of the ions in POSCAR format.
Definition: ions.F90:1338
real(real64) function ions_val_charge(this, mask)
Definition: ions.F90:1100
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
subroutine, public messages_print_with_emphasis(msg, iunit, namespace)
Definition: messages.F90:898
character(len=512), private msg
Definition: messages.F90:167
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:525
subroutine, public messages_obsolete_variable(namespace, name, rep)
Definition: messages.F90:1000
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:691
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1040
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:272
subroutine mpi_grp_init(grp, comm)
Initialize MPI group instance.
Definition: mpi.F90:341
This module handles the communicators for the various parallelization strategies.
Definition: multicomm.F90:147
logical function, public parse_is_defined(namespace, name)
Definition: parser.F90:463
This module provides a class for (classical) phonon modes.
integer, parameter, public read_coords_reduced
subroutine, public read_coords_init(gf)
integer, parameter, public xyz_flags_move
subroutine, public read_coords_end(gf)
subroutine, public read_coords_read(what, gf, space, namespace)
subroutine, public species_factory_init(factory, namespace)
subroutine, public species_factory_end(factory)
This module implements the abstract system type.
Definition: system.F90:120
subroutine, public system_init_parallelization(this, grp)
Basic functionality: copy the MPI group. This function needs to be implemented by extended types that...
Definition: system.F90:1242
subroutine, public tdf_read(f, namespace, function_name, ierr)
This function initializes "f" from the TDFunctions block.
Definition: tdfunction.F90:220
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:134
character(len=20) pure function, public units_abbrev(this)
Definition: unit.F90:225
This module defines the unit system, used for input and output.
type(unit_t), public unit_amu
Mass in atomic mass units (AKA Dalton).
type(unit_system_t), public units_out
integer, parameter, public wigner_q
integer, parameter, public wigner_p
abstract interaction class
surrogate interaction class to avoid circular dependencies between modules.
Stores all communicators and groups.
Definition: multicomm.F90:208
This class describes phonon modes, which are specified by their frequencies and eigenvectors.
An abstract class for species. Derived classes include jellium, all electron, and pseudopotential spe...
Definition: species.F90:147
Class describing a Wigner distribution for sampling initial conditions in multi-trajectory Ehrenfest ...
int true(void)