Octopus
born_charges.F90
Go to the documentation of this file.
1!! Copyright (C) 2009 D. Strubbe
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 atom_oct_m
23 use debug_oct_m
24 use global_oct_m
25 use io_oct_m
27 use mpi_oct_m
29 use parser_oct_m
33 use utils_oct_m
34
35 implicit none
36
37 private
38 public :: &
43
45 private
46 complex(real64), allocatable, public :: charge(:, :, :)
47 complex(real64), allocatable :: sum_ideal(:,:)
48 complex(real64), allocatable :: delta(:,:)
49 logical :: correct
50 end type born_charges_t
51
52contains
53
54 ! ---------------------------------------------------------
55 subroutine born_charges_init(this, namespace, natoms, val_charge, qtot, dim)
56 type(born_charges_t), intent(out) :: this
57 type(namespace_t), intent(in) :: namespace
58 integer, intent(in) :: natoms
59 real(real64), intent(in) :: val_charge
60 real(real64), intent(in) :: qtot
61 integer, intent(in) :: dim
62
63 integer :: idir
64
65 push_sub(born_charges_init)
66
67 safe_allocate(this%charge(1:dim, 1:dim, 1:natoms))
68 safe_allocate(this%sum_ideal(1:dim, 1:dim))
69 safe_allocate(this%delta(1:dim, 1:dim))
70 this%charge(1:dim, 1:dim, 1:natoms) = m_zero
71 this%delta(1:dim, 1:dim) = m_zero
72
73 this%sum_ideal = m_zero
74 do idir = 1, dim
75 this%sum_ideal(idir, idir) = -(val_charge + qtot) ! total charge
76 end do
77
78 !%Variable BornChargeSumRuleCorrection
79 !%Type logical
80 !%Default true
81 !%Section Linear Response::Polarizabilities
82 !%Description
83 !% Enforce the acoustic sum rule by distributing the excess sum of Born charges equally among the atoms.
84 !% Sum rule: <math>\sum_{\alpha} Z^{*}_{\alpha, i, j} = Z_{\rm tot} \delta_{ij}</math>.
85 !% Violation of the sum rule may be caused by inadequate spacing, box size (in finite directions),
86 !% or <i>k</i>-point sampling (in periodic directions).
87 !%End
88
89 call parse_variable(namespace, 'BornChargeSumRuleCorrection', .true., this%correct)
90
91 pop_sub(born_charges_init)
92 end subroutine born_charges_init
93
94 ! ---------------------------------------------------------
95 subroutine born_charges_end(this)
96 type(born_charges_t), intent(inout) :: this
97
98 push_sub(born_charges_end)
99
100 safe_deallocate_a(this%charge)
101 safe_deallocate_a(this%sum_ideal)
102 safe_deallocate_a(this%delta)
103
104 pop_sub(born_charges_end)
105 end subroutine born_charges_end
106
107 ! ---------------------------------------------------------
110 subroutine correct_born_charges(this, natoms, dim)
111 type(born_charges_t), intent(inout) :: this
112 integer, intent(in) :: natoms
113 integer, intent(in) :: dim
115 complex(real64) :: born_sum(dim, dim) ! the sum of born charges from the calculation
116 integer :: iatom
117
118 push_sub(correct_born_charges)
119
120 born_sum = m_zero
121
122 do iatom = 1, natoms
123 born_sum = born_sum + this%charge(:, :, iatom)
124 end do
125
126 this%delta = (born_sum - this%sum_ideal) / natoms
127
128 if (this%correct) then
129 do iatom = 1, natoms
130 this%charge(:, :, iatom) = this%charge(:, :, iatom) - this%delta
131 end do
132 end if
133
134 pop_sub(correct_born_charges)
135 end subroutine correct_born_charges
136
137 ! ---------------------------------------------------------
138 subroutine born_output_charges(this, atom, charge, natoms, namespace, dim, dirname, write_real)
139 type(born_charges_t), intent(inout) :: this
140 type(atom_t), intent(in) :: atom(:)
141 real(real64), intent(in) :: charge(:)
142 integer, intent(in) :: natoms
143 type(namespace_t), intent(in) :: namespace
144 integer, intent(in) :: dim
145 character(len=*), intent(in) :: dirname
146 logical, intent(in) :: write_real
147
149 integer iatom, iunit
150 real(real64) :: phase(dim, dim)
151
152 push_sub(born_output_charges)
153
154 call correct_born_charges(this, natoms, dim)
155
156 if (mpi_grp_is_root(mpi_world)) then ! only first node outputs
157 iunit = io_open(trim(dirname)//'/born_charges', namespace, action='write')
158 write(iunit,'(a)') '# (Frequency-dependent) Born effective charge tensors'
159 if (.not. write_real) write(iunit,'(a)') '# Real and imaginary parts'
160 do iatom = 1, natoms
161 write(iunit,'(a,i5,a,a5,a,f10.4)') 'Index: ', iatom, ' Label: ', trim(atom(iatom)%species%get_label()), &
162 ' Ionic charge: ', charge(iatom)
163
164 if (.not. write_real) write(iunit,'(a)') 'Real:'
165 call output_tensor(real(this%charge(:, :, iatom), real64), dim, unit_one, iunit=iunit)
166
167 if (.not. write_real) then
168 write(iunit,'(a)') 'Imaginary:'
169 call output_tensor(aimag(this%charge(:, :, iatom)), dim, unit_one, iunit=iunit)
170 end if
171
172 write(iunit,'(a)')
173 end do
174
175 if (.not. write_real) then
176 write(iunit,'(a)') '# Magnitude and phase'
177 do iatom = 1, natoms
178 write(iunit,'(a,i5,a,a5,a,f10.4)') 'Index: ', iatom, ' Label: ', trim(atom(iatom)%species%get_label()), &
179 ' Ionic charge: ', charge(iatom)
180
181 write(iunit,'(a)') 'Magnitude:'
182 call output_tensor(real(abs(this%charge(:, :, iatom)), real64), dim, unit_one, iunit=iunit)
183
184 write(iunit,'(a)') 'Phase:'
185
186 where (abs(this%charge(:, :, iatom)) > m_epsilon)
187 phase = atan2(aimag(this%charge(:, :, iatom)), real(this%charge(:, :, iatom), real64))
188 else where
189 phase = m_zero
190 end where
191 call output_tensor(phase, dim, unit_one, write_average = .false., iunit=iunit)
192 write(iunit,'(a)')
193 end do
194 end if
195
196 write(iunit,'(a)') '# Discrepancy of Born effective charges from acoustic sum rule before correction, per atom'
197 if (.not. write_real) write(iunit,'(a)') 'Real:'
198 call output_tensor(real(this%delta, real64) , dim, unit_one, iunit=iunit)
199 if (.not. write_real) then
200 write(iunit,'(a)') 'Imaginary:'
201 call output_tensor(aimag(this%delta), dim, unit_one, iunit=iunit)
202 end if
204 call io_close(iunit)
205 end if
206
207 pop_sub(born_output_charges)
208 end subroutine born_output_charges
209
210end module born_charges_oct_m
211
212!! Local Variables:
213!! mode: f90
214!! coding: utf-8
215!! End:
double atan2(double __y, double __x) __attribute__((__nothrow__
subroutine, public born_charges_end(this)
subroutine, public born_output_charges(this, atom, charge, natoms, namespace, dim, dirname, write_real)
subroutine, public born_charges_init(this, namespace, natoms, val_charge, qtot, dim)
subroutine correct_born_charges(this, natoms, dim)
The sum over atoms of a given tensor component of the born charges should be Z delta_ij to satisfy th...
real(real64), parameter, public m_zero
Definition: global.F90:187
real(real64), parameter, public m_epsilon
Definition: global.F90:203
Definition: io.F90:114
subroutine, public io_close(iunit, grp)
Definition: io.F90:468
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
Definition: io.F90:395
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
This module defines the unit system, used for input and output.
type(unit_t), public unit_one
some special units required for particular quantities
This module is intended to contain simple general-purpose utility functions and procedures.
Definition: utils.F90:118
subroutine, public output_tensor(tensor, ndim, unit, write_average, iunit, namespace)
Definition: utils.F90:242
int true(void)