Octopus
lattice_vectors.F90
Go to the documentation of this file.
1!! Copyright (C) 2021 N. Tancogne-Dejean, M. Oliveira
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, intrinsic :: iso_fortran_env
25 use math_oct_m
28 use parser_oct_m
30 use space_oct_m
31 use unit_oct_m
33
34 implicit none
35
36 private
37
38 public :: &
41
43 ! Components are public by default
44 type(space_t), private :: space
45 real(real64), allocatable :: rlattice_primitive(:, :)
46 real(real64), allocatable :: rlattice (:, :)
47 real(real64), allocatable :: klattice_primitive(:, :)
48 real(real64), allocatable :: klattice (:, :)
49 real(real64) :: alpha, beta, gamma
50 real(real64) :: rcell_volume
51 logical :: nonorthogonal = .false.
52 ! Some notes:
53 ! rlattice_primitive is the A matrix from Chelikowski PRB 78 075109 (2008)
54 ! klattice_primitive is the transpose (!) of the B matrix, with no 2 pi factor included
55 ! klattice is the proper reciprocal lattice vectors, with 2 pi factor, and in units of 1/bohr
56 ! The F matrix of Chelikowski is matmul(transpose(latt%klattice_primitive), latt%klattice_primitive)
57 contains
58 procedure :: copy => lattice_vectors_copy
59 generic :: assignment(=) => copy
60 procedure :: scale => lattice_vectors_scale
61 procedure :: write_info => lattice_vectors_write_info
62 procedure :: short_info => lattice_vectors_short_info
63 procedure :: cart_to_red => lattice_vectors_cart_to_red
64 procedure :: red_to_cart => lattice_vectors_red_to_cart
65 procedure :: fold_into_cell => lattice_vectors_fold_into_cell
66 procedure :: max_length => lattice_vectors_max_length
67 procedure :: update => lattice_vectors_update
69 end type lattice_vectors_t
70
71 interface lattice_vectors_t
73 end interface lattice_vectors_t
74
79 private
80 integer, public :: n_cells = 0
81 integer, allocatable :: icell(:, :)
82 type(lattice_vectors_t), pointer :: latt => null()
83 contains
84 procedure :: copy => lattice_iterator_copy
85 generic :: assignment(=) => copy
86 procedure :: get => lattice_iterator_get
88 end type lattice_iterator_t
89
90 interface lattice_iterator_t
91 module procedure lattice_iterator_constructor
92 end interface lattice_iterator_t
93
94 real(real64), parameter :: eps = 100.0_real64 * m_epsilon
95
96contains
97
98 !--------------------------------------------------------------
99 type(lattice_vectors_t) function lattice_vectors_constructor_from_rlattice(namespace, space, rlattice) result(latt)
100 type(namespace_t), intent(in) :: namespace
101 class(space_t), intent(in) :: space
102 real(real64), intent(in) :: rlattice(:, :)
103
104 integer :: idir, idir2
105 real(real64) :: volume_element
106
107 push_sub(lattice_vectors_constructor_from_rlattice)
108
109 latt%space = space
110
111 safe_allocate(latt%rlattice_primitive(1:space%dim, 1:space%dim))
112 safe_allocate(latt%rlattice(1:space%dim, 1:space%dim))
113 safe_allocate(latt%klattice_primitive(1:space%dim, 1:space%dim))
114 safe_allocate(latt%klattice(1:space%dim, 1:space%dim))
115
116 latt%rlattice(1:space%dim, 1:space%dim) = rlattice(1:space%dim, 1:space%dim)
117 do idir = 1, space%dim
118 latt%rlattice_primitive(:, idir) = latt%rlattice(:, idir) / norm2(latt%rlattice(:, idir))
119 end do
120
121 latt%nonorthogonal = .false.
122 do idir = 1, space%dim
123 do idir2 = 1, space%dim
124 if (idir /= idir2 .and. abs(latt%rlattice_primitive(idir, idir2)) > m_epsilon) then
125 latt%nonorthogonal = .true.
126 end if
127 end do
128 end do
129
130 call reciprocal_lattice(latt%rlattice, latt%klattice, latt%rcell_volume, space%dim, namespace)
131 latt%klattice = latt%klattice * m_two*m_pi
132
133 call reciprocal_lattice(latt%rlattice_primitive, latt%klattice_primitive, volume_element, space%dim, namespace)
134
135 if (space%dim == 3) then
136 call angles_from_rlattice_primitive(latt%rlattice_primitive, latt%alpha, latt%beta, latt%gamma)
137 else
138 ! Angles should not be used, so set them to zero
139 latt%alpha = m_zero
140 latt%beta = m_zero
141 latt%gamma = m_zero
142 end if
144 pop_sub(lattice_vectors_constructor_from_rlattice)
147 !--------------------------------------------------------------
148 type(lattice_vectors_t) function lattice_vectors_constructor_from_input(namespace, space, variable_prefix) result(latt)
149 type(namespace_t), intent(in) :: namespace
150 class(space_t), intent(in) :: space
151 character(len=*), optional, intent(in) :: variable_prefix
152
153 type(block_t) :: blk
154 real(real64) :: norm, lparams(space%dim), volume_element
155 integer :: idim, jdim, ncols
156 logical :: has_angles
157 real(real64) :: angles(1:space%dim)
158 character(len=:), allocatable :: prefix
162 if (present(variable_prefix)) then
163 prefix = variable_prefix
164 else
165 prefix = ''
166 end if
167
168 latt%space = space
169
170 safe_allocate(latt%rlattice_primitive(1:space%dim, 1:space%dim))
171 safe_allocate(latt%rlattice(1:space%dim, 1:space%dim))
172 safe_allocate(latt%klattice_primitive(1:space%dim, 1:space%dim))
173 safe_allocate(latt%klattice(1:space%dim, 1:space%dim))
174
175 latt%alpha = 90.0_real64
176 latt%beta = 90.0_real64
177 latt%gamma = 90.0_real64
178
179 has_angles = .false.
180 angles = 90.0_real64
182 if (space%is_periodic()) then
183
184 !%Variable LatticeParameters
185 !%Type block
186 !%Section Mesh::Simulation Box
187 !%Description
188 !% The lattice parameters (a, b, c).
189 !% This variable is mandatory for periodic systems and is ignored otherwise.
190 !% When PeriodicDimensions = 3, a second optional line can be used to
191 !% define the angles between the lattice vectors. If the angles are not
192 !% provided, then the variable LatticeVectors must be set.
193 !% The number of parameters specified in the block must be at least equal
194 !% to the number of periodic dimensions, but it is not mandatory to
195 !% specify parameters for the non-periodic dimensions (in that case they
196 !% are set to 1).
197 !%End
198 if (parse_block(namespace, prefix//'LatticeParameters', blk) == 0) then
199 ncols = parse_block_cols(blk, 0)
200 if (ncols < space%periodic_dim) then
201 call messages_input_error(namespace, prefix//'LatticeParameters', &
202 'The number of columns must be at least PeriodicDimensions')
203 end if
204 do idim = 1, ncols
205 call parse_block_float(blk, 0, idim - 1, lparams(idim))
206 end do
207
208 ! If some parameters for non-periodic dimensions are not set in the input file, with set them to 1.
209 do idim = ncols + 1, space%dim
210 lparams(idim) = m_one
211 end do
212
213 ! Parse angles, if available
214 if (parse_block_n(blk) > 1) then
215 if (space%dim /= 3) then
216 call messages_input_error(namespace, prefix//'LatticeParameters', 'Angles can only be specified when Dimensions = 3')
217 end if
218
219 ncols = parse_block_cols(blk, 1)
220 if (ncols /= space%dim) then
221 call messages_input_error(namespace, prefix//'LatticeParameters', 'You must specify three angles')
222 end if
223 do idim = 1, space%dim
224 call parse_block_float(blk, 1, idim - 1, angles(idim))
225 end do
226 has_angles = .true.
227 end if
228 call parse_block_end(blk)
229
230 else
231 call messages_input_error(namespace, prefix//'LatticeParameters', 'Variable is mandatory for periodic systems')
232 end if
233
234
235 if (has_angles) then
236 latt%alpha = angles(1)
237 latt%beta = angles(2)
238 latt%gamma = angles(3)
239
240 if (parse_is_defined(namespace, prefix//'LatticeVectors')) then
241 message(1) = 'LatticeParameters with angles is incompatible with LatticeVectors'
242 call messages_print_var_info(prefix//"LatticeParameters", namespace=namespace)
243 call messages_fatal(1, namespace=namespace)
244 end if
245
246 call build_metric_from_angles(latt, angles)
247
248 else
249
250 !%Variable LatticeVectors
251 !%Type block
252 !%Default simple cubic
253 !%Section Mesh::Simulation Box
254 !%Description
255 !% Primitive lattice vectors. Vectors are stored in rows.
256 !% LatticeVectors are normalized and then used, if both LatticeParameters and
257 !% LatticeVectors are defined, norm of LatticeVectors are
258 !% used as multipliers of lattice parameters.
259 !% Default:
260 !% <br><br><tt>%LatticeVectors
261 !% <br>&nbsp;&nbsp;1.0 | 0.0 | 0.0
262 !% <br>&nbsp;&nbsp;0.0 | 1.0 | 0.0
263 !% <br>&nbsp;&nbsp;0.0 | 0.0 | 1.0
264 !% <br>%<br></tt>
265 !%End
266 latt%rlattice_primitive(:, :) = diagonal_matrix(space%dim, m_one)
267 latt%nonorthogonal = .false.
268
269 if (parse_block(namespace, prefix//'LatticeVectors', blk) == 0) then
270 do idim = 1, space%dim
271 do jdim = 1, space%dim
272 call parse_block_float(blk, idim - 1, jdim - 1, latt%rlattice_primitive(jdim, idim))
273 if (idim /= jdim .and. abs(latt%rlattice_primitive(jdim, idim)) > m_epsilon) then
274 latt%nonorthogonal = .true.
275 end if
276 end do
277 end do
278 call parse_block_end(blk)
279
280 end if
281 end if
282
283 else
284 ! Non-periodic
285 lparams = m_one
286 latt%rlattice_primitive(:, :) = diagonal_matrix(space%dim, m_one)
287 end if
288
289 latt%rlattice = m_zero
290 do idim = 1, space%dim
291 norm = norm2(latt%rlattice_primitive(1:space%dim, idim))
292 lparams(idim) = lparams(idim)*norm
293 do jdim = 1, space%dim
294 latt%rlattice_primitive(jdim, idim) = latt%rlattice_primitive(jdim, idim) / norm
295 latt%rlattice(jdim, idim) = latt%rlattice_primitive(jdim, idim) * lparams(idim)
296 end do
297 end do
298
299 call reciprocal_lattice(latt%rlattice, latt%klattice, latt%rcell_volume, space%dim, namespace)
300 latt%klattice = latt%klattice * m_two*m_pi
301
302 call reciprocal_lattice(latt%rlattice_primitive, latt%klattice_primitive, volume_element, space%dim, namespace)
303
304 if (.not. has_angles .and. space%dim == 3) then
305 !We compute the angles from the lattice vectors
306 call angles_from_rlattice_primitive(latt%rlattice_primitive, latt%alpha, latt%beta, latt%gamma)
307 end if
308
311
312 !--------------------------------------------------------------
313 subroutine angles_from_rlattice_primitive(rlattice_primitive, alpha, beta, gamma)
314 real(real64), intent(in) :: rlattice_primitive(1:3, 1:3)
315 real(real64), intent(out) :: alpha
316 real(real64), intent(out) :: beta
317 real(real64), intent(out) :: gamma
318
319 real(real64) :: rlatt(1:3, 1:3)
320
322
323 rlatt = matmul(transpose(rlattice_primitive), rlattice_primitive)
324 alpha = acos(rlatt(2, 3)/sqrt(rlatt(2, 2)*rlatt(3, 3)))/m_pi*180.0_real64
325 beta = acos(rlatt(1, 3)/sqrt(rlatt(1, 1)*rlatt(3, 3)))/m_pi*180.0_real64
326 gamma = acos(rlatt(1, 2)/sqrt(rlatt(1, 1)*rlatt(2, 2)))/m_pi*180.0_real64
327
329 end subroutine angles_from_rlattice_primitive
330
331 !--------------------------------------------------------------
332 subroutine lattice_vectors_copy(this, source)
333 class(lattice_vectors_t), intent(out) :: this
334 class(lattice_vectors_t), intent(in) :: source
335
336 push_sub(lattice_vectors_copy)
337
338 this%space = source%space
339 safe_allocate_source_a(this%rlattice_primitive, source%rlattice_primitive)
340 safe_allocate_source_a(this%rlattice, source%rlattice)
341 safe_allocate_source_a(this%klattice_primitive, source%klattice_primitive)
342 safe_allocate_source_a(this%klattice, source%klattice)
343 this%alpha = source%alpha
344 this%beta = source%beta
345 this%gamma = source%gamma
346 this%rcell_volume = source%rcell_volume
347 this%nonorthogonal = source%nonorthogonal
348
349 pop_sub(lattice_vectors_copy)
350 end subroutine lattice_vectors_copy
351
352 !--------------------------------------------------------------
353 subroutine lattice_vectors_finalize(this)
354 type(lattice_vectors_t), intent(inout) :: this
355
357
358 safe_deallocate_a(this%rlattice_primitive)
359 safe_deallocate_a(this%rlattice)
360 safe_deallocate_a(this%klattice_primitive)
361 safe_deallocate_a(this%klattice)
362 this%nonorthogonal = .false.
363
365 end subroutine lattice_vectors_finalize
366
367 !--------------------------------------------------------------
368 subroutine lattice_vectors_scale(this, factor)
369 class(lattice_vectors_t), intent(inout) :: this
370 real(real64), intent(in) :: factor(this%space%dim)
371
372 integer :: idir
373
374 push_sub(lattice_vectors_scale)
375
376 ! Scale the lattice in real space
377 do idir = 1, this%space%dim
378 this%rlattice(1:this%space%dim, idir) = this%rlattice(1:this%space%dim, idir)*factor(idir)
379 end do
380
381 ! Regenerate the lattice in reciprocal space
382 call reciprocal_lattice(this%rlattice, this%klattice, this%rcell_volume, this%space%dim)
383 this%klattice = this%klattice * m_two*m_pi
384
385 pop_sub(lattice_vectors_scale)
386 end subroutine lattice_vectors_scale
387
388 !--------------------------------------------------------------
390 subroutine lattice_vectors_update(this, rlattice)
391 class(lattice_vectors_t), intent(inout) :: this
392 real(real64), intent(in) :: rlattice(this%space%dim, this%space%dim)
393
394 integer :: idir
395 real(real64) :: volume_element, maxrlatt
396
397 push_sub(lattice_vectors_update)
398
399 this%rlattice(:, :) = rlattice(:, :)
400 maxrlatt = maxval(abs(this%rlattice))
401 where(abs(this%rlattice) < 1e-12_real64*maxrlatt) this%rlattice = m_zero
402
403 ! Update the primitive lattice vectors
404 do idir = 1, this%space%dim
405 this%rlattice_primitive(:, idir) = this%rlattice(:, idir) / norm2(this%rlattice(:, idir))
406 end do
407
408 ! Regenerate the lattice in reciprocal space
409 call reciprocal_lattice(this%rlattice, this%klattice, this%rcell_volume, this%space%dim)
410 this%klattice = this%klattice * m_two*m_pi
411
412 call reciprocal_lattice(this%rlattice_primitive, this%klattice_primitive, volume_element, this%space%dim)
413
414 ! Recompute the angles
415 if (this%space%dim == 3) then
416 call angles_from_rlattice_primitive(this%rlattice_primitive, this%alpha, this%beta, this%gamma)
417 else
418 ! Angles should not be used, so set them to zero
419 this%alpha = m_zero
420 this%beta = m_zero
421 this%gamma = m_zero
422 end if
423
425 end subroutine lattice_vectors_update
426
427 !--------------------------------------------------------------
428 pure function lattice_vectors_cart_to_red(this, xx_cart) result(xx_red)
429 class(lattice_vectors_t), intent(in) :: this
430 real(real64), intent(in) :: xx_cart(this%space%dim)
431 real(real64) :: xx_red(this%space%dim)
432
433 xx_red = matmul(xx_cart, this%klattice)/(m_two*m_pi)
434
435 end function lattice_vectors_cart_to_red
436
437 !--------------------------------------------------------------
438 pure function lattice_vectors_red_to_cart(this, xx_red) result(xx_cart)
439 class(lattice_vectors_t), intent(in) :: this
440 real(real64), intent(in) :: xx_red(this%space%dim)
441 real(real64) :: xx_cart(this%space%dim)
442
443 xx_cart = matmul(this%rlattice, xx_red)
444
445 end function lattice_vectors_red_to_cart
446
447 !--------------------------------------------------------------
448 function lattice_vectors_fold_into_cell(this, xx) result(new_xx)
449 class(lattice_vectors_t), intent(in) :: this
450 real(real64), intent(in) :: xx(this%space%dim)
451 real(real64) :: new_xx(this%space%dim)
452
453 integer :: idir
454
455 if (this%space%is_periodic()) then
456 ! Convert the position to reduced coordinates
457 new_xx = this%cart_to_red(xx)
458
459 do idir = 1, this%space%periodic_dim
460 ! Change of origin
461 new_xx(idir) = new_xx(idir) + m_half
462
463 ! Fold into cell
464 new_xx(idir) = new_xx(idir) - anint(new_xx(idir))
465 if (new_xx(idir) < -1.0e-6_real64) then
466 new_xx(idir) = new_xx(idir) + m_one
467 end if
468
469 ! Sanity checks
470 assert(new_xx(idir) >= -1.0e-6_real64)
471 assert(new_xx(idir) < m_one)
472
473 ! Change origin back
474 new_xx(idir) = new_xx(idir) - m_half
475 end do
476
477 ! Convert back to Cartesian coordinates
478 new_xx = this%red_to_cart(new_xx)
479 else
480 new_xx = xx
481 end if
482
484
485 !--------------------------------------------------------------
486 subroutine lattice_vectors_write_info(this, namespace)
487 class(lattice_vectors_t), intent(in) :: this
488 type(namespace_t), intent(in) :: namespace
489
490 integer :: idir, idir2
491
493
494 assert(this%space%is_periodic())
495
496 call messages_print_with_emphasis(msg="Lattice", namespace=namespace)
497
498 write(message(1),'(a,3a,a)') ' Lattice Vectors [', trim(units_abbrev(units_out%length)), ']'
499 do idir = 1, this%space%periodic_dim
500 write(message(1+idir),'(9f12.6)') (units_from_atomic(units_out%length, this%rlattice(idir2, idir)), &
501 idir2 = 1, this%space%dim)
502 end do
503 call messages_info(1+this%space%periodic_dim, namespace=namespace)
504
505 select case (this%space%periodic_dim)
506 case (1)
507 write(message(1),'(a)') ' Cell length ='
508 case (2)
509 write(message(1),'(a)') ' Cell area ='
510 case default
511 write(message(1),'(a)') ' Cell volume ='
512 end select
513 write(message(1),'(a,1x,f18.4,3a,i1.1,a)') &
514 trim(message(1)), units_from_atomic(units_out%length**this%space%periodic_dim, this%rcell_volume), &
515 ' [', trim(units_abbrev(units_out%length**this%space%periodic_dim)), ']'
516 call messages_info(1, namespace=namespace)
517 write(message(1),'(a,3a,a)') ' Reciprocal-Lattice Vectors [', trim(units_abbrev(units_out%length**(-1))), ']'
518 do idir = 1, this%space%periodic_dim
519 write(message(1+idir),'(3f12.6)') (units_from_atomic(unit_one / units_out%length, this%klattice(idir2, idir)), &
520 idir2 = 1, this%space%dim)
521 end do
522 call messages_info(1+this%space%periodic_dim, namespace=namespace)
524 if (this%space%dim == 3) then
525 write(message(1),'(a)') ' Cell angles [degree]'
526 write(message(2),'(a, f8.3)') ' alpha = ', this%alpha
527 write(message(3),'(a, f8.3)') ' beta = ', this%beta
528 write(message(4),'(a, f8.3)') ' gamma = ', this%gamma
529 call messages_info(4, namespace=namespace)
530 end if
531
532 call messages_print_with_emphasis(namespace=namespace)
535 end subroutine lattice_vectors_write_info
536
537 !--------------------------------------------------------------
538 character(len=140) function lattice_vectors_short_info(this, unit_length) result(info)
539 class(lattice_vectors_t), intent(in) :: this
540 type(unit_t), intent(in) :: unit_length
541
542 integer :: idir1, idir2
545
546 write(info, '(a,a,a)') 'LatticeVectors [', trim(units_abbrev(unit_length)), '] = '
547
548 do idir1 = 1, this%space%dim
549 write(info, '(a,a)') trim(info), '['
550 do idir2 = 1, this%space%dim
551 write(info, '(a,1x,f11.6)') trim(info), units_from_atomic(unit_length, this%rlattice(idir2, idir1))
552 end do
553 write(info, '(a,a)') trim(info), ']'
554 if (idir1 < this%space%dim) then
555 write(info, '(a,a)') trim(info), ', '
556 end if
557 end do
558
560 end function lattice_vectors_short_info
561
562 !--------------------------------------------------------------
563 real(real64) function lattice_vectors_max_length(this) result(length)
564 class(lattice_vectors_t), intent(in) :: this
565
567
568 if (this%space%is_periodic()) then
569 length = maxval(norm2(this%rlattice(:,1:this%space%periodic_dim), dim=1))
570 else
571 length = m_zero
572 end if
573
575 end function lattice_vectors_max_length
576
577 !--------------------------------------------------------------
578 subroutine build_metric_from_angles(this, angles)
579 type(lattice_vectors_t), intent(inout) :: this
580 real(real64), intent(in) :: angles(3)
582 real(real64) :: cosang, a2, aa, cc
583 real(real64), parameter :: tol_angle = 1.0e-6_real64
584
586
587 !Converting the angles to LatticeVectors
588 !See 57_iovars/ingeo.F90 in Abinit for details
589 if (abs(angles(1) - angles(2)) < tol_angle .and. abs(angles(2) - angles(3)) < tol_angle .and. &
590 (abs(angles(1) - 90.0_real64) + abs(angles(2) - 90.0_real64) + abs(angles(3) - 90.0_real64)) > tol_angle) then
591
592 cosang = cos(m_pi*angles(1)/180.0_real64)
593 a2 = m_two/m_three*(m_one - cosang)
594 aa = sqrt(a2)
595 cc = sqrt(m_one - a2)
596 this%rlattice_primitive(1, 1) = aa
597 this%rlattice_primitive(2, 1) = m_zero
598 this%rlattice_primitive(3, 1) = cc
599 this%rlattice_primitive(1, 2) = -m_half*aa
600 this%rlattice_primitive(2, 2) = m_half*sqrt(m_three)*aa
601 this%rlattice_primitive(3, 2) = cc
602 this%rlattice_primitive(1, 3) = -m_half*aa
603 this%rlattice_primitive(2, 3) = -m_half*sqrt(m_three)*aa
604 this%rlattice_primitive(3, 3) = cc
605 else
606 this%rlattice_primitive(1, 1) = m_one
607 this%rlattice_primitive(2, 1) = m_zero
608 this%rlattice_primitive(3, 1) = m_zero
609 this%rlattice_primitive(1, 2) = cos(m_pi*angles(3)/180.0_real64)
610 this%rlattice_primitive(2, 2) = sin(m_pi*angles(3)/180.0_real64)
611 this%rlattice_primitive(3, 2) = m_zero
612 this%rlattice_primitive(1, 3) = cos(m_pi*angles(2)/180.0_real64)
613 this%rlattice_primitive(2, 3) = (cos(m_pi*angles(1)/180.0_real64) - &
614 this%rlattice_primitive(1, 2)*this%rlattice_primitive(1, 3))/this%rlattice_primitive(2,2)
615 this%rlattice_primitive(3, 3) = sqrt(m_one - this%rlattice_primitive(1, 3)**2 - this%rlattice_primitive(2, 3)**2)
616 end if
617
618 this%nonorthogonal = any(abs(angles - 90.0_real64) > m_epsilon)
619
621 end subroutine build_metric_from_angles
622
623 !--------------------------------------------------------------
624 subroutine reciprocal_lattice(rv, kv, volume, dim, namespace)
625 real(real64), intent(in) :: rv(:, :)
626 real(real64), intent(out) :: kv(:, :)
627 real(real64), intent(out) :: volume
628 integer, intent(in) :: dim
629 type(namespace_t), optional, intent(in) :: namespace
630
631 integer :: ii
632 real(real64) :: cross(1:3)
634 push_sub(reciprocal_lattice)
635
636 select case (dim)
637 case (3)
638 cross(1:3) = dcross_product(rv(1:3, 2), rv(1:3, 3))
639 volume = dot_product(rv(1:3, 1), cross(1:3))
640
641 kv(1:3, 1) = dcross_product(rv(:, 2), rv(:, 3))/volume
642 kv(1:3, 2) = dcross_product(rv(:, 3), rv(:, 1))/volume
643 kv(1:3, 3) = dcross_product(rv(:, 1), rv(:, 2))/volume
644 case (2)
645 volume = rv(1, 1)*rv(2, 2) - rv(2, 1)*rv(1, 2)
646 kv(1, 1) = rv(2, 2)/volume
647 kv(2, 1) = -rv(1, 2)/volume
648 kv(1, 2) = -rv(2, 1)/volume
649 kv(2, 2) = rv(1, 1)/volume
650 case (1)
651 volume = rv(1, 1)
652 kv(1, 1) = m_one / rv(1, 1)
653 case default ! dim > 3
654 message(1) = "Reciprocal lattice for dim > 3 assumes no periodicity."
655 call messages_warning(1, namespace=namespace)
656 volume = m_one
657 kv(:, :) = m_zero
658 do ii = 1, dim
659 kv(ii, ii) = m_one/rv(ii,ii)
660 ! At least initialize the thing
661 volume = volume * norm2(rv(:, ii))
662 end do
663 end select
664
665 if (volume < m_zero) then
666 message(1) = "Your lattice vectors form a left-handed system."
667 call messages_fatal(1, namespace=namespace)
668 end if
669
670 pop_sub(reciprocal_lattice)
671 end subroutine reciprocal_lattice
672
673 ! ---------------------------------------------------------
674 !! The solution is based on the replies found in
675 !! https://stackoverflow.com/questions/29538135/best-way-to-find-all-points-of-lattice-in-sphere
676 !! https://math.stackexchange.com/questions/1230112/maximizing-a-coordinate-of-xt-at-a-x-leq-r2/1230160#1230160
677 !!
678 !! When the reduced coordinates of a center are given, the iterator uses
679 !! asymmetric, non-zero centered periodic copies to minimize the number of
680 !! replicas, and precomputes the cells that are actually relevant for that
681 !! center. In both cases, %get(ii) returns the Cartesian translation of the ii-th cell
682 function lattice_iterator_constructor(latt, range, center) result(iter)
683 type(lattice_vectors_t), target, intent(in) :: latt
684 real(real64), intent(in) :: range
685 real(real64), optional, intent(in) :: center(:)
686 type(lattice_iterator_t) :: iter
687
688 integer :: ii, jj, idir, n_valid
689 integer :: n_size(latt%space%periodic_dim)
690 real(real64) :: temp(latt%space%dim)
691 real(real64), allocatable :: delta(:)
692 integer, allocatable :: n_count(:), icell_tmp(:, :)
693 integer :: icell(latt%space%dim), n_min, n_max
694 logical :: valid
695
697
698 iter%latt => latt
699
700 ! Determine number of cells
701 iter%n_cells = 1
702
703 if (present(center)) then
704 ! Allow for asymmetric, non-zero centered periodic copies, to minimize the number of replicas
705 safe_allocate(delta(1:latt%space%dim))
706 safe_allocate(n_count(1:latt%space%dim))
707
708 delta(:) = m_zero
709 n_count(:) = 1
710 do idir = 1, latt%space%periodic_dim
711 temp = range * matmul(transpose(latt%klattice), latt%klattice(:, idir)) &
712 / norm2(latt%klattice(:, idir)) / (m_two * m_pi)
713 delta(idir) = temp(idir)
714 n_count(idir) = floor(m_one + m_two*delta(idir) + eps) + 1
715 iter%n_cells = iter%n_cells*n_count(idir)
716 end do
717
718 ! Precompute the cells that fall inside the exact center-dependent bounds
719 safe_allocate(icell_tmp(1:latt%space%dim, 1:iter%n_cells))
720 n_valid = 0
721 do ii = 1, iter%n_cells
722 icell(:) = 0
723 jj = ii - 1
724 valid = .true.
725 do idir = latt%space%periodic_dim, 1, -1
726 n_min = ceiling(-delta(idir) - center(idir) - eps)
727 n_max = floor(m_one + delta(idir) - center(idir) + eps)
728 icell(idir) = n_min + mod(jj, n_count(idir))
729 if (idir > 1) jj = jj/n_count(idir)
730 if (icell(idir) < n_min .or. icell(idir) > n_max) then
731 valid = .false.
732 exit
733 end if
734 end do
735 if (.not. valid) cycle
736 n_valid = n_valid + 1
737 icell_tmp(:, n_valid) = icell(:)
738 end do
739
740 iter%n_cells = n_valid
741 safe_allocate(iter%icell(1:latt%space%dim, 1:n_valid))
742 iter%icell(:, 1:n_valid) = icell_tmp(:, 1:n_valid)
743
744 safe_deallocate_a(icell_tmp)
745 safe_deallocate_a(delta)
746 safe_deallocate_a(n_count)
747
748 else
749 do idir = 1, latt%space%periodic_dim
750 temp = range * matmul(transpose(latt%klattice),latt%klattice(:, idir)) / norm2(latt%klattice(:, idir)) / (m_two * m_pi)
751 n_size(idir) = ceiling(temp(idir))
752 iter%n_cells = iter%n_cells*(2*n_size(idir) + 1)
753 end do
754
755 ! Indexes of the cells
756 safe_allocate(iter%icell(1:latt%space%dim, 1:iter%n_cells))
757
758 do ii = 1, iter%n_cells
759 jj = ii - 1
760 do idir = latt%space%periodic_dim, 1, -1
761 iter%icell(idir, ii) = mod(jj, 2*n_size(idir) + 1) - n_size(idir)
762 if (idir > 1) jj = jj/(2*n_size(idir) + 1)
763 end do
764 iter%icell(latt%space%periodic_dim + 1:latt%space%dim, ii) = 0
765 end do
766
767 end if
768
771
772 !--------------------------------------------------------------
773 subroutine lattice_iterator_copy(this, source)
774 class(lattice_iterator_t), intent(out) :: this
775 class(lattice_iterator_t), intent(in) :: source
776
778
779 this%n_cells = source%n_cells
780 safe_allocate_source_a(this%icell, source%icell)
781 this%latt => source%latt
782
783 pop_sub(lattice_iterator_copy)
784 end subroutine lattice_iterator_copy
785
788 function lattice_iterator_get(this, ii) result(coord)
789 class(lattice_iterator_t), intent(in) :: this
790 integer, intent(in) :: ii
791 real(real64) :: coord(1:this%latt%space%dim)
792
793 ! No push/pop, this is called too often
794
795 assert(ii <= this%n_cells)
796
797 coord(:) = matmul(this%latt%rlattice(:, :), this%icell(:, ii))
798
799 end function lattice_iterator_get
800
801 ! ---------------------------------------------------------
802 subroutine lattice_iterator_finalize(this)
803 type(lattice_iterator_t), intent(inout) :: this
804
806
807 safe_deallocate_a(this%icell)
808 this%n_cells = 0
809 nullify(this%latt)
810
812 end subroutine lattice_iterator_finalize
813
814end module lattice_vectors_oct_m
815
816!! Local Variables:
817!! mode: f90
818!! coding: utf-8
819!! End:
subroutine info()
Definition: em_resp.F90:1093
double acos(double __x) __attribute__((__nothrow__
double sin(double __x) __attribute__((__nothrow__
double sqrt(double __x) __attribute__((__nothrow__
double cos(double __x) __attribute__((__nothrow__
double floor(double __x) __attribute__((__nothrow__
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_epsilon
Definition: global.F90:216
real(real64) function, dimension(this%space%dim) lattice_vectors_fold_into_cell(this, xx)
pure real(real64) function, dimension(this%space%dim) lattice_vectors_red_to_cart(this, xx_red)
subroutine reciprocal_lattice(rv, kv, volume, dim, namespace)
subroutine lattice_vectors_update(this, rlattice)
Updates the lattice vector object from a new set of Cartesian lattice vectors.
pure real(real64) function, dimension(this%space%dim) lattice_vectors_cart_to_red(this, xx_cart)
character(len=140) function lattice_vectors_short_info(this, unit_length)
subroutine angles_from_rlattice_primitive(rlattice_primitive, alpha, beta, gamma)
subroutine lattice_vectors_scale(this, factor)
type(lattice_vectors_t) function lattice_vectors_constructor_from_input(namespace, space, variable_prefix)
real(real64) function, dimension(1:this%latt%space%dim) lattice_iterator_get(this, ii)
real(real64) function lattice_vectors_max_length(this)
subroutine lattice_vectors_copy(this, source)
subroutine lattice_iterator_copy(this, source)
real(real64), parameter eps
subroutine build_metric_from_angles(this, angles)
subroutine lattice_vectors_write_info(this, namespace)
subroutine lattice_vectors_finalize(this)
type(lattice_vectors_t) function lattice_vectors_constructor_from_rlattice(namespace, space, rlattice)
type(lattice_iterator_t) function lattice_iterator_constructor(latt, range, center)
subroutine lattice_iterator_finalize(this)
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:134
This module defines the unit system, used for input and output.
The following class implements a lattice iterator. It allows one to loop over all cells that are with...
int true(void)