Octopus
stencil_stargeneral.F90
Go to the documentation of this file.
1!! Copyright (C) 2016 U. De Giovannini, H Huebener; 2019 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
28 use debug_oct_m
29 use global_oct_m
30 use, intrinsic :: iso_fortran_env
31 use math_oct_m
35 use sort_oct_m
36
37 implicit none
38
39 private
40 public :: &
46
47
48
49contains
50
51 ! ---------------------------------------------------------
54 subroutine stencil_stargeneral_get_arms(this, dim, coord_system)
55 type(stencil_t), intent(inout) :: this
56 integer, intent(in) :: dim
57 class(coordinate_system_t), intent(in) :: coord_system
58
59 real(real64) :: theta(dim), basis_vectors(dim, dim)
60 integer :: arm(dim)
61 integer :: ii, jj, kk
62 real(real64) :: norm
63
64 integer, allocatable :: cand_vecs(:,:), idx(:)
65 real(real64), allocatable :: cand_norms(:)
66 integer :: n_cand, max_cand
67 logical :: active(3)
68 integer :: n_found
69 integer :: det
70 integer :: v1_xy, v1_xz, v1_yz
71 integer :: v2_xy, v2_xz, v2_yz
72 integer :: v3_xy, v3_xz, v3_yz
73 integer :: i1, i2, i3, i, j, k
74
76
77 assert(dim <= 3)
78
79 this%stargeneral%narms = 0
80 this%stargeneral%arms = 0
81
82 if (dim == 1) then
83 !we are done
85 return
86 end if
87
88 select type (coord_system)
89 class is (affine_coordinates_t)
90 basis_vectors = coord_system%basis%vectors
91 class default
92 message(1) = "Stencil stargeneral can currently only be used with affine coordinate systems."
93 call messages_fatal(1)
94 end select
95
96 if (dim == 2) then
97 !get the angle between the primitive vectors
98 theta(1) = acos(dot_product(basis_vectors(:, 1), basis_vectors(:, 2)))
99
100 if (theta(1) < m_pi*m_half) then
101 this%stargeneral%narms = this%stargeneral%narms + 1
102 arm = (/1,-1/)
103 this%stargeneral%arms(this%stargeneral%narms, 1:dim) = arm
104 else if (theta(1) > m_pi*m_half) then
105 this%stargeneral%narms = this%stargeneral%narms + 1
106 arm = (/1,+1/)
107 this%stargeneral%arms(this%stargeneral%narms, 1:dim) = arm
108 end if
109 !if theta == pi/2 we do not need additional arms
110
112 return
113 end if
114
115 ! dim>2
116
117 !We first count how many arms we need
118 theta(1) = acos(dot_product(basis_vectors(:, 1), basis_vectors(:, 2)))
119 if (abs(theta(1) - m_pi*m_half) > 1e-8_real64) this%stargeneral%narms = this%stargeneral%narms + 1
121 theta(2) = acos(dot_product(basis_vectors(:, 2), basis_vectors(:, 3)))
122 if (abs(theta(2)-m_pi*m_half) > 1e-8_real64) this%stargeneral%narms = this%stargeneral%narms + 1
123
124 theta(3) = acos(dot_product(basis_vectors(:, 3), basis_vectors(:, 1)))
125 if (abs(theta(3) - m_pi*m_half) > 1e-8_real64) this%stargeneral%narms = this%stargeneral%narms + 1
126
127
128 !Cubic cell, no extra arms
129 if (this%stargeneral%narms == 0) then
131 return
132 end if
133
134 ! Determine active off-diagonal components
135 ! Mapping:
136 ! active(1): xy (theta 12) -> theta(1)
137 ! active(2): xz (theta 13) -> theta(3)
138 ! active(3): yz (theta 23) -> theta(2)
139 active = .false.
140 if (abs(theta(1) - m_pi*m_half) > 1e-8_real64) active(1) = .true.
141 if (abs(theta(3) - m_pi*m_half) > 1e-8_real64) active(2) = .true.
142 if (abs(theta(2) - m_pi*m_half) > 1e-8_real64) active(3) = .true.
143
144 ! Generate candidates in range [-2, 2]
145 max_cand = 5**3
146 safe_allocate(cand_vecs(3, max_cand))
147 safe_allocate(cand_norms(max_cand))
148 safe_allocate(idx(max_cand))
149 n_cand = 0
150
151 do ii= -2,2
152 do jj = -2,2
153 do kk = -2,2
154 ! Skip origin
155 if (ii == 0 .and. jj == 0 .and. kk == 0) cycle
156
157 ! Skip axes
158 if (ii /= 0 .and. jj == 0 .and. kk == 0) cycle
159 if (ii == 0 .and. jj /= 0 .and. kk == 0) cycle
160 if (ii == 0 .and. jj == 0 .and. kk /= 0) cycle
161
162 ! Skip orthogonal planes if angle is 90
163 if (.not. active(1) .and. kk == 0) cycle ! xy plane, but x-y orthogonal
164 if (.not. active(3) .and. ii == 0) cycle ! yz plane, but y-z orthogonal
165 if (.not. active(2) .and. jj == 0) cycle ! xz plane, but x-z orthogonal
166
167 ! We only store one vector from the pair (v, -v) as they define the same line.
168 ! To ensure uniqueness, we enforce that the first non-zero component is negative.
169 if (ii > 0) cycle
170 if (ii == 0 .and. jj > 0) cycle
171 if (ii == 0 .and. jj == 0 .and. kk > 0) cycle
172
173 norm = sum((ii*basis_vectors(:, 1) + jj*basis_vectors(:, 2) + kk*basis_vectors(:, 3))**2)
174
175 n_cand = n_cand + 1
176 cand_vecs(1, n_cand) = ii
177 cand_vecs(2, n_cand) = jj
178 cand_vecs(3, n_cand) = kk
179 cand_norms(n_cand) = norm
180 end do
181 end do
182 end do
183
184 ! Sort candidates by norm
185 call robust_sort_by_abs(cand_norms(1:n_cand), cand_vecs(:, 1:n_cand), idx(1:n_cand))
186
187 ! Select narms vectors
188 n_found = 0
189
190 ! We iterate through sorted candidates
191 ! Case 1: narms = 1
192 ! This is a 3D system with two orthogonal directions.
193 ! We only need to have a non-zero component along the non-orthogonal direction.
194 if (this%stargeneral%narms == 1) then
195 do i = 1, n_cand
196 i1 = idx(i)
197 ! Check if this vector has non-zero component for the active direction
198 if (active(1) .and. cand_vecs(1,i1)*cand_vecs(2,i1) /= 0) n_found = 1
199 if (active(2) .and. cand_vecs(1,i1)*cand_vecs(3,i1) /= 0) n_found = 1
200 if (active(3) .and. cand_vecs(2,i1)*cand_vecs(3,i1) /= 0) n_found = 1
201
202 if (n_found == 1) then
203 this%stargeneral%arms(1, 1:3) = cand_vecs(:, i1)
204 exit
205 end if
206 end do
207 end if
208
209 ! Case 2: narms = 2
210 ! This corresponds to a 3D system, with one orthogonal direction.
211 ! We just need to pick two linearly independent vectors.
212 if (this%stargeneral%narms == 2) then
213 do i = 1, n_cand
214 i1 = idx(i)
215 do j = i + 1, n_cand
216 i2 = idx(j)
217
218 ! Check linear independence (cross product non-zero)
219 if ( (cand_vecs(2, i1) * cand_vecs(3, i2) - cand_vecs(3, i1) * cand_vecs(2, i2)) /= 0 .or. &
220 (cand_vecs(3, i1) * cand_vecs(1, i2) - cand_vecs(1, i1) * cand_vecs(3, i2)) /= 0 .or. &
221 (cand_vecs(1, i1) * cand_vecs(2, i2) - cand_vecs(2, i1) * cand_vecs(1, i2)) /= 0 ) then
222
223 this%stargeneral%arms(1, 1:3) = cand_vecs(:, i1)
224 this%stargeneral%arms(2, 1:3) = cand_vecs(:, i2)
225 n_found = 2
226 exit
227 end if
228 end do
229 if (n_found == 2) exit
230 end do
231 end if
232
233 ! Case 3: narms = 3 (Triclinic)
234 if (this%stargeneral%narms == 3) then
235 do i = 1, n_cand
236 i1 = idx(i)
237 do j = i + 1, n_cand
238 i2 = idx(j)
239 do k = j + 1, n_cand
240 i3 = idx(k)
241
242 ! We check if the off-diagonal terms of the outer product of the vectors
243 ! form a linearly independent set. This is required for the matrix of the
244 ! linear system to determine the weights to be non-singular.
245 ! See Natan et al., PRB 78, 075109 (2008), eq. 19.
246 ! The matrix is M_ij = (n_l)_i * (n_l)_j, where n_l are the arm vectors.
247 ! The off-diagonal terms are xy, xz, yz.
248 ! We form the 3x3 matrix where each row corresponds to one arm vector,
249 ! and the columns correspond to the xy, xz, yz components.
250
251 v1_xy = cand_vecs(1, i1) * cand_vecs(2, i1)
252 v1_xz = cand_vecs(1, i1) * cand_vecs(3, i1)
253 v1_yz = cand_vecs(2, i1) * cand_vecs(3, i1)
254
255 v2_xy = cand_vecs(1, i2) * cand_vecs(2, i2)
256 v2_xz = cand_vecs(1, i2) * cand_vecs(3, i2)
257 v2_yz = cand_vecs(2, i2) * cand_vecs(3, i2)
258
259 v3_xy = cand_vecs(1, i3) * cand_vecs(2, i3)
260 v3_xz = cand_vecs(1, i3) * cand_vecs(3, i3)
261 v3_yz = cand_vecs(2, i3) * cand_vecs(3, i3)
262
263 det = v1_xy * ( v2_xz * v3_yz - v3_xz * v2_yz ) &
264 - v1_xz * ( v2_xy * v3_yz - v3_xy * v2_yz ) &
265 + v1_yz * ( v2_xy * v3_xz - v3_xy * v2_xz )
266
267 if (det /= 0) then
268 this%stargeneral%arms(1, 1:3) = cand_vecs(:, i1)
269 this%stargeneral%arms(2, 1:3) = cand_vecs(:, i2)
270 this%stargeneral%arms(3, 1:3) = cand_vecs(:, i3)
271 n_found = 3
272 exit
273 end if
274 end do
275 if (n_found == 3) exit
276 end do
277 if (n_found == 3) exit
278 end do
279 end if
280
281 if (n_found < this%stargeneral%narms) then
282 write(message(1), '(a, i0, a, i0)') 'Stencil stargeneral could not find enough independent arms. Found ', &
283 n_found, ' needed ', this%stargeneral%narms
284 call messages_fatal(1)
285 end if
286
287 safe_deallocate_a(cand_vecs)
288 safe_deallocate_a(cand_norms)
289 safe_deallocate_a(idx)
290
292 end subroutine stencil_stargeneral_get_arms
293
294
295 ! ---------------------------------------------------------
296 integer function stencil_stargeneral_size_lapl(this, dim, order) result(n)
297 type(stencil_t), intent(inout) :: this
298 integer, intent(in) :: dim
299 integer, intent(in) :: order
300
302
303 !normal star
304 n = 2*dim*order + 1
305
306 ! star general
307 n = n + 2 * order * this%stargeneral%narms
308
309
312
313
314 ! ---------------------------------------------------------
317 integer function stencil_stargeneral_extent(dir, order)
318 integer, intent(in) :: dir
319 integer, intent(in) :: order
320
321 integer :: extent
322
324
325 extent = 0
326 if (dir >= 1 .and. dir <= 3) then
327 if (order <= 2) then
328 extent = 2
329 else
330 extent = order
331 end if
332 end if
334
336 end function stencil_stargeneral_extent
337
338
339
340 ! ---------------------------------------------------------
341 subroutine stencil_stargeneral_get_lapl(this, dim, order)
342 type(stencil_t), intent(inout) :: this
343 integer, intent(in) :: dim
344 integer, intent(in) :: order
345
346 integer :: i, j, n
347 logical :: got_center
348
350
351 call stencil_allocate(this, dim, stencil_stargeneral_size_lapl(this, dim, order))
352
353 n = 1
354 select case (dim)
355 case (1)
356 n = 1
357 do i = 1, dim
358 do j = -order, order
359 if (j == 0) cycle
360 n = n + 1
361 this%points(i, n) = j
362 end do
363 end do
364 case (2)
365 n = 1
366 do i = 1, dim
367 do j = -order, order
368 if (j == 0) cycle
369 n = n + 1
370 this%points(i, n) = j
371 end do
372 end do
373
374 do j = -order, order
375 if (j == 0) cycle
376 do i = 1, this%stargeneral%narms
377 n = n + 1
378 this%points(1:2, n) = this%stargeneral%arms(i, 1:2)*j
379 end do
380 end do
381
382 case (3)
383 got_center = .false.
384
385 n = 0
386 do i = 1, dim
387 do j = -order, order
388
389 ! count center only once
390 if (j == 0) then
391 if (got_center) then
392 cycle
393 else
394 got_center = .true.
395 end if
396
397 end if
398 n = n + 1
399 this%points(i, n) = j
400 end do
401 end do
402
403 do j = -order, order
404 if (j == 0) cycle
405 do i = 1, this%stargeneral%narms
406 n = n + 1
407 this%points(1:3, n) = this%stargeneral%arms(i, 1:3)*j
408 end do
409 end do
410
411 end select
413 call stencil_init_center(this)
414
416 end subroutine stencil_stargeneral_get_lapl
417
418
419
420
421 ! ---------------------------------------------------------
422 subroutine stencil_stargeneral_pol_lapl(this, dim, order, pol)
423 type(stencil_t), intent(in) :: this
424 integer, intent(in) :: dim
425 integer, intent(in) :: order
426 integer, intent(out) :: pol(:,:)
427
428 integer :: i, j, n, j1
429 logical :: zeros_treated(dim)
430
432
433 n = 1
434 select case (dim)
435 case (1)
436 n = 1
437 pol(:,:) = 0
438 do i = 1, dim
439 do j = 1, 2*order
440 n = n + 1
441 pol(i, n) = j
442 end do
443 end do
444 case (2)
445 n = 1
446 pol(:,:) = 0
447 do i = 1, dim
448 do j = 1, 2*order
449 n = n + 1
450 pol(i, n) = j
451 end do
452 end do
453
454 do j = 1, 2*order
455 do i = 1, this%stargeneral%narms
456 n = n + 1
457 if (sum(this%stargeneral%arms(i,1:dim)) == 0) then
458 pol(1:2, n) = (/j,1/)
459 else
460 pol(1:2, n) = (/1,j/)
461 end if
462 end do
463 end do
464
465 case (3)
466 n = 1
467 pol(:,:) = 0
468 do i = 1, dim
469 do j = 1, 2*order
470 n = n + 1
471 pol(i, n) = j
472 end do
473 end do
474
475 ! first, treat arms with zeros: they should have polynomials with zeros in the
476 ! corresponding dimension
477 zeros_treated = .false.
478 do i = 1, this%stargeneral%narms
479 if (this%stargeneral%arms(i, 1) == 0) then
480 do j1 = 1, 2*order
481 n = n + 1
482 pol(1:3, n) = (/0, 1, j1/)
483 end do
484 zeros_treated(1) = .true.
485 else if (this%stargeneral%arms(i, 2) == 0) then
486 do j1 = 1, 2*order
487 n = n + 1
488 pol(1:3, n) = (/j1, 0, 1/)
489 end do
490 zeros_treated(2) = .true.
491 else if (this%stargeneral%arms(i, 3) == 0) then
492 do j1 = 1, 2*order
493 n = n + 1
494 pol(1:3, n) = (/1, j1, 0/)
495 end do
496 zeros_treated(3) = .true.
497 end if
498 end do ! end loop on number of arms
499 ! then treat the remaining arms, use the polynomials that have not been used yet
500 do i = 1, this%stargeneral%narms - count(zeros_treated)
501 if (.not. zeros_treated(1)) then
502 do j1 = 1, 2*order
503 n = n + 1
504 pol(1:3, n) = (/0, 1, j1/)
505 end do
506 zeros_treated(1) = .true.
507 else if (.not. zeros_treated(2)) then
508 do j1 = 1, 2*order
509 n = n + 1
510 pol(1:3, n) = (/j1, 0, 1/)
511 end do
512 zeros_treated(2) = .true.
513 else if (.not. zeros_treated(3)) then
514 do j1 = 1, 2*order
515 n = n + 1
516 pol(1:3, n) = (/1, j1, 0/)
517 end do
518 zeros_treated(3) = .true.
519 end if
520 end do ! end loop on number of arms
521 end select
522
524 end subroutine stencil_stargeneral_pol_lapl
525
526
528
529!! Local Variables:
530!! mode: f90
531!! coding: utf-8
532!! End:
double acos(double __x) __attribute__((__nothrow__
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:198
real(real64), parameter, public m_half
Definition: global.F90:206
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
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
This module is intended to contain "only mathematical" functions and procedures.
Definition: sort.F90:119
This module defines stencils used in Octopus.
Definition: stencil.F90:137
subroutine, public stencil_allocate(this, dim, size)
Definition: stencil.F90:180
subroutine, public stencil_init_center(this)
Definition: stencil.F90:277
This module defines routines, generating operators for a generalized star stencil.
subroutine, public stencil_stargeneral_get_arms(this, dim, coord_system)
Finds the direction of the arms of the star-general stencil as described in Natan et al....
subroutine, public stencil_stargeneral_pol_lapl(this, dim, order, pol)
integer function, public stencil_stargeneral_extent(dir, order)
Returns maximum extension of the stencil in spatial direction dir = 1, 2, 3 for a given discretizatio...
subroutine, public stencil_stargeneral_get_lapl(this, dim, order)
integer function, public stencil_stargeneral_size_lapl(this, dim, order)
The class representing the stencil, which is used for non-local mesh operations.
Definition: stencil.F90:165
int true(void)