Octopus
vibrations.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
22 use debug_oct_m
23 use global_oct_m
24 use io_oct_m
27 use mpi_oct_m
30 use space_oct_m
31 use unit_oct_m
33
34 implicit none
35
36 private
37 public :: &
52
53 type vibrations_t
54 private
55 integer, public :: num_modes
56 integer, public :: ndim
57 integer, public :: natoms
58 real(real64), allocatable, public :: dyn_matrix(:,:)
59 real(real64), allocatable, public :: infrared(:,:)
60 real(real64), allocatable, public :: normal_mode(:,:)
61 real(real64), allocatable, public :: freq(:)
62 real(real64), public :: disp
63 real(real64) :: total_mass
64 real(real64), pointer :: mass(:) => null()
65 character (len=2) :: suffix
66 character (len=80) :: filename_dynmat
67 type(unit_t) :: unit_dynmat
68 type(namespace_t), pointer, public :: namespace
69 end type vibrations_t
70
71contains
72
73 ! ---------------------------------------------------------
74 subroutine vibrations_init(this, space, natoms, mass, suffix, namespace)
75 type(vibrations_t), intent(out) :: this
76 class(space_t), intent(in) :: space
77 integer, intent(in) :: natoms
78 real(real64), target, intent(in) :: mass(:)
79 character (len=2), intent(in) :: suffix
80 type(namespace_t), target, intent(in) :: namespace
81
82 push_sub(vibrations_init)
83
84 this%ndim = space%dim
85 this%natoms = natoms
86 this%num_modes = natoms*space%dim
87 this%namespace => namespace
88 safe_allocate(this%dyn_matrix(1:this%num_modes, 1:this%num_modes))
89 safe_allocate(this%infrared(1:this%num_modes, 1:this%ndim))
90 safe_allocate(this%normal_mode(1:this%num_modes, 1:this%num_modes))
91 safe_allocate(this%freq(1:this%num_modes))
92
93 this%mass => mass
94 this%total_mass = sum(mass)
95
96 ! Since frequencies are reported as invcm, the matrix they are derived from can be expressed in invcm**2.
97 ! However, that matrix is the dynamical matrix divided by the total mass, so it has a different unit.
98 this%unit_dynmat = units_out%energy / units_out%length**2
99
100 this%suffix = suffix
101 this%filename_dynmat = vib_modes_dir//'dynamical_matrix_'//trim(this%suffix)
102 if (mpi_grp_is_root(mpi_world)) then
103 call io_mkdir(vib_modes_dir, namespace)
104 call io_rm(this%filename_dynmat, namespace)
106 end if
107
108 pop_sub(vibrations_init)
109 end subroutine vibrations_init
110
111
112 ! ---------------------------------------------------------
113 subroutine vibrations_end(this)
114 type(vibrations_t), intent(inout) :: this
115
116 push_sub(vibrations_end)
117
118 safe_deallocate_a(this%dyn_matrix)
119 safe_deallocate_a(this%infrared)
120 safe_deallocate_a(this%freq)
121 safe_deallocate_a(this%normal_mode)
122
123 pop_sub(vibrations_end)
124 end subroutine vibrations_end
125
126
127 ! ---------------------------------------------------------
128 character(len=2) pure function vibrations_get_suffix(this)
129 type(vibrations_t), intent(in) :: this
130
131 vibrations_get_suffix = this%suffix
132 end function vibrations_get_suffix
133
134
135 ! ---------------------------------------------------------
137 subroutine vibrations_symmetrize_dyn_matrix(this)
138 type(vibrations_t), intent(inout) :: this
139
140 integer :: imat, jmat
141 real(real64) :: average, maxdiff
142
144
145 ! FIXME: enforce acoustic sum rule.
147 maxdiff = m_zero
148 do imat = 1, this%num_modes
149 do jmat = imat + 1, this%num_modes
150 average = m_half * (this%dyn_matrix(imat, jmat) + this%dyn_matrix(jmat, imat))
151 maxdiff = max(maxdiff, abs(this%dyn_matrix(imat, jmat) - this%dyn_matrix(jmat, imat)))
152 this%dyn_matrix(imat, jmat) = average
153 this%dyn_matrix(jmat, imat) = average
154 end do
155 end do
157 write(message(1),'(a)') 'Info: Symmetrizing dynamical matrix.'
158 write(message(2),'(a,es13.6,a,a)') 'Info: Maximum discrepancy from symmetry: ', &
159 units_from_atomic(this%unit_dynmat, maxdiff), &
160 " ", trim(units_abbrev(this%unit_dynmat))
161 call messages_info(2, namespace=this%namespace)
162
165
166 ! ---------------------------------------------------------
168 type(vibrations_t), intent(inout) :: this
169
170 integer :: iatom, idir, jatom, jdir, imat, jmat
171
173
174 do iatom = 1, this%natoms
175 do idir = 1, this%ndim
176
177 imat = vibrations_get_index(this, iatom, idir)
178
179 do jatom = 1, this%natoms
180 do jdir = 1, this%ndim
181
182 jmat = vibrations_get_index(this, jatom, jdir)
183
184 this%dyn_matrix(jmat, imat) = &
185 this%dyn_matrix(jmat, imat) * vibrations_norm_factor(this, iatom, jatom)
186
187 end do
188 end do
189
190 end do
191 end do
192
195
196 ! ---------------------------------------------------------
197 real(real64) pure function vibrations_norm_factor(this, iatom, jatom)
198 type(vibrations_t), intent(in) :: this
199 integer, intent(in) :: iatom
200 integer, intent(in) :: jatom
201
202 vibrations_norm_factor = this%total_mass / &
203 sqrt(this%mass(iatom) * this%mass(jatom))
204
205 end function vibrations_norm_factor
207 ! ---------------------------------------------------------
209 subroutine vibrations_out_dyn_matrix_row(this, imat)
210 type(vibrations_t), intent(in) :: this
211 integer, intent(in) :: imat
212
213 integer :: iatom, idir, jatom, jdir, jmat, iunit
214
215 if (.not. mpi_grp_is_root(mpi_world)) return
216
218
219 iatom = vibrations_get_atom(this, imat)
220 idir = vibrations_get_dir(this, imat)
222 iunit = io_open(this%filename_dynmat, this%namespace, action='write', position='append')
223
224 do jmat = 1, this%num_modes
225 jatom = vibrations_get_atom(this, jmat)
226 jdir = vibrations_get_dir(this, jmat)
227
228 write(iunit, '(2(i8, i6), e25.12)') &
229 jatom, jdir, iatom, idir, units_from_atomic(this%unit_dynmat, this%dyn_matrix(jmat, imat))
230 end do
231 call io_close(iunit)
232
234 end subroutine vibrations_out_dyn_matrix_row
235
236 ! ---------------------------------------------------------
237 subroutine vibrations_out_dyn_matrix_header(this)
238 type(vibrations_t), intent(in) :: this
239
240 integer :: iunit
241
242 if (.not. mpi_grp_is_root(mpi_world)) return
243
245
246 iunit = io_open(this%filename_dynmat, namespace=this%namespace, action='write') ! start at the beginning
247 write(iunit, '(2(a8, a6), a25)') 'atom', 'dir', 'atom', 'dir', &
248 '[' // trim(units_abbrev(this%unit_dynmat)) // ']'
249 call io_close(iunit)
250
253
254 ! ---------------------------------------------------------
256 subroutine vibrations_diag_dyn_matrix(this)
257 type(vibrations_t), intent(inout) :: this
258
259 integer :: imode
262
263 this%normal_mode = m_zero
264
265 this%normal_mode = this%dyn_matrix
266 call lalg_eigensolve(this%num_modes, this%normal_mode, this%freq)
267
268 ! FIXME: why not remove total mass here and in norm_factor?
269 this%freq(1:this%num_modes) = -this%freq(1:this%num_modes) / this%total_mass
270
271 if (any(this%freq(1:this%num_modes) < -m_epsilon)) then
272 message(1) = "There are imaginary vibrational frequencies (represented as negative)."
273 call messages_warning(1, namespace=this%namespace)
274 end if
275
276 do imode = 1, this%num_modes
277 this%freq(imode) = sign(sqrt(abs(this%freq(imode))), this%freq(imode))
278
279 ! make the largest component positive, to specify the phase
280 if (maxval(this%normal_mode(:, imode)) - abs(minval(this%normal_mode(:, imode))) < -m_epsilon) then
281 this%normal_mode(:, imode) = -this%normal_mode(:, imode)
282 end if
283 end do
284
286 end subroutine vibrations_diag_dyn_matrix
287
288
289 ! ---------------------------------------------------------
290 integer pure function vibrations_get_index(this, iatom, idim)
291 type(vibrations_t), intent(in) :: this
292 integer, intent(in) :: iatom
293 integer, intent(in) :: idim
294
295 vibrations_get_index = (iatom - 1)*this%ndim + idim
296 end function vibrations_get_index
297
298
299 ! ---------------------------------------------------------
300 integer pure function vibrations_get_atom(this, index)
301 type(vibrations_t), intent(in) :: this
302 integer, intent(in) :: index
303
304 vibrations_get_atom = 1 + (index - 1)/ this%ndim
305 end function vibrations_get_atom
306
307
308 ! ---------------------------------------------------------
309 integer pure function vibrations_get_dir(this, index)
310 type(vibrations_t), intent(in) :: this
311 integer, intent(in) :: index
312
313 vibrations_get_dir = 1 + mod(index - 1, this%ndim)
314 end function vibrations_get_dir
315
316
317 ! ---------------------------------------------------------
319 subroutine vibrations_output(this)
320 type(vibrations_t), intent(in) :: this
321
322 integer :: iunit, imat, jmat
323
324 if (.not. mpi_grp_is_root(mpi_world)) return
325
326 push_sub(vibrations_output)
327
328 ! output frequencies and eigenvectors
329 iunit = io_open(vib_modes_dir//'normal_frequencies_'//trim(this%suffix), this%namespace, action='write')
330 write(iunit,'(a)') "# Mode Frequency [cm-1]"
331 do imat = 1, this%num_modes
332 write(iunit, '(i6,f17.8)') imat, units_from_atomic(unit_invcm, this%freq(imat))
333 end do
334 call io_close(iunit)
335
336 ! output eigenvectors
337 iunit = io_open(vib_modes_dir//'normal_modes_'//trim(this%suffix), this%namespace, action='write')
338 do imat = 1, this%num_modes
339 write(iunit, '(i6)', advance='no') imat
340 do jmat = 1, this%num_modes
341 write(iunit, '(es14.5)', advance='no') this%normal_mode(jmat, imat)
342 end do
343 write(iunit, '(1x)')
344 end do
345 call io_close(iunit)
346
347 pop_sub(vibrations_output)
348 end subroutine vibrations_output
350end module vibrations_oct_m
351
352!! Local Variables:
353!! mode: f90
354!! coding: utf-8
355!! End:
double sqrt(double __x) __attribute__((__nothrow__
character(len= *), parameter, public vib_modes_dir
Definition: global.F90:255
Definition: io.F90:114
subroutine, public io_rm(fname, namespace)
Definition: io.F90:385
subroutine, public io_mkdir(fname, namespace, parents)
Definition: io.F90:354
logical function mpi_grp_is_root(grp)
Is the current MPI process of grpcomm, root.
Definition: mpi.F90:430
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:266
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:132
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
character(len=2) pure function, public vibrations_get_suffix(this)
Definition: vibrations.F90:222
subroutine, public vibrations_out_dyn_matrix_header(this)
Definition: vibrations.F90:331
real(real64) pure function, public vibrations_norm_factor(this, iatom, jatom)
Definition: vibrations.F90:291
subroutine, public vibrations_diag_dyn_matrix(this)
Diagonalize the dynamical matrix.
Definition: vibrations.F90:350
subroutine, public vibrations_out_dyn_matrix_row(this, imat)
Outputs one row of the dynamical matrix.
Definition: vibrations.F90:303
subroutine, public vibrations_init(this, space, natoms, mass, suffix, namespace)
Definition: vibrations.F90:168
integer pure function, public vibrations_get_dir(this, index)
Definition: vibrations.F90:403
subroutine, public vibrations_normalize_dyn_matrix(this)
Definition: vibrations.F90:261
subroutine, public vibrations_symmetrize_dyn_matrix(this)
Symmetrize the dynamical matric, which is real symmetric matrix.
Definition: vibrations.F90:231
integer pure function, public vibrations_get_index(this, iatom, idim)
Definition: vibrations.F90:384
subroutine, public vibrations_output(this)
Outputs the eigenvectors and eigenenergies of the dynamical matrix.
Definition: vibrations.F90:413
subroutine, public vibrations_end(this)
Definition: vibrations.F90:207
integer pure function, public vibrations_get_atom(this, index)
Definition: vibrations.F90:394