13 use,
intrinsic :: iso_fortran_env
41 real(real64),
allocatable :: alpha(:)
42 real(real64),
allocatable :: l_m(:)
43 real(real64),
allocatable :: prefactor(:)
47 type(namespace_t) :: namespace
64 subroutine phonon_modes_init(this, namespace, dim_space, num_atoms, periodic, masses)
65 class(phonon_modes_t),
intent(inout) :: this
66 type(namespace_t),
intent(in) :: namespace
67 integer,
intent(in) :: dim_space
68 integer,
intent(in) :: num_atoms
69 logical,
intent(in) :: periodic
70 real(real64),
intent(in) :: masses(:)
72 character(len=256) :: filename, version_string
73 character(len=800) :: line
74 character(len=128) :: dummy
75 integer :: iunit, iatom, idim, i
76 integer :: imode, num_modes, file_num_modes, file_num_atoms, num_zero_modes, max_modes
79 real(real64),
parameter :: frequency_default_threshold = 1.0e-5_real64
80 real(real64),
parameter :: mass_rel_tolerance = 1.0e-3_real64
81 real(real64) :: frequency_threshold
83 real(real64) :: eigenvecs(1:dim_space*num_atoms)
84 real(real64) :: file_masses(1:num_atoms)
85 real(real64) :: alpha, frequency
90 this%namespace = namespace
91 this%dim = dim_space * num_atoms
128 call parse_variable(namespace,
"PhononModesFile",
"NONE", filename)
139 call parse_variable(namespace,
"PhononModesZeroThreshold", frequency_default_threshold, frequency_threshold)
141 if (trim(filename) /=
"NONE")
then
143 iunit =
io_open(filename, action=
"read")
146 read(line, *) dummy, version_string
147 if (trim(dummy) /=
'Version:' .or. trim(version_string) /=
'1.0')
then
148 message(1) =
"Unsupported phonon file format in "//trim(filename)//
" (expected 'Version: 1.0')."
149 message(2) =
"Regenerate the file with the current oct-phonopy-eigenmodes utility."
154 read(line, *) dummy, file_num_modes
155 if (trim(dummy) /=
'Nmodes:')
then
156 message(1) =
"Phonon file ("//trim(filename)//
") is ill formatted (expected: 'Nmodes:')."
161 read(line, *) dummy, file_num_atoms
162 if (trim(dummy) /=
'Natoms:')
then
163 message(1) =
"Phonon file ("//trim(filename)//
") is ill formatted (expected 'Natoms:')."
166 if (file_num_atoms /= num_atoms)
then
167 write(
message(1),
'(A,I0,A,I0,A)')
"The phonon file ("//trim(filename)//
") was generated for ", &
168 file_num_atoms,
" atoms, but this system has ", num_atoms,
" atoms."
173 read(line, *) dummy, this%num_super
174 if (trim(dummy) /=
'Np:')
then
175 message(1) =
"Phonon file ("//trim(filename)//
") is ill formatted (expected 'Np:')."
178 if (this%num_super < 1)
then
179 write(
message(1),
'(A,I0,A)')
"The phonon file ("//trim(filename)//
") declares Np = ", &
180 this%num_super,
", but Np must be at least 1."
186 if (trim(dummy) /=
'Masses:')
then
187 message(1) =
"Phonon file ("//trim(filename)//
") is ill formatted (expected 'Masses:')."
190 read(iunit, *) file_masses(:)
191 do iatom = 1, num_atoms
192 if (abs(file_masses(iatom) - masses(iatom)) > mass_rel_tolerance * masses(iatom))
then
193 write(
message(1),
'(A,I0,A)')
"Mass mismatch between the phonon file ("//trim(filename)// &
194 ") and this run for atom ", iatom,
":"
195 write(
message(2),
'(A,F14.8,A,F14.8,A)')
" file: ", file_masses(iatom),
" AMU, system: ", &
196 masses(iatom),
" AMU."
197 message(3) =
"The phonon calculation and the MTEF run must use identical masses."
202 safe_allocate(this%frequencies(1:file_num_modes))
203 safe_allocate(this%alpha(1:file_num_modes))
204 safe_allocate(this%eigenvectors(1:this%dim, file_num_modes))
208 do imode=1, file_num_modes
210 read(line, *) dummy, frequency
211 if(trim(dummy) /=
'frequency:')
then
212 message(1) =
"Phonon file is ill formatted. ("//trim(filename)//
")"
215 read(iunit, *) eigenvecs(:)
218 read(line, *) dummy, alpha
219 if(trim(dummy) /=
'alpha:')
then
220 message(1) =
"Phonon file is ill formatted. ("//trim(filename)//
")"
230 if (trim(dummy) ==
'alpha:')
then
231 read(line, *) dummy, alpha
238 if (frequency > frequency_threshold)
then
239 num_modes = num_modes + 1
240 this%frequencies(num_modes) = frequency
241 this%eigenvectors(:, num_modes) = eigenvecs(:)
242 this%alpha(num_modes) = alpha
244 write(
message(1),
'(A,"Skip zero frequency mode. Omega = ",F15.5)') trim(filename), frequency
246 num_zero_modes = num_zero_modes + 1
250 this%num_modes = num_modes
256 max_modes = dim_space*num_atoms
258 max_modes = dim_space*(num_atoms-1)
261 if (this%num_modes + num_zero_modes > max_modes)
then
262 message(1) =
"The phonon file ("//trim(filename)//
") contains too many non-zero frequency modes."
263 message(2) =
"Most likely the zero frequency modes have not been identified. Try increasing PhononModesZeroThreshold."
264 message(3) =
"Note that for molecules with rotational symmetry, this warning might be triggered wrongly."
271 allocate(this%l_m(1:this%num_modes))
272 this%l_m =
sqrt(1.0_real64/(
unit_amu%factor * this%frequencies(1:this%num_modes))) * this%alpha(1:this%num_modes)
274 allocate(this%prefactor(1:(dim_space*num_atoms)))
276 do iatom = 1, num_atoms
278 this%prefactor(i) = 1.0_real64/
sqrt(masses(iatom) * this%num_super)
301 integer,
intent(in) :: imode
302 real(real64),
intent(in) :: Q
303 real(real64),
intent(in) :: P
304 real(real64),
intent(out) :: pos_disp_flat(1:this%dim)
305 real(real64),
intent(out) :: vel_disp_flat(1:this%dim)
307 pos_disp_flat = this%prefactor(:) * this%eigenvectors(:, imode) * q * this%l_m(imode)
308 vel_disp_flat = this%prefactor(:) * this%eigenvectors(:, imode) * p * this%l_m(imode) * this%frequencies(imode)
325 subroutine phonon_modes_sample(this, temperature, seed, pos_displacements, vel_displacements)
327 real(real64),
intent(in) :: temperature
328 integer(int64),
intent(in) :: seed
329 real(real64),
intent(out) :: pos_displacements(:, :)
330 real(real64),
intent(out) :: vel_displacements(:, :)
333 real(real64) :: sigma(1:this%num_modes), mu(1:this%num_modes)
334 real(real64) :: Q(1:this%num_modes), P(1:this%num_modes)
335 real(real64) :: pos_disp_flat(1:this%dim), vel_disp_flat(1:this%dim)
336 real(real64) :: beta_half
337 integer :: imode, iatom, dim_space, num_atoms
339 real(real64),
parameter :: low_temperature_tolerance = 1.0e-6_real64
343 num_atoms =
size(pos_displacements, 2)
344 dim_space =
size(pos_displacements, 1)
346 call wigner%init(this%num_modes, seed)
350 if (temperature < low_temperature_tolerance)
then
351 sigma = 1.0_real64/
sqrt(2.0_real64)
353 beta_half =
m_one / (2 *
p_kb * temperature)
354 sigma = 1.0_real64/
sqrt(2.0_real64 *
tanh(beta_half * this%frequencies(1:this%num_modes)))
362 pos_displacements =
m_zero
363 vel_displacements =
m_zero
365 do imode = 1, this%num_modes
368 pos_displacements = pos_displacements + reshape(pos_disp_flat, [dim_space, num_atoms])
369 vel_displacements = vel_displacements + reshape(vel_disp_flat, [dim_space, num_atoms])
377 write(
message(1),
'("Sampled displacements:")')
379 do iatom = 1, num_atoms
380 write(
message(1),
'(2x,3E15.5)') pos_displacements(1:dim_space, iatom)
384 write(
message(1),
'("Sampled velocities:")')
386 do iatom = 1, num_atoms
387 write(
message(1),
'(2x,3E15.5)') vel_displacements(1:dim_space, iatom)
399 integer,
intent(in) :: iunit
400 character(len=*),
intent(out) :: line
401 character(len=*),
intent(in) :: filename
403 logical,
optional,
intent(out) :: eof
409 if (
present(eof)) eof = .false.
411 read(iunit,
'(A)', iostat=iostat) line
412 if (iostat /= 0)
then
413 if (
present(eof))
then
419 message(1) =
"Unexpected end of phonon file ("//trim(filename)//
")."
423 if (len_trim(line) == 0) cycle
424 if (line(1:1) ==
'#') cycle
436 safe_deallocate_a(this%alpha)
double sqrt(double __x) __attribute__((__nothrow__
double tanh(double __x) __attribute__((__nothrow__
This module provides a general class for classical modes.
real(real64), parameter, public m_zero
real(real64), parameter, public p_kb
Boltzmann constant in Ha/K.
real(real64), parameter, public m_one
subroutine, public io_close(iunit, grp)
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
subroutine, public messages_warning(no_lines, 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_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
This module provides a class for (classical) phonon modes.
subroutine phonon_modes_finalize(this)
subroutine phonon_modes_get_displacements(this, imode, Q, P, pos_disp_flat, vel_disp_flat)
get the displacements for a single mode and the respective generalized coordinates.
subroutine phonon_modes_sample(this, temperature, seed, pos_displacements, vel_displacements)
Sample initial ionic displacements and velocities from the Wigner distribution of the modes.
subroutine next_data_line(iunit, line, filename, namespace, eof)
Read the next line that is neither blank nor a # comment.
subroutine phonon_modes_init(this, namespace, dim_space, num_atoms, periodic, masses)
Initialize the phonon modes.
This module defines the unit system, used for input and output.
type(unit_t), public unit_amu
Mass in atomic mass units (AKA Dalton).
integer, parameter, public wigner_q
integer, parameter, public wigner_p
This class describes classical modes, which are specified by their frequencies and eigenvectors.
This class describes phonon modes, which are specified by their frequencies and eigenvectors.
Class describing a Wigner distribution for sampling initial conditions in multi-trajectory Ehrenfest ...