28 use,
intrinsic :: iso_fortran_env
73 integer,
parameter :: &
96 logical :: constant_velocity
99 real(real64) :: current_temperature
101 real(real64),
allocatable :: oldforce(:, :)
104 real(real64),
allocatable :: old_pos(:, :)
106 real(real64) :: old_cell_force(3,3)
107 real(real64),
public :: stress(3,3)
108 real(real64) :: cell_vel(3,3)
111 type(nose_hoover_t) :: nh(1:2)
112 type(tdf_t) :: temperature_function
114 logical :: drive_ions
115 type(ion_td_displacement_t),
allocatable :: td_displacements(:)
116 type(ions_t),
pointer :: ions_t0
118 real(real64),
public :: ionic_scale
123 real(real64),
allocatable :: pos(:, :)
124 real(real64),
allocatable :: vel(:, :)
125 real(real64),
allocatable :: old_pos(:, :)
126 type(nose_hoover_t) :: nh(1:2)
131 real(real64),
allocatable :: pos(:, :)
132 real(real64),
allocatable :: vel(:, :)
133 real(real64),
allocatable :: old_pos(:, :)
140 use iso_c_binding,
only: c_ptr
141 type(ion_dynamics_t),
intent(out) :: this
142 type(namespace_t),
intent(in) :: namespace
143 type(ions_t),
intent(inout) :: ions
145 integer :: i, j, iatom, ierr
146 real(real64) :: xx(ions%space%dim), temperature, sigma, kin1, kin2
147 type(c_ptr) :: random_gen_pointer
148 type(read_coords_info) :: xyz
149 character(len=100) :: temp_function_name
150 logical :: have_velocities
154 character(len=200) :: expression
158 have_velocities = .false.
159 this%drive_ions = .false.
193 write(
message(1),
'(a)')
'Input: TDIonicTimeScale must be positive.'
211 call parse_variable(namespace,
'IonsConstantVelocity', .false., this%constant_velocity)
214 if (this%constant_velocity)
then
241 if (
parse_block(namespace,
'IonsTimeDependentDisplacements', blk) == 0)
then
244 safe_allocate(this%td_displacements(1:ions%natoms))
245 this%td_displacements(1:ions%natoms)%move = .false.
246 if (ndisp > 0) this%drive_ions =.
true.
250 this%td_displacements(iatom)%move = .
true.
253 call tdf_read(this%td_displacements(iatom)%fx, namespace, trim(expression), ierr)
255 write(
message(1),
'(3A)')
'Could not find "', trim(expression),
'" in the TDFunctions block:'
261 call tdf_read(this%td_displacements(iatom)%fy, namespace, trim(expression), ierr)
263 write(
message(1),
'(3A)')
'Could not find "', trim(expression),
'" in the TDFunctions block:'
268 call tdf_read(this%td_displacements(iatom)%fz, namespace, trim(expression), ierr)
270 write(
message(1),
'(3A)')
'Could not find "', trim(expression),
'" in the TDFunctions block:'
276 safe_allocate(this%ions_t0)
299 call parse_variable(namespace,
'Thermostat', thermo_none, this%thermostat)
303 if (this%thermostat /= thermo_none)
then
305 have_velocities = .
true.
307 if (this%drive_ions)
then
308 call messages_write(
'You cannot use a Thermostat and IonsConstantVelocity or IonsTimeDependentDisplacements')
325 call parse_variable(namespace,
'TemperatureFunction',
'temperature', temp_function_name)
327 call tdf_read(this%temperature_function, namespace, temp_function_name, ierr)
330 message(1) =
"You have enabled a thermostat but Octopus could not find"
331 message(2) =
"the '"//trim(temp_function_name)//
"' function in the TDFunctions block."
347 this%nh(2)%mass = this%nh(1)%mass
352 safe_allocate(this%old_pos(1:ions%space%dim, 1:ions%natoms))
354 this%old_pos = ions%pos
376 have_velocities = .
true.
383 do i = 1, ions%natoms
386 sigma =
sqrt(temperature / ions%mass(i))
392 call mpi_world%bcast(ions%vel(:, i), ions%space%dim, mpi_double_precision, 0)
399 call ions%update_kinetic_energy()
400 kin1 = ions%kinetic_energy
402 xx = ions%center_of_mass_vel()
403 do i = 1, ions%natoms
404 ions%vel(:, i) = ions%vel(:, i) - xx
407 call ions%update_kinetic_energy()
408 kin2 = ions%kinetic_energy
410 do i = 1, ions%natoms
411 ions%vel(:, i) =
sqrt(kin1/kin2)*ions%vel(:, i)
414 call ions%update_kinetic_energy()
416 write(
message(1),
'(a,f10.4,1x,a)')
'Info: Initial velocities randomly distributed with T =', &
418 write(
message(2),
'(2x,a,f8.4,1x,a)')
'<K> =', &
421 write(
message(3),
'(2x,a,f8.4,1x,a)')
'3/2 k_B T =', &
480 have_velocities = .
true.
482 if (ions%natoms /= xyz%n)
then
483 write(
message(1),
'(a,i4,a,i4)')
'I need exactly ', ions%natoms,
' velocities, but I found ', xyz%n
488 do i = 1, ions%natoms
499 call ions%update_kinetic_energy()
509 call parse_variable(namespace,
'MoveIons', have_velocities, this%move_ions)
512 if (this%move_ions .and. ions%space%periodic_dim == 1)
then
514 'Moving ions for a 1D periodic system is not allowed, as forces are incorrect.')
518 safe_allocate(this%oldforce(1:ions%space%dim, 1:ions%natoms))
521 this%old_cell_force =
m_zero
535 safe_deallocate_a(this%oldforce)
537 if (this%thermostat /= thermo_none)
then
538 call tdf_end(this%temperature_function)
541 if (this%drive_ions .and.
allocated(this%td_displacements))
then
542 if (any(this%td_displacements(1:this%ions_t0%natoms)%move))
then
547 safe_deallocate_a(this%td_displacements)
557 type(
ions_t),
intent(inout) :: ions
558 real(real64),
intent(in) :: time
559 real(real64),
intent(in) :: dt
563 real(real64) :: dr(3)
576 if (this%thermostat /= thermo_none)
then
579 if (this%current_temperature <
m_zero)
then
580 write(
message(1),
'(a, f10.3, 3a, f10.3, 3a)') &
581 "Negative temperature (", &
588 this%current_temperature =
m_zero
593 do iatom = 1, ions%natoms
594 if (ions%fixed(iatom)) cycle
596 if (.not. this%drive_ions)
then
598 ions%pos(:, iatom) = ions%pos(:, iatom) + dt*ions%vel(:, iatom) + &
599 m_half*dt**2 / ions%mass(iatom) * ions%tot_force(:, iatom)
601 this%oldforce(:, iatom) = ions%tot_force(:, iatom)
603 else if (this%constant_velocity)
then
605 ions%pos(:, iatom) = ions%pos(:, iatom) + dt*ions%vel(:, iatom)
607 else if (
allocated(this%td_displacements))
then
609 if (this%td_displacements(iatom)%move)
then
611 dr(1:3)=(/ real(
tdf(this%td_displacements(iatom)%fx,time), real64), &
612 real(
tdf(this%td_displacements(iatom)%fy,time), real64), &
613 real(tdf(this%td_displacements(iatom)%fz,time), real64) /)
615 ions%pos(:, iatom) = this%ions_t0%pos(:, iatom) + dr(1:ions%space%dim)
632 do iatom = 1, ions%natoms
633 ions%pos(:, iatom) = ions%pos(:, iatom) +
m_half*dt*ions%vel(:, iatom)
639 if (ions%space%is_periodic())
then
640 call ions%fold_atoms_into_cell()
661 type(
ions_t),
intent(inout) :: ions
663 real(real64) :: g1, g2, ss, uk, dt, temp
669 call ions%update_kinetic_energy()
670 uk = ions%kinetic_energy
672 temp = this%current_temperature
674 g2 = (this%nh(1)%mass*this%nh(1)%vel**2 - temp)/this%nh(2)%mass
675 this%nh(2)%vel = this%nh(2)%vel + g2*dt/
m_four
676 this%nh(1)%vel = this%nh(1)%vel*
exp(-this%nh(2)%vel*dt/8.0_real64)
678 g1 = (
m_two*uk -
m_three*ions%natoms*temp)/this%nh(1)%mass
679 this%nh(1)%vel = this%nh(1)%vel + g1*dt/
m_four
680 this%nh(1)%vel = this%nh(1)%vel*
exp(-this%nh(2)%vel*dt/8.0_real64)
681 this%nh(1)%pos = this%nh(1)%pos + this%nh(1)%vel*dt/
m_two
682 this%nh(2)%pos = this%nh(2)%pos + this%nh(2)%vel*dt/
m_two
686 ions%vel = ss*ions%vel
690 this%nh(1)%vel = this%nh(1)%vel*
exp(-this%nh(2)%vel*dt/8.0_real64)
691 g1 = (
m_two*uk -
m_three*ions%natoms*temp)/this%nh(1)%mass
692 this%nh(1)%vel = this%nh(1)%vel + g1*dt/
m_four
693 this%nh(1)%vel = this%nh(1)%vel*
exp(-this%nh(2)%vel*dt/8.0_real64)
695 g2 = (this%nh(1)%mass*this%nh(1)%vel**2 - temp)/this%nh(2)%mass
696 this%nh(2)%vel = this%nh(2)%vel + g2*dt/
m_four
705 type(
ions_t),
intent(inout) :: ions
706 logical,
optional,
intent(out) :: atoms_moved
708 integer :: iatom, periodic_dim
712 if (this%drive_ions)
return
716 if (
present(atoms_moved)) atoms_moved = this%thermostat ==
thermo_nh
721 do iatom = 1, ions%natoms
722 if (ions%fixed(iatom)) cycle
724 ions%vel(:, iatom) = ions%vel(:, iatom) &
725 + this%dt/ions%mass(iatom) *
m_half * (this%oldforce(:, iatom) + &
726 ions%tot_force(:, iatom))
732 do iatom = 1, ions%natoms
733 ions%vel(:, iatom) = ions%vel(:, iatom) + this%dt*ions%tot_force(:, iatom) / ions%mass(iatom)
734 ions%pos(:, iatom) = ions%pos(:, iatom) +
m_half*this%dt*ions%vel(:, iatom)
744 ions%vel = scal*ions%vel
747 if (ions%space%is_periodic())
then
748 periodic_dim = ions%space%periodic_dim
749 this%cell_vel(1:periodic_dim, 1:periodic_dim) = this%cell_vel(1:periodic_dim, 1:periodic_dim) &
750 + this%dt*
m_half * (this%old_cell_force(1:periodic_dim, 1:periodic_dim) + this%stress(1:periodic_dim, 1:periodic_dim))
760 type(
ions_t),
intent(in) :: ions
761 real(real64),
intent(inout) :: q(:, :)
762 real(real64),
intent(inout) :: v(:, :)
763 real(real64),
intent(in) :: fold(:, :)
764 real(real64),
intent(in) :: dt
771 do iatom = 1, ions%natoms
772 v(iatom, 1:ions%space%dim) = v(iatom, 1:ions%space%dim) / ions%mass(iatom)
776 do iatom = 1, ions%natoms
777 if (ions%fixed(iatom)) cycle
778 q(iatom, 1:ions%space%dim) = q(iatom, 1:ions%space%dim) + dt * v(iatom, 1:ions%space%dim) + &
779 m_half*dt**2 / ions%mass(iatom) * fold(iatom, 1:ions%space%dim)
783 do iatom = 1, ions%natoms
784 v(iatom, 1:ions%space%dim) = ions%mass(iatom) * v(iatom, 1:ions%space%dim)
795 type(
ions_t),
intent(in) :: ions
796 real(real64),
intent(inout) :: v(:, :)
797 real(real64),
intent(in) :: fold(:, :)
798 real(real64),
intent(in) :: fnew(:, :)
799 real(real64),
intent(in) :: dt
806 do iatom = 1, ions%natoms
807 v(iatom, 1:ions%space%dim) = v(iatom, 1:ions%space%dim) / ions%mass(iatom)
811 do iatom = 1, ions%natoms
812 if (ions%fixed(iatom)) cycle
813 v(iatom, 1:ions%space%dim) = v(iatom, 1:ions%space%dim) &
814 + dt / ions%mass(iatom) *
m_half * (fold(iatom, 1:ions%space%dim) + fnew(iatom, 1:ions%space%dim))
818 do iatom = 1, ions%natoms
819 v(iatom, 1:ions%space%dim) = ions%mass(iatom) * v(iatom, 1:ions%space%dim)
829 type(
ions_t),
intent(in) :: ions
836 safe_allocate(state%pos(1:ions%space%dim, 1:ions%natoms))
837 safe_allocate(state%vel(1:ions%space%dim, 1:ions%natoms))
843 safe_allocate(state%old_pos(1:ions%space%dim, 1:ions%natoms))
844 state%old_pos(1:ions%space%dim, 1:ions%natoms) = this%old_pos(1:ions%space%dim, 1:ions%natoms)
845 state%nh(1:2)%pos = this%nh(1:2)%pos
846 state%nh(1:2)%vel = this%nh(1:2)%vel
856 type(
ions_t),
intent(inout) :: ions
867 this%old_pos(1:ions%space%dim, 1:ions%natoms) = state%old_pos(1:ions%space%dim, 1:ions%natoms)
868 this%nh(1:2)%pos = state%nh(1:2)%pos
869 this%nh(1:2)%vel = state%nh(1:2)%vel
870 safe_deallocate_a(state%old_pos)
873 safe_deallocate_a(state%pos)
874 safe_deallocate_a(state%vel)
884 ions_move = this%move_ions
893 drive_ions = this%drive_ions
901 type(ions_t),
intent(in) :: ions
904 real(real64) :: kinetic_energy
906 kinetic_energy = m_zero
907 do iatom = 1, ions%natoms
908 kinetic_energy = kinetic_energy + &
909 m_half * ions%mass(iatom) * sum(ions%vel(:, iatom)**2)
911 temperature = m_two/m_three*kinetic_energy/ions%natoms
920 if (this%move_ions)
then
921 this%move_ions = .false.
933 this%move_ions = .
true.
939 type(restart_t),
intent(in) :: restart
940 integer,
intent(out) :: ierr
944 if (
allocated(this%oldforce))
then
945 call drestart_write_binary(restart,
"ion_dynamics_oldforce",
size(this%oldforce), &
955 type(restart_t),
intent(in) :: restart
956 integer,
intent(out) :: ierr
960 if (
allocated(this%oldforce))
then
961 call drestart_read_binary(restart,
"ion_dynamics_oldforce",
size(this%oldforce), &
971 type(namespace_t),
intent(in) :: namespace
972 type(grid_t),
intent(inout) :: gr
973 class(space_t),
intent(in) :: space
974 type(poisson_t),
intent(inout) :: psolver
975 type(kpoints_t),
intent(inout) :: kpoints
976 type(multicomm_t),
intent(in) :: mc
977 real(real64),
intent(in) :: qtot
978 type(lattice_vectors_t),
intent(in) :: new_latt
981 real(real64) :: length(1:space%dim)
986 select type(box => gr%box)
987 type is (box_parallelepiped_t)
988 do idir = 1, space%dim
989 length(idir) = norm2(new_latt%rlattice(1:space%dim, idir))
991 call box%regenerate(space%dim, new_latt%rlattice, length, namespace)
993 call messages_not_implemented(
"Grid regeneration for non-parallelepiped boxes", namespace=namespace)
997 call grid_lattice_vectors_update(gr, space, namespace, mc, new_latt)
1000 call poisson_end(psolver)
1001 call poisson_init(psolver, namespace, space, gr%der, mc, gr%stencil, qtot, verbose=.false.)
1003 call kpoints_lattice_vectors_update(kpoints, new_latt)
Functions to generate random numbers.
Prints out to iunit a message in the form: ["InputVariable" = value] where "InputVariable" is given b...
double exp(double __x) __attribute__((__nothrow__
real(real64), parameter, public m_two
real(real64), parameter, public m_zero
real(real64), parameter, public m_four
real(real64), parameter, public m_half
real(real64), parameter, public m_one
real(real64), parameter, public m_three
This module implements the underlying real-space grid.
subroutine, public ion_dynamics_verlet_step2(ions, v, fold, fnew, dt)
A bare verlet integrator.
subroutine, public ion_dynamics_dump(this, restart, ierr)
subroutine, public ion_dynamics_verlet_step1(ions, q, v, fold, dt)
A bare verlet integrator.
subroutine nh_chain(this, ions)
subroutine, public ion_dynamics_restore_state(this, ions, state)
subroutine, public ion_dynamics_propagate(this, ions, time, dt, namespace)
subroutine, public ion_dynamics_save_state(this, ions, state)
logical pure function, public ion_dynamics_ions_move(this)
subroutine, public ion_dynamics_unfreeze(this)
Unfreezes the ionic movement.
subroutine, public ion_dynamics_init(this, namespace, ions)
subroutine, public ion_dynamics_propagate_vel(this, ions, atoms_moved)
subroutine, public ion_dynamics_load(this, restart, ierr)
integer, parameter thermo_scal
subroutine, public electrons_lattice_vectors_update(namespace, gr, space, psolver, kpoints, mc, qtot, new_latt)
subroutine, public ion_dynamics_end(this)
integer, parameter thermo_nh
logical function, public ion_dynamics_freeze(this)
Freezes the ionic movement.
logical pure function, public ion_dynamics_drive_ions(this)
real(real64) function, public ion_dynamics_temperature(ions)
This function returns the ionic temperature in energy units.
subroutine, public messages_warning(no_lines, all_nodes, namespace)
subroutine, public messages_obsolete_variable(namespace, name, rep)
subroutine, public messages_info(no_lines, iunit, verbose_limit, stress, all_nodes, namespace)
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
subroutine, public messages_input_error(namespace, var, details, row, column)
subroutine, public messages_experimental(name, namespace)
logical function mpi_grp_is_root(grp)
Is the current MPI process of grpcomm, root.
type(mpi_grp_t), public mpi_world
This module handles the communicators for the various parallelization strategies.
logical function, public parse_is_defined(namespace, name)
integer function, public parse_block(namespace, name, blk, check_varinfo_)
integer, parameter, public read_coords_err
for read_coords_info::file_type
subroutine, public read_coords_init(gf)
subroutine, public read_coords_end(gf)
subroutine, public read_coords_read(what, gf, space, namespace)
subroutine, public tdf_end(f)
subroutine, public tdf_read(f, namespace, function_name, ierr)
This function initializes "f" from the TDFunctions block.
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
character(len=20) pure function, public units_abbrev(this)
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
type(unit_t), public unit_kelvin
For converting energies into temperatures.
type(unit_system_t), public units_inp
the units systems for reading and writing