Octopus
magnetic_constrain.F90
Go to the documentation of this file.
1!! Copyright (C) 2021 N. Tancogne-Dejean
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
25 use debug_oct_m
27 use global_oct_m
28 use, intrinsic :: iso_fortran_env
32 use mesh_oct_m
34 use parser_oct_m
36 use space_oct_m
39
40 implicit none
41
42 private
43 public :: &
49
50 integer, public, parameter :: &
51 CONSTRAIN_NONE = 0, &
52 constrain_dir = 1, &
54
55
58 private
59 integer, public :: level = constrain_none
60 real(real64) :: lambda = m_zero
61 real(real64), allocatable :: constrain(:,:)
62 real(real64), allocatable, public :: pot(:,:)
63 real(real64), public :: energy
64
65 real(real64) :: lmm_r
67
68 ! Threshold for considering zero magnetic moments
69 real(real64), parameter :: tol_mag_norm = 1.0e-6_real64
70
71contains
72
73 ! ---------------------------------------------------------
75 subroutine magnetic_constrain_init(this, namespace, mesh, std, natoms, min_dist)
76 type(magnetic_constrain_t), intent(inout) :: this
77 type(namespace_t), intent(in) :: namespace
78 class(mesh_t), intent(in) :: mesh
79 type(states_elec_dim_t), intent(in) :: std
80 integer, intent(in) :: natoms
81 real(real64), intent(in) :: min_dist
82
83 integer :: ia, idir
84 real(real64) :: rmin, norm
85 type(block_t) :: blk
86
88
89 !%Variable MagneticConstrain
90 !%Type integer
91 !%Default no
92 !%Section Hamiltonian
93 !%Description
94 !% This variable selects which magnetic constrain expression is added to the Hamiltonian.
95 !% The <tt>AtomsMagnetDirection</tt> block is used for determining the constrained direction.
96 !%
97 !% This is ignored for independent particles, as in this case, there is nothing that
98 !% determines the magnetic direction.
99 !%
100 !%Option constrain_none 0
101 !% No constrain is added to the Hamiltonian.
102 !%Option constrain_dir 1
103 !% We are adding a constrain for the direction and sign of the magnetic moments only,
104 !% see Ma and Dudarev, PRB 91, 054420 (2015).
105 !%Option constrain_full 2
106 !% We are adding a constrain for the direction and norm of the magnetic moments only,
107 !% see Ma and Dudarev, PRB 91, 054420 (2015).
108 !%End
109 call parse_variable(namespace, 'MagneticConstrain', constrain_none, this%level)
110 call messages_print_var_value('MagneticConstrain', this%level, namespace=namespace)
111 if (this%level == constrain_none) then
113 return
114 end if
115
116 call messages_experimental('MagneticConstrain', namespace=namespace)
117
118 !%Variable MagneticConstrainStrength
119 !%Type float
120 !%Default 0.01
121 !%Section Hamiltonian
122 !%Description
123 !% This variable determines the value of the Lagrange multiplier used for the constrain term.
124 !%End
125 call parse_variable(namespace, 'MagneticConstrainStrength', 0.01_real64, this%lambda)
126 call messages_print_var_value('MagneticConstrainStrength', this%lambda, namespace=namespace)
127
128
129 !We are using the AtomsMagnetDirection block for the contrain
130 if (parse_block(namespace, 'AtomsMagnetDirection', blk) < 0) then
131 message(1) = "AtomsMagnetDirection block is not defined."
132 message(2) = "Magnetic constrained DFT cannot be used without it."
133 call messages_fatal(2, namespace=namespace)
134 end if
135
136 if (parse_block_n(blk) /= natoms) then
137 message(1) = "AtomsMagnetDirection block has the wrong number of rows."
138 call messages_fatal(1, namespace=namespace)
139 end if
140
141 if(std%ispin == unpolarized) then
142 message(1) = "Magnetic constrains can only be used for spin-polized and spinor calculations."
143 call messages_fatal(1, namespace=namespace)
144 end if
146 safe_allocate(this%constrain(1:3, natoms))
147 do ia = 1, natoms
148 !Read from AtomsMagnetDirection block
149 if (std%ispin == spin_polarized) then
150 call parse_block_float(blk, ia-1, 0, this%constrain(3, ia))
151 this%constrain(1:2, ia) = m_zero
152 elseif (std%ispin == spinors) then
153 do idir = 1, 3
154 call parse_block_float(blk, ia-1, idir-1, this%constrain(idir, ia))
155 if (abs(this%constrain(idir, ia)) < tol_mag_norm) this%constrain(idir, ia) = m_zero
156 end do
157 end if
159 if(this%level == constrain_dir) then
160 !Renormalization for constrained directions
161 norm = norm2(this%constrain(1:3, ia))
162 if (norm > tol_mag_norm) then
163 this%constrain(1:3, ia) = this%constrain(1:3, ia) / norm
164 end if
165 end if
166 end do
167 call parse_block_end(blk)
168
169
170 safe_allocate(this%pot(1:mesh%np, 1:std%nspin))
171 this%pot = m_zero
172
173 rmin = min_dist
174 call parse_variable(namespace, 'LocalMagneticMomentsSphereRadius', min(m_half*rmin, 100.0_real64), &
175 this%lmm_r)
176
178 end subroutine magnetic_constrain_init
179
180 ! ---------------------------------------------------------
182 subroutine magnetic_constrain_end(this)
183 type(magnetic_constrain_t), intent(inout) :: this
184
185 push_sub(magnetic_constrain_end)
186
187 safe_deallocate_a(this%constrain)
188 safe_deallocate_a(this%pot)
189
191 end subroutine magnetic_constrain_end
192
193 ! ---------------------------------------------------------
195 subroutine magnetic_constrain_copy(this_out, this_in)
196 type(magnetic_constrain_t), intent(out) :: this_out
197 type(magnetic_constrain_t), intent(in) :: this_in
198
200
201 this_out%level = this_in%level
202
203 ! Keep a deterministic state when the source object was never initialized
204 ! through magnetic_constrain_init.
205 this_out%lambda = m_zero
206 this_out%energy = m_zero
207 this_out%lmm_r = m_zero
208 if (this_in%level /= constrain_none) then
209 this_out%lambda = this_in%lambda
210 this_out%energy = this_in%energy
211 this_out%lmm_r = this_in%lmm_r
212 else if (allocated(this_in%pot) .or. allocated(this_in%constrain)) then
213 this_out%energy = this_in%energy
214 end if
215
216 if (allocated(this_in%constrain)) then
217 safe_allocate(this_out%constrain(1:size(this_in%constrain, dim=1), 1:size(this_in%constrain, dim=2)))
218 this_out%constrain = this_in%constrain
219 end if
220
221 if (allocated(this_in%pot)) then
222 safe_allocate(this_out%pot(1:size(this_in%pot, dim=1), 1:size(this_in%pot, dim=2)))
223 this_out%pot = this_in%pot
224 end if
225
227 end subroutine magnetic_constrain_copy
228
229 ! ---------------------------------------------------------
231 subroutine magnetic_constrain_update(this, mesh, std, space, latt, pos, rho)
232 type(magnetic_constrain_t), intent(inout) :: this
233 class(mesh_t), intent(in) :: mesh
234 type(states_elec_dim_t), intent(in) :: std
235 class(space_t), intent(in) :: space
236 type(lattice_vectors_t), intent(in) :: latt
237 real(real64), intent(in) :: pos(:,:)
238 real(real64), intent(in) :: rho(:,:)
239
240 integer :: ia, idir, ip
241 real(real64) :: bb(3), b2, lmm(3), dotp, norm, xx, max_r
242 real(real64), allocatable :: md(:,:), mdf(:), mask(:)
243 type(submesh_t) :: sphere
244 integer :: natoms
245 ! Threshold for computes sinc(x) with a Taylor expansion
246 ! The error in the Taylor expansion is below 8e-11 for this value
247 real(real64), parameter :: tol_sinc = 0.01_real64
248
249 if (this%level == constrain_none) return
250
252
253 call profiling_in(tostring(magnetic_constrain))
254
255 this%pot = m_zero
256 this%energy = m_zero
257
258 safe_allocate(md(1:mesh%np, 1:max(space%dim, 3)))
259 call magnetic_density(mesh, std, rho, md)
260
261 natoms = size(pos, dim=2)
262
263 do ia = 1, natoms
264 call submesh_init(sphere, space, mesh, latt, pos(:, ia), this%lmm_r)
265
266 ! Create a mask to be applied to the magnetic moment, see
267 ! Ma and Dudarev, PRB 91, 054420 (2015)
268 ! This helps the convergence in avoiding jitter at the edge of the sphere,
269 ! and produces a continuous potential
270 max_r = maxval(sphere%r)
271 safe_allocate(mask(1:sphere%np))
272 mask = m_zero
273 !$omp parallel do private(xx)
274 do ip = 1, sphere%np
275 xx = sphere%r(ip) * m_pi / max_r
276 if(xx < tol_sinc) then
277 mask(ip) = m_one-xx*xx/6.0_real64
278 else
279 mask(ip) = sin(xx)/xx
280 end if
281 end do
282
283 ! We compute the local moment here
284 ! We multiply here by a function that decreases monotonically to zero
285 ! at the boundary of the atomic sphere.
286 lmm = m_zero
287 safe_allocate(mdf(1:sphere%np))
288 do idir = 1, max(space%dim, 3)
289 !$omp parallel do
290 do ip = 1, sphere%np
291 mdf(ip) = md(sphere%map(ip), idir) * mask(ip)
292 end do
293 lmm(idir) = dsm_integrate(mesh, sphere, mdf)
294 end do
295 safe_deallocate_a(mdf)
296
297 ! See Ma and Dudarev, PRB 91, 054420 (2015)
298 ! See for instance https://www.vasp.at/wiki/index.php/I_CONSTRAINED_M
299 select case(this%level)
300 case(constrain_dir)
301 norm = norm2(lmm)
302 if (norm > tol_mag_norm) then
303 dotp = dot_product(lmm, this%constrain(1:3, ia))
304 this%energy = this%energy + this%lambda * (norm - dotp)
305 bb = lmm/norm - this%constrain(:,ia)
306 else
307 call submesh_end(sphere)
308 cycle
309 end if
310 case(constrain_full)
311 this%energy = this%energy + this%lambda * sum((lmm - this%constrain(:,ia))**2)
312 bb = m_two * (lmm - this%constrain(:,ia))
313 end select
314 bb = bb * this%lambda
315
316 ! We are adding an effective Zeeman term within the atomic sphere
317 select case(std%ispin)
318 case (spin_polarized)
319 b2 = norm2(bb(1:max(space%dim, 3)))
320 do ip = 1, sphere%np
321 this%pot(sphere%map(ip), 1) = this%pot(sphere%map(ip), 1) + b2 * mask(ip)
322 this%pot(sphere%map(ip), 2) = this%pot(sphere%map(ip), 2) - b2 * mask(ip)
323 end do
324
325 case (spinors)
326 do ip = 1, sphere%np
327 this%pot(sphere%map(ip), 1) = this%pot(sphere%map(ip), 1) + bb(3) * mask(ip)
328 this%pot(sphere%map(ip), 2) = this%pot(sphere%map(ip), 2) - bb(3) * mask(ip)
329 this%pot(sphere%map(ip), 3) = this%pot(sphere%map(ip), 3) + bb(1) * mask(ip)
330 this%pot(sphere%map(ip), 4) = this%pot(sphere%map(ip), 4) - bb(2) * mask(ip)
331 end do
332
333 end select
334
335 safe_deallocate_a(mask)
336 call submesh_end(sphere)
337 end do
338
339 safe_deallocate_a(md)
340
341 call profiling_out(tostring(magnetic_constrain))
342
344 end subroutine magnetic_constrain_update
345
347
348!! Local Variables:
349!! mode: f90
350!! coding: utf-8
351!! End:
Prints out to iunit a message in the form: ["InputVariable" = value] where "InputVariable" is given b...
Definition: messages.F90:182
double sin(double __x) __attribute__((__nothrow__
integer, parameter, public unpolarized
Parameters...
integer, parameter, public spinors
integer, parameter, public spin_polarized
real(real64), parameter, public m_two
Definition: global.F90:202
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:198
real(real64), parameter, public m_half
Definition: global.F90:206
real(real64), parameter, public m_one
Definition: global.F90:201
This modules implements the routines for doing constrain DFT for noncollinear magnetism.
subroutine, public magnetic_constrain_copy(this_out, this_in)
Deep-copy magnetic constrain data.
subroutine, public magnetic_constrain_end(this)
Releases memory of the magnetic constrain.
subroutine, public magnetic_constrain_update(this, mesh, std, space, latt, pos, rho)
Recomputes the magnetic contraining potential.
integer, parameter, public constrain_full
integer, parameter, public constrain_dir
subroutine, public magnetic_constrain_init(this, namespace, mesh, std, natoms, min_dist)
Initilializes the magnetic_constrain_t object.
subroutine, public magnetic_density(mesh, std, rho, md)
Definition: magnetic.F90:164
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
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_experimental(name, namespace)
Definition: messages.F90:1040
integer function, public parse_block(namespace, name, blk, check_varinfo_)
Definition: parser.F90:623
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:631
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:554
This module handles spin dimensions of the states and the k-point distribution.
real(real64) function, public dsm_integrate(mesh, sm, ff, reduce)
Definition: submesh.F90:1091
subroutine, public submesh_end(this)
Definition: submesh.F90:677
subroutine, public submesh_init(this, space, mesh, latt, center, rc)
Definition: submesh.F90:226
Datatype containing the magnetic constrain information.
Describes mesh distribution to nodes.
Definition: mesh.F90:187
class for organizing spins and k-points
A submesh is a type of mesh, used for the projectors in the pseudopotentials It contains points on a ...
Definition: submesh.F90:174