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