Octopus
mxll_elec_coupling.F90
Go to the documentation of this file.
1!! Copyright (C) 2023 F. Bonafé
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#include "global.h"
19
22 use debug_oct_m
24 use global_oct_m
25 use grid_oct_m
27 use, intrinsic :: iso_fortran_env
28 use math_oct_m
29 use mesh_oct_m
32 use parser_oct_m
34 use space_oct_m
37
38 implicit none
39
40 private
41 public :: &
49
50 type :: mxll_coupling_t
51 integer :: coupling_mode
52 integer :: coupling_terms
53 integer :: dipole_field
54 real(real64), allocatable :: e_field(:,:)
55 real(real64), allocatable :: b_field(:,:)
56 real(real64), allocatable :: vec_pot(:,:)
57 real(real64), allocatable :: e_field_dip(:)
58 real(real64), allocatable :: vec_pot_dip(:)
59 real(real64), allocatable :: b_field_dip(:)
60 real(real64), allocatable :: e_quadrupole_pot(:)
61 logical :: calc_field_dip = .false.
62 logical :: add_electric_dip = .false.
63 logical :: add_electric_quad = .false.
64 logical :: add_magnetic_dip = .false.
65 logical :: add_zeeman = .false.
66 logical :: add_non_uniform_vec_pot = .false.
67 real(real64) :: center_of_mass(1:3)
68 integer :: center_of_mass_ip
69 integer :: center_of_mass_rankmin
70 logical :: test_equad = .false.
71 type(derivatives_t), pointer, private :: der
72 real(real64), private :: mass
73 end type mxll_coupling_t
74
75 ! See explanation in the MaxwellCouplingMode variable description.
76 integer, public, parameter :: &
77 NO_MAXWELL_COUPLING = 0, &
82
83 integer, public, parameter :: &
84 DIPOLE_AVERAGE = 0, &
86
87
88contains
89
90 ! ------------------------------------------------------------------------
92 subroutine mxll_coupling_init(this, d, gr, namespace, mass)
93 type(mxll_coupling_t), intent(inout) :: this
94 type(states_elec_dim_t),intent(in) :: d
95 type(grid_t), target, intent(in) :: gr
96 type(namespace_t), intent(in) :: namespace
97 real(real64), intent(in) :: mass
98
99 integer :: terms_default, fmc_default, multipolar_terms, pauli_terms
100
101 push_sub(mxll_coupling_init)
102
103 this%der => gr%der
104 this%mass = mass
105
106 !%Variable MaxwellCouplingMode
107 !%Type integer
108 !%Default none
109 !%Section Hamiltonian
110 !%Description
111 !% This variable selects the level of light-matter coupling with electromagnetic fields from the Maxwell solver.
112 !% Each option defines a coupling Hamiltonian <math> H_{int}</math>.
113 !%Option none 0
114 !% No coupling.
115 !%Option length_gauge_dipole 1
116 !% Dipole level in length gauge with transverse electric field E(t):
117 !% <math> H_{int} = -r.E </math>
118 !% The option MaxwellDipoleField should be set to define if <math>E</math> is calculated as an average at evaluated
119 !% at the center of mass (by default, dipole coupling with the average electric field will be used).
120 !%Option multipolar_expansion 2
121 !% The option MultipoleExpansionTerms selects the terms to be included (electric dipole, electric quadrupole
122 !% and/or magnetic dipole).
123 !%Option velocity_gauge_dipole 3
124 !% Coupling at dipole level in velocity gauge with transverse vector potential A(t).
125 !% <math> H_{int} = 1/2m (2 A(t).p + A^2(t)) </math>
126 !% The option MaxwellDipoleField should be set.
127 !%Option full_minimal_coupling 4
128 !% Full vector potential A(r,t) coupled to the momentum.
129 !% <math> H_{int} = 1/2m (2 A(r,t).p + A^2(r,t)) </math>
130 !%End
131 call parse_variable(namespace, 'MaxwellCouplingMode', no_maxwell_coupling, &
132 this%coupling_mode)
133 this%add_electric_dip = (this%coupling_mode == length_gauge_dipole)
134
135 if (this%coupling_mode == multipolar_expansion) then
136
137 !%Variable MultipolarExpansionTerms
138 !%Type flag
139 !%Default electric_dipole + electric_quadrupole + magnetic_dipole
140 !%Section Hamiltonian
141 !%Description
142 !% Terms to be included in the multipolar expansion.
143 !% For this type of coupling to make sense, the E field has to be calculated at the center of mass (not averaged).
144 !%Option electric_dipole bit(1)
145 !% Adds electric dipole term in length gauge, uses transverse electric field E(t):
146 !% <math> H_{int} = -e (r.E) </math>
147 !%Option electric_quadrupole bit(2)
148 !% Adds electric quadrupole term:
149 !% <math> H_{int} = (1/2) e (r . Q . r) </math>
150 !% where Q is the outer product of gradient and electric field:
151 !% <math> Q_{ij} = partial_i E_j |_{r=r_0} </math>
152 !%Option magnetic_dipole bit(3)
153 !% Adds magnetic dipole term:
154 !% <math> H_{int} = -i (e hbar / 2m) sum_i (B(r_0) . (r x grad)) </math>
155 !%End
156 terms_default = &
157 option__multipolarexpansionterms__electric_dipole + &
158 option__multipolarexpansionterms__electric_quadrupole + &
159 option__multipolarexpansionterms__magnetic_dipole
161 call parse_variable(namespace, 'MultipolarExpansionTerms', terms_default, multipolar_terms)
163 if (bitand(multipolar_terms, option__multipolarexpansionterms__electric_dipole) /= 0) then
164 this%add_electric_dip = .true.
165 end if
166 if (bitand(multipolar_terms, option__multipolarexpansionterms__electric_quadrupole) /=0) then
167 this%add_electric_quad = .true.
168 end if
169 if (bitand(multipolar_terms, option__multipolarexpansionterms__magnetic_dipole) /=0) then
170 this%add_magnetic_dip = .true.
171 end if
172
173 end if
174
175 if (this%coupling_mode == full_minimal_coupling) then
176
177 !%Variable PauliHamiltonianTerms
178 !%Type flag
179 !%Default non_uniform_vector_potential + zeeman_term
180 !%Section Hamiltonian
181 !%Description
182 !% Terms to be included in the Pauli Hamiltonnian.
183 !%Option non_uniform_vector_potential bit(1)
184 !% Adds non-uniform vector potential to the canonical momentum.
185 !%Option zeeman_term bit(2)
186 !% Adds the non-uniform Zeeman potential.
187 !%End
188
189 if (d%nspin == 2) then
190 fmc_default = option__paulihamiltonianterms__non_uniform_vector_potential + &
191 option__paulihamiltonianterms__zeeman_term
192 else
193 fmc_default = option__paulihamiltonianterms__non_uniform_vector_potential
194 end if
195
196 call parse_variable(namespace, 'PauliHamiltonianTerms', fmc_default, pauli_terms)
197 if (bitand(pauli_terms, option__paulihamiltonianterms__non_uniform_vector_potential) /= 0) then
198 this%add_non_uniform_vec_pot = .true.
199 end if
200 if (bitand(pauli_terms, option__paulihamiltonianterms__zeeman_term) /=0) then
201 this%add_zeeman = .true.
202 end if
203
204 end if
205
206 !%Variable MaxwellDipoleField
207 !%Type integer
208 !%Default average
209 !%Section Hamiltonian
210 !%Description
211 !% This variable selects the method to get the E field vector at the position of the system
212 !% if Maxwell-matter coupling at dipole level within length gauge is done.
213 !%Option average 0
214 !% Transverse E field is averaged in the volume occupied by the matter system.
215 !%Option center_of_mass 1
216 !% Tranverse E field is evaluated at the center of mass of the matter system (at a single point).
217 !%End
218 call parse_variable(namespace, 'MaxwellDipoleField', dipole_average, &
219 this%dipole_field)
220
221 if (this%coupling_mode /= no_maxwell_coupling) then
222 safe_allocate(this%e_field(1:gr%np, 1:gr%box%dim))
223 safe_allocate(this%b_field(1:gr%np, 1:gr%box%dim))
224 safe_allocate(this%vec_pot(1:gr%np, 1:gr%box%dim))
225 this%e_field = m_zero
226 this%b_field = m_zero
227 this%vec_pot = m_zero
228 safe_allocate(this%e_field_dip(1:gr%box%dim))
229 this%e_field_dip = m_zero
230 safe_allocate(this%b_field_dip(1:gr%box%dim))
231 this%b_field_dip = m_zero
232 safe_allocate(this%vec_pot_dip(1:gr%box%dim))
233 this%vec_pot_dip = m_zero
234 end if
235
236 if (this%coupling_mode == multipolar_expansion .and. this%add_electric_quad) then
237 safe_allocate(this%e_quadrupole_pot(1:gr%np))
238 end if
239
240 call mxll_quadrupole_test_init(this, gr, namespace)
241
242 pop_sub(mxll_coupling_init)
243
244 end subroutine mxll_coupling_init
245
246 ! ------------------------------------------------------------------------
250 subroutine mxll_quadrupole_test_init(this, gr, namespace)
251 type(mxll_coupling_t), intent(inout) :: this
252 type(grid_t), intent(in) :: gr
253 type(namespace_t), intent(in) :: namespace
254
255 integer :: ip, rankmin
256 real(real64) :: dmin
257
259
260 !%Variable MaxwellTestQuadrupole
261 !%Type logical
262 !%Default no
263 !%Section Maxwell
264 !%Description
265 !% Override Maxwell field with linear E field for testing.
266 !%End
267 call parse_variable(namespace, 'MaxwellTestQuadrupole', .false., this%test_equad)
268
269 if (this%test_equad) then
270 safe_allocate(this%e_field(1:gr%np, 1:gr%box%dim))
271 this%e_field = m_zero
272 do ip = 1, gr%np
273 this%e_field(ip,1) = 0.02_real64 * gr%x(1, ip)
274 end do
275 this%calc_field_dip = .true.
276 this%center_of_mass(1:3) = m_zero
277 this%center_of_mass_ip = mesh_nearest_point(gr, this%center_of_mass, dmin, rankmin)
278 this%center_of_mass_rankmin = rankmin
279 safe_allocate(this%e_quadrupole_pot(1:gr%np))
280 this%e_quadrupole_pot = m_zero
281 end if
282
284
285 end subroutine mxll_quadrupole_test_init
286
287 ! ------------------------------------------------------------------------
289 subroutine mxll_coupling_calc(this, hm_base, mesh, d, space)
290 type(mxll_coupling_t), intent(inout) :: this
291 type(hamiltonian_elec_base_t), intent(inout) :: hm_base
292 type(mesh_t), intent(in) :: mesh
293 type(states_elec_dim_t), intent(in) :: d
294 class(space_t), intent(in) :: space
295
296 integer :: ip, ispin, idir
297
298 push_sub(mxll_coupling_calc)
299
300 ! coupling to Maxwell field
301 select case (this%coupling_mode)
302
304
305 if (this%add_electric_dip) then
306 do ispin = 1, d%spin_channels
307 do ip = 1, mesh%np
308 ! The -1 sign is missing here. Check epot.F90 for the explanation.
309 hm_base%potential(ip, ispin) = hm_base%potential(ip, ispin) + &
310 sum(this%e_field_dip(1:space%dim) * (mesh%x(1:space%dim, ip) - this%center_of_mass(1:space%dim)))
311 end do
312 end do
313 end if
314
315 if (this%add_electric_quad .and. this%calc_field_dip) then
316 ! the calc_field_dip condition prevents calling the routine before initializing the interactions
317 call set_electric_quadrupole_pot(this, mesh)
318 do ispin = 1, d%spin_channels
319 do ip = 1, mesh%np
320 hm_base%potential(ip, ispin) = hm_base%potential(ip, ispin) + this%e_quadrupole_pot(ip)
321 end do
322 end do
323 end if
324
325 ! magnetic_dipole term is added in hamiltonian_elec_inc.F90
326
328 call hm_base%allocate_field(mesh, field_uniform_vector_potential, .false.)
329 ! The minus sign is here is for the wrong convention of Octopus for the phase
330 hm_base%uniform_vector_potential(1:space%dim) = hm_base%uniform_vector_potential(1:space%dim) - &
331 this%vec_pot_dip(1:space%dim)
332
334 call hm_base%allocate_field(mesh, field_vector_potential, .false.)
335 if (this%add_non_uniform_vec_pot) then
336 do idir = 1, mesh%box%dim
337 hm_base%vector_potential(idir, :) = hm_base%vector_potential(idir, :) + this%vec_pot(:, idir)
338 end do
339 end if
340 if (this%add_zeeman) then
341 call hm_base%allocate_field(mesh, field_magnetic_field, .false.)
342 hm_base%magnetic_field(:,:) = hm_base%magnetic_field(:,:) + this%b_field(:,:)
343 end if
344
345 end select
346
347 pop_sub(mxll_coupling_calc)
348
349 end subroutine mxll_coupling_calc
350
351 ! ------------------------------------------------------------------------
353 subroutine mxll_coupling_end(this)
354 type(mxll_coupling_t), intent(inout) :: this
355
356 push_sub(mxll_coupling_end)
357
358 safe_deallocate_a(this%e_field)
359 safe_deallocate_a(this%b_field)
360 safe_deallocate_a(this%vec_pot)
361 safe_deallocate_a(this%e_field_dip)
362 safe_deallocate_a(this%b_field_dip)
363 safe_deallocate_a(this%vec_pot_dip)
364 safe_deallocate_a(this%e_quadrupole_pot)
365
366 pop_sub(mxll_coupling_end)
367 end subroutine mxll_coupling_end
368
369 ! ------------------------------------------------------------------------
371 subroutine mxll_coupling_copy(this_out, this_in, der_target)
372 type(mxll_coupling_t), intent(out) :: this_out
373 type(mxll_coupling_t), intent(in) :: this_in
374 type(derivatives_t), target, intent(in) :: der_target
375
376 push_sub(mxll_coupling_copy)
377
378 this_out%coupling_mode = this_in%coupling_mode
379 this_out%coupling_terms = this_in%coupling_terms
380 this_out%dipole_field = this_in%dipole_field
381 this_out%calc_field_dip = this_in%calc_field_dip
382 this_out%add_electric_dip = this_in%add_electric_dip
383 this_out%add_electric_quad = this_in%add_electric_quad
384 this_out%add_magnetic_dip = this_in%add_magnetic_dip
385 this_out%add_zeeman = this_in%add_zeeman
386 this_out%add_non_uniform_vec_pot = this_in%add_non_uniform_vec_pot
387 this_out%center_of_mass = this_in%center_of_mass
388 this_out%center_of_mass_ip = this_in%center_of_mass_ip
389 this_out%center_of_mass_rankmin = this_in%center_of_mass_rankmin
390 this_out%test_equad = this_in%test_equad
391
392 this_out%der => der_target
393 this_out%mass = this_in%mass
394
395 if (allocated(this_in%e_field)) then
396 safe_allocate(this_out%e_field(1:size(this_in%e_field, dim=1), 1:size(this_in%e_field, dim=2)))
397 this_out%e_field = this_in%e_field
398 end if
399
400 if (allocated(this_in%b_field)) then
401 safe_allocate(this_out%b_field(1:size(this_in%b_field, dim=1), 1:size(this_in%b_field, dim=2)))
402 this_out%b_field = this_in%b_field
403 end if
404
405 if (allocated(this_in%vec_pot)) then
406 safe_allocate(this_out%vec_pot(1:size(this_in%vec_pot, dim=1), 1:size(this_in%vec_pot, dim=2)))
407 this_out%vec_pot = this_in%vec_pot
408 end if
409
410 if (allocated(this_in%e_field_dip)) then
411 safe_allocate(this_out%e_field_dip(1:size(this_in%e_field_dip)))
412 this_out%e_field_dip = this_in%e_field_dip
413 end if
414
415 if (allocated(this_in%vec_pot_dip)) then
416 safe_allocate(this_out%vec_pot_dip(1:size(this_in%vec_pot_dip)))
417 this_out%vec_pot_dip = this_in%vec_pot_dip
418 end if
419
420 if (allocated(this_in%b_field_dip)) then
421 safe_allocate(this_out%b_field_dip(1:size(this_in%b_field_dip)))
422 this_out%b_field_dip = this_in%b_field_dip
423 end if
424
425 if (allocated(this_in%e_quadrupole_pot)) then
426 safe_allocate(this_out%e_quadrupole_pot(1:size(this_in%e_quadrupole_pot)))
427 this_out%e_quadrupole_pot = this_in%e_quadrupole_pot
428 end if
429
430 pop_sub(mxll_coupling_copy)
431 end subroutine mxll_coupling_copy
432
433
434 ! ------------------------------------------------------------------------
438 subroutine set_electric_quadrupole_pot(this, mesh)
439 type(mxll_coupling_t), intent(inout) :: this
440 type(mesh_t), intent(in) :: mesh
441
442 real(real64), allocatable :: e_field_quadrupole_tensor(:,:), tmp_partial(:,:), this_e_field(:,:)
443 real(real64) :: r(3), tensor_dot_rr(3)
444 integer :: ip, i, dims
445
447
448 call profiling_in('SET_ELECTRIC_QUADRUPOLE')
449
450 safe_allocate(e_field_quadrupole_tensor(1:mesh%box%dim, 1:mesh%box%dim))
451 safe_allocate(this_e_field(1:mesh%np_part, 1:mesh%box%dim))
452 safe_allocate(tmp_partial(1:mesh%np, 1:mesh%box%dim))
453
454 this_e_field(1:mesh%np,:) = this%e_field(1:mesh%np,:)
455
456 e_field_quadrupole_tensor = m_zero
457 dims = this%der%dim
458 do i = 1, dims
459 call dderivatives_grad(this%der, this_e_field(:, i), tmp_partial)
460 if (mesh%mpi_grp%rank == this%center_of_mass_rankmin) then
461 e_field_quadrupole_tensor(i, 1:dims) = tmp_partial(this%center_of_mass_ip, 1:dims)
462 end if
463 end do
464 call mesh%allreduce(e_field_quadrupole_tensor)
465
466 do ip = 1, mesh%np
467 r(:) = mesh%x(:, ip) - this%center_of_mass(:)
468 tensor_dot_rr = matmul(e_field_quadrupole_tensor, r)
469 ! This term is +1/2 r.Q.r and not -1/2 r.Q.r (as e=+1 in Octopus)
470 this%e_quadrupole_pot(ip) = m_half * dot_product(r, tensor_dot_rr)
471 end do
472
473 safe_deallocate_a(e_field_quadrupole_tensor)
474 safe_deallocate_a(tmp_partial)
475 safe_deallocate_a(this_e_field)
476
477 call profiling_out('SET_ELECTRIC_QUADRUPOLE')
479
480 end subroutine set_electric_quadrupole_pot
481
482
483 ! ------------------------------------------------------------------------
488 subroutine magnetic_dipole_coupling(this, psib, hpsib)
489 type(mxll_coupling_t), intent(in) :: this
490 type(wfs_elec_t), intent(inout) :: psib
491 type(wfs_elec_t), intent(inout) :: hpsib
492
493 integer :: ip, idir
494 real(real64) :: rtmp(3)
495 real(real64), allocatable :: bxr(:,:)
496 type(wfs_elec_t) :: gradb(this%der%dim)
497 type(wfs_elec_t) :: bxr_dot_gradb
498
500
501 safe_allocate(bxr(1:this%der%mesh%np,1:3))
502 call profiling_in('MAGNETIC_DIPOLE')
503
504 do ip = 1, this%der%mesh%np
505 rtmp(:) = this%der%mesh%x(:, ip) - this%center_of_mass
506 bxr(ip,:) = dcross_product(this%b_field_dip, rtmp)
507 end do
508
509 call zderivatives_batch_grad(this%der, psib, gradb)
510
511 call hpsib%copy_to(bxr_dot_gradb)
512 do idir = 1, this%der%dim
513 call batch_mul_mf(this%der%mesh%np, bxr(:,idir), gradb(idir), bxr_dot_gradb)
514 call batch_axpy(this%der%mesh%np, m_zi*m_half/this%mass, bxr_dot_gradb, hpsib)
515 end do
516
517 call bxr_dot_gradb%end()
518 do idir = 1, this%der%dim
519 call gradb(idir)%end()
520 end do
521 safe_deallocate_a(bxr)
522
523 call profiling_out('MAGNETIC_DIPOLE')
525 end subroutine magnetic_dipole_coupling
526
528
529!! Local Variables:
530!! mode: f90
531!! coding: utf-8
532!! End:
batchified version of the BLAS axpy routine:
Definition: batch_ops.F90:159
batchified multiplication by mesh function with optional conjugation:
Definition: batch_ops.F90:254
This module implements common operations on batches of mesh functions.
Definition: batch_ops.F90:118
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
subroutine, public dderivatives_grad(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the gradient to a mesh function
subroutine, public zderivatives_batch_grad(der, ffb, opffb, ghost_update, set_bc, to_cartesian, factor)
apply the gradient to a batch of mesh functions
real(real64), parameter, public m_zero
Definition: global.F90:200
complex(real64), parameter, public m_zi
Definition: global.F90:214
real(real64), parameter, public m_half
Definition: global.F90:206
This module implements the underlying real-space grid.
Definition: grid.F90:119
integer, parameter, public field_uniform_vector_potential
integer, parameter, public field_vector_potential
integer, parameter, public field_magnetic_field
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
pure real(real64) function, dimension(1:3), public dcross_product(a, b)
Definition: math.F90:1895
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
integer function, public mesh_nearest_point(mesh, pos, dmin, rankmin)
Returns the index of the point which is nearest to a given vector position pos.
Definition: mesh.F90:386
subroutine, public mxll_coupling_init(this, d, gr, namespace, mass)
Parse variables and initialize Maxwell coupling.
subroutine, public set_electric_quadrupole_pot(this, mesh)
Computes the electric quadrupole potential where .
subroutine mxll_quadrupole_test_init(this, gr, namespace)
Initializes quadrupole test when requested. The test applies an electric field defined as E=(0....
integer, parameter, public length_gauge_dipole
subroutine, public mxll_coupling_end(this)
Finalize and deallocate Maxwell coupling arrays.
subroutine, public mxll_coupling_calc(this, hm_base, mesh, d, space)
Add the Maxwell coupling to the electronic Hamiltonian.
subroutine, public mxll_coupling_copy(this_out, this_in, der_target)
Deep-copy Maxwell-electron coupling data and rebind derivatives.
integer, parameter, public velocity_gauge_dipole
integer, parameter, public multipolar_expansion
subroutine, public magnetic_dipole_coupling(this, psib, hpsib)
Computes the magnetic dipole term of the Hamiltonian. This routine acually implements the equivalent...
integer, parameter, public dipole_at_com
integer, parameter, public full_minimal_coupling
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.
class representing derivatives
Description of the grid, containing information on derivatives, stencil, and symmetries.
Definition: grid.F90:171
The basic Hamiltonian for electronic system.
Describes mesh distribution to nodes.
Definition: mesh.F90:187
class for organizing spins and k-points
batches of electronic states
Definition: wfs_elec.F90:141
int true(void)