LCOV - code coverage report
Current view: top level - src/pw - pw_unittest.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:e08e82d) Lines: 96.8 % 221 214
Test Date: 2026-10-16 00:55:08 Functions: 100.0 % 8 8

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8            2 : PROGRAM pw_unittest
       9            2 :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      10              :                                               cp_logger_type
      11              :    USE kinds,                           ONLY: dp
      12              :    USE libcp2k,                         ONLY: cp2k_finalize,&
      13              :                                               cp2k_init
      14              :    USE mathconstants,                   ONLY: fourpi,&
      15              :                                               pi
      16              :    USE message_passing,                 ONLY: mp_comm_type,&
      17              :                                               mp_comm_world,&
      18              :                                               mp_para_env_type
      19              :    USE mt_util,                         ONLY: mt2d_kernel_strain
      20              :    USE pw_copy_all,                     ONLY: pw_copy_match
      21              :    USE pw_grid_types,                   ONLY: FULLSPACE,&
      22              :                                               HALFSPACE,&
      23              :                                               pw_grid_type
      24              :    USE pw_grids,                        ONLY: pw_grid_create,&
      25              :                                               pw_grid_release
      26              :    USE pw_methods,                      ONLY: do_accurate_sum,&
      27              :                                               do_standard_sum,&
      28              :                                               pw_integral_ab
      29              :    USE pw_poisson_methods,              ONLY: pw_poisson_set,&
      30              :                                               pw_poisson_solve
      31              :    USE pw_poisson_types,                ONLY: pw_poisson_mt,&
      32              :                                               pw_poisson_type
      33              :    USE pw_pool_types,                   ONLY: pw_pool_create
      34              :    USE pw_types,                        ONLY: pw_c1d_gs_type,&
      35              :                                               pw_r1d_gs_type
      36              :    USE realspace_grid_cube,             ONLY: cube_read_values,&
      37              :                                               cube_value_format,&
      38              :                                               cube_values_format
      39              : 
      40              :    IMPLICIT NONE
      41              : 
      42              :    INTEGER                                            :: mode
      43              :    REAL(dp)                                           :: max_error
      44              :    TYPE(cp_logger_type), POINTER                      :: logger
      45              :    TYPE(mp_para_env_type), POINTER                     :: para_env
      46              : 
      47            2 :    CALL cp2k_init()
      48            2 :    logger => cp_get_default_logger()
      49            2 :    para_env => logger%para_env
      50            2 :    CALL check_cube_values()
      51           20 :    DO mode = 0, 8
      52           20 :       CALL check_copy(mode)
      53              :    END DO
      54            2 :    max_error = 0.0_dp
      55            2 :    CALL check_pw_integral_and_poisson()
      56            2 :    CALL check_mt2d_kernel()
      57            2 :    PRINT '(A,ES16.7)', 'PW integral and MT2D kernel maximum scaled error: ', max_error
      58            2 :    CALL cp2k_finalize()
      59              : 
      60              : CONTAINS
      61              : 
      62              : ! **************************************************************************************************
      63              : !> \brief Preserve three-digit cube exponents and parse adjacent values across z-slices.
      64              : ! **************************************************************************************************
      65            2 :    SUBROUTINE check_cube_values()
      66              :       CHARACTER(LEN=13)                                  :: value
      67              :       CHARACTER(LEN=79)                                  :: values
      68              :       CHARACTER(LEN=92)                                  :: slice_values
      69              :       REAL(KIND=dp), DIMENSION(6)                        :: buffer, reference
      70              :       REAL(KIND=dp), DIMENSION(7)                        :: slice_buffer, slice_reference
      71              : 
      72            2 :       WRITE (value, cube_value_format) 3.33004E-101_dp
      73            2 :       IF (INDEX(value, "E-101") <= 0) THEN
      74            0 :          ERROR STOP "Cube value format must preserve three-digit negative exponents."
      75              :       END IF
      76              : 
      77              :       reference = [0.0_dp, -0.91458E-35_dp, 3.33004E-101_dp, &
      78            2 :                    -1.32487E-103_dp, 1.0_dp, -1.0_dp]
      79            2 :       WRITE (values(1:78), cube_values_format) reference
      80            2 :       values(79:79) = NEW_LINE("C")
      81            2 :       IF (INDEX(values, "E-101") <= 0) THEN
      82            0 :          ERROR STOP "Cube line format must preserve E-101."
      83              :       END IF
      84            2 :       IF (INDEX(values, "E-103") <= 0) THEN
      85            0 :          ERROR STOP "Cube line format must preserve E-103."
      86              :       END IF
      87            2 :       CALL cube_read_values(values, buffer)
      88           14 :       IF (MAXVAL(ABS(buffer - reference)) > 1.0E-12_dp) THEN
      89            0 :          ERROR STOP "Cube reader must parse adjacent explicit-exponent values."
      90              :       END IF
      91              : 
      92           14 :       slice_reference(1:6) = reference
      93            2 :       slice_reference(7) = 0.27183E-123_dp
      94            2 :       WRITE (slice_values(1:78), cube_values_format) slice_reference(1:6)
      95            2 :       slice_values(79:79) = NEW_LINE("C")
      96            2 :       WRITE (slice_values(80:92), cube_value_format) slice_reference(7)
      97            2 :       CALL cube_read_values(slice_values, slice_buffer)
      98           16 :       IF (MAXVAL(ABS(slice_buffer - slice_reference)) > 1.0E-12_dp) THEN
      99            0 :          ERROR STOP "Cube reader must parse multi-line z-slices."
     100              :       END IF
     101            2 :    END SUBROUTINE check_cube_values
     102              : 
     103              : ! **************************************************************************************************
     104              : !> \brief Compare identical, permuted, redistributed, partial and empty PW grids.
     105              : !> \param mode Layout variant; mode two changes only one rank's ordering.
     106              : !> \author Thomas D. Kuehne (tkuehne@cp2k.org)
     107              : ! **************************************************************************************************
     108           18 :    SUBROUTINE check_copy(mode)
     109              :       INTEGER, INTENT(IN)                                :: mode
     110              : 
     111              :       COMPLEX(dp), PARAMETER :: untouched = (-999.0_dp, 17.0_dp)
     112              : 
     113              :       COMPLEX(dp)                                        :: expected
     114              :       INTEGER                                            :: dimensions(1), i, j, nsource, ntarget, &
     115              :                                                             owner, rank
     116              :       TYPE(pw_c1d_gs_type)                               :: destination, source
     117         1854 :       TYPE(pw_grid_type), TARGET                         :: source_grid, target_grid
     118              : 
     119           18 :       rank = para_env%mepos
     120           18 :       nsource = 7
     121           18 :       IF (mode == 6 .OR. (mode >= 7 .AND. rank == 0)) nsource = 0
     122           18 :       owner = rank
     123           18 :       IF (mode == 3 .OR. mode == 8) owner = MOD(rank + 1, para_env%num_pe)
     124           18 :       ntarget = nsource
     125           18 :       IF (mode == 4) ntarget = nsource - 1
     126           18 :       IF (mode == 5) ntarget = nsource + 1
     127           18 :       IF (mode == 8) ntarget = MERGE(0, 7, owner == 0)
     128           64 :       ALLOCATE (source_grid%g_hat(3, nsource), target_grid%g_hat(3, ntarget))
     129           64 :       ALLOCATE (source%array(nsource), destination%array(ntarget))
     130           18 :       source%pw_grid => source_grid
     131           18 :       destination%pw_grid => target_grid
     132           36 :       dimensions = [para_env%num_pe]
     133           18 :       CALL source_grid%para%group%create(para_env, 1, dimensions)
     134           18 :       target_grid%para%group = source_grid%para%group
     135           18 :       source_grid%ngpts_cut_local = nsource
     136           18 :       target_grid%ngpts_cut_local = ntarget
     137          116 :       DO i = 1, nsource
     138          392 :          source_grid%g_hat(:, i) = [i, rank, 0]
     139          116 :          source%array(i) = CMPLX(i + 11*rank, -i - 3*rank, dp)
     140              :       END DO
     141          116 :       DO i = 1, ntarget
     142           98 :          j = i
     143           98 :          IF (mode == 1 .OR. (mode == 2 .AND. rank == 0)) j = ntarget + 1 - i
     144          410 :          target_grid%g_hat(:, i) = [j, owner, 0]
     145              :       END DO
     146          116 :       destination%array(:) = untouched
     147           18 :       CALL pw_copy_match(source, destination)
     148          116 :       DO i = 1, ntarget
     149           98 :          j = target_grid%g_hat(1, i)
     150           98 :          expected = CMPLX(j + 11*owner, -j - 3*owner, dp)
     151           98 :          IF (mode == 5 .AND. j > nsource) expected = untouched
     152          116 :          IF (ABS(destination%array(i) - expected) > 1.e-14_dp) ERROR STOP 'Incorrect PW copy'
     153              :       END DO
     154           18 :       DEALLOCATE (source%array, destination%array)
     155           18 :       CALL source_grid%para%group%free()
     156           36 :    END SUBROUTINE check_copy
     157              : 
     158              : ! **************************************************************************************************
     159              : !> \brief Check PW multiplicity and volume normalization, and auxiliary-density Poisson routes.
     160              : ! **************************************************************************************************
     161            2 :    SUBROUTINE check_pw_integral_and_poisson()
     162              :       REAL(dp), PARAMETER :: step = 5.e-4_dp, &
     163              :          strain_steps(4) = [step, -step, 2.0_dp*step, -2.0_dp*step]
     164              : 
     165              :       COMPLEX(dp)                                        :: a_mode, b_mode
     166              :       INTEGER                                            :: axis, ig, sign_step, span, sumtype, test
     167              :       REAL(dp) :: cell(3, 3), derivative, energy, energy_dv, energy_v, energy_v_dv, expected, &
     168              :          expected_real, kernel, reference, shifted_g(3), strain_energy(4), stress(3, 3), &
     169              :          stress_dv(3, 3), stress_only(3, 3), stress_v(3, 3), stress_v_dv(3, 3)
     170              :       TYPE(mp_comm_type)                                 :: comm
     171           14 :       TYPE(pw_c1d_gs_type)                               :: a, b, gradient(3), gradient_v_dv(3), &
     172              :                                                             potential, potential_v_dv
     173              :       TYPE(pw_grid_type), POINTER                        :: grid
     174              :       TYPE(pw_poisson_type)                              :: poisson
     175              :       TYPE(pw_r1d_gs_type)                               :: a_real, b_real
     176              : 
     177            2 :       CALL comm%set_handle(mp_comm_world%get_handle())
     178              :       ! Nonunit volume and an appreciable truncation factor exp(-pi).
     179            2 :       cell = 0.0_dp
     180            8 :       DO axis = 1, 3
     181            8 :          cell(axis, axis) = 7.0_dp**(1.0_dp/3.0_dp)
     182              :       END DO
     183            2 :       a_mode = CMPLX(0.4_dp, -0.2_dp, dp)
     184            2 :       b_mode = CMPLX(-0.7_dp, 0.9_dp, dp)
     185            2 :       NULLIFY (grid)
     186            6 :       DO span = FULLSPACE, HALFSPACE, -1
     187              :          ! Reciprocal inputs and outputs need no FFT. The production grid still
     188              :          ! supplies the distributed G0 ownership and half/full-space mappings.
     189            4 :          CALL pw_grid_create(grid, comm, cell, grid_span=span, npts=[5, 5, 5], odd=.TRUE., fft_usage=.FALSE.)
     190            4 :          CALL a%create(grid)
     191            4 :          CALL b%create(grid)
     192            4 :          CALL a_real%create(grid)
     193            4 :          CALL b_real%create(grid)
     194            4 :          CALL potential%create(grid)
     195            4 :          CALL potential_v_dv%create(grid)
     196           16 :          DO axis = 1, 3
     197           12 :             CALL gradient(axis)%create(grid)
     198           16 :             CALL gradient_v_dv(axis)%create(grid)
     199              :          END DO
     200            8 :          ALLOCATE (poisson%pw_pools(1))
     201            4 :          CALL pw_pool_create(poisson%pw_pools(1)%pool, grid)
     202            4 :          poisson%pw_level = 1
     203            4 :          poisson%parameters%solver = pw_poisson_mt
     204           16 :          poisson%parameters%periodic = [1, 1, 0]
     205            4 :          poisson%parameters%mt_alpha = 1.0_dp
     206            4 :          CALL pw_poisson_set(poisson, cell_hmat=cell, use_level=1)
     207           12 :          DO test = 1, 2
     208              :             ! Constant G0 only and G0 plus a Hermitian mode pair.
     209          384 :             a%array = 0.0_dp
     210          384 :             b%array = 0.0_dp
     211              :             expected = 0.0_dp
     212              :             expected_real = 0.0_dp
     213            8 :             reference = 0.0_dp
     214            8 :             IF (grid%have_g0) THEN
     215            4 :                a%array(1) = 2.0_dp
     216            4 :                b%array(1) = 3.0_dp
     217              :             END IF
     218            8 :             expected = 6.0_dp
     219            8 :             expected_real = 6.0_dp
     220            8 :             reference = 6.0_dp*mt2d_kernel_reference([0.0_dp, 0.0_dp, 0.0_dp], 3, cell(3, 3))
     221            8 :             IF (test /= 1) THEN
     222          192 :                DO ig = 1, grid%ngpts_cut_local
     223          234 :                   IF (ANY(grid%g_hat(2:3, ig) /= 0)) CYCLE
     224           12 :                   IF (grid%g_hat(1, ig) == 1) THEN
     225            1 :                      a%array(ig) = a_mode
     226            1 :                      b%array(ig) = b_mode
     227            7 :                   ELSE IF (grid%g_hat(1, ig) == -1) THEN
     228            2 :                      a%array(ig) = CONJG(a_mode)
     229            2 :                      b%array(ig) = CONJG(b_mode)
     230              :                   END IF
     231              :                END DO
     232            4 :                expected = expected + 2.0_dp*REAL(CONJG(a_mode)*b_mode, dp)
     233            4 :                expected_real = expected_real + 2.0_dp*REAL(a_mode, dp)*REAL(b_mode, dp)
     234           16 :                kernel = mt2d_kernel_reference([2.0_dp*pi/cell(1, 1), 0.0_dp, 0.0_dp], 3, cell(3, 3))
     235            4 :                reference = reference + 2.0_dp*REAL(CONJG(a_mode)*b_mode, dp)*kernel
     236              :             END IF
     237          384 :             a_real%array = REAL(a%array, dp)
     238          384 :             b_real%array = REAL(b%array, dp)
     239              :             ! G0 must have weight one, the mode pair weight two, and both must
     240              :             ! receive V=7 for an integral. just_sum retains the unscaled oracle.
     241           24 :             DO sumtype = do_accurate_sum, do_standard_sum
     242           16 :                CALL check_error(pw_integral_ab(a, b, sumtype=sumtype), 7.0_dp*expected, 3.e-14_dp)
     243           16 :                CALL check_error(pw_integral_ab(a, b, sumtype=sumtype, just_sum=.TRUE.), expected, 3.e-14_dp)
     244           16 :                CALL check_error(pw_integral_ab(a_real, b_real, sumtype=sumtype), 7.0_dp*expected_real, 3.e-14_dp)
     245              :                CALL check_error(pw_integral_ab(a_real, b_real, sumtype=sumtype, just_sum=.TRUE.), &
     246           16 :                                 expected_real, 3.e-14_dp)
     247           16 :                CALL check_error(pw_integral_ab(a, b_real, sumtype=sumtype), 7.0_dp*expected_real, 3.e-14_dp)
     248           24 :                CALL check_error(pw_integral_ab(a_real, b, sumtype=sumtype), 7.0_dp*expected_real, 3.e-14_dp)
     249              :             END DO
     250            8 :             CALL pw_poisson_solve(poisson, a, energy, h_stress=stress, aux_density=b)
     251            8 :             CALL pw_poisson_solve(poisson, a, energy_dv, dvhartree=gradient, h_stress=stress_dv, aux_density=b)
     252            8 :             CALL pw_poisson_solve(poisson, a, energy_v, potential, h_stress=stress_v, aux_density=b)
     253              :             CALL pw_poisson_solve(poisson, a, energy_v_dv, potential_v_dv, dvhartree=gradient_v_dv, &
     254            8 :                                   h_stress=stress_v_dv, aux_density=b)
     255            8 :             CALL pw_poisson_solve(poisson, a, h_stress=stress_only, aux_density=b)
     256            8 :             CALL check_error(energy, 0.5_dp*7.0_dp*reference, 3.e-13_dp)
     257            8 :             CALL check_error(energy_dv, energy, 3.e-13_dp)
     258            8 :             CALL check_error(energy_v, energy, 3.e-13_dp)
     259            8 :             CALL check_error(energy_v_dv, energy, 3.e-13_dp)
     260          104 :             CALL check_error(MAXVAL(ABS(stress_dv - stress)), 0.0_dp, 3.e-11_dp)
     261          104 :             CALL check_error(MAXVAL(ABS(stress_v - stress)), 0.0_dp, 3.e-11_dp)
     262          104 :             CALL check_error(MAXVAL(ABS(stress_v_dv - stress)), 0.0_dp, 3.e-11_dp)
     263          104 :             CALL check_error(MAXVAL(ABS(stress_only - stress)), 0.0_dp, 3.e-11_dp)
     264            8 :             IF (test == 2) THEN
     265              :                ! Fixed Fourier charges Q=V*rho give E(epsilon)=V/2/(1+epsilon)
     266              :                ! times the strained kernel sum. This absolute production-virial
     267              :                ! oracle includes both G0/volume and the nonzero-mode kernel term.
     268           20 :                DO sign_step = 1, 4
     269           64 :                   shifted_g = [2.0_dp*pi/cell(1, 1)/(1.0_dp + strain_steps(sign_step)), 0.0_dp, 0.0_dp]
     270              :                   reference = 6.0_dp*mt2d_kernel_reference([0.0_dp, 0.0_dp, 0.0_dp], 3, cell(3, 3)) &
     271           16 :                               + 2.0_dp*REAL(CONJG(a_mode)*b_mode, dp)*mt2d_kernel_reference(shifted_g, 3, cell(3, 3))
     272           20 :                   strain_energy(sign_step) = 0.5_dp*7.0_dp*reference/(1.0_dp + strain_steps(sign_step))
     273              :                END DO
     274              :                derivative = (8.0_dp*(strain_energy(1) - strain_energy(2)) &
     275            4 :                              - strain_energy(3) + strain_energy(4))/(12.0_dp*step)
     276            4 :                CALL check_error(stress(1, 1) + derivative, 0.0_dp, 3.e-9_dp)
     277              :             END IF
     278          404 :             DO ig = 1, grid%ngpts_cut_local
     279          376 :                kernel = mt2d_kernel_reference(grid%g(:, ig), 3, cell(3, 3))
     280          376 :                CALL check_error(ABS(potential%array(ig) - kernel*a%array(ig)), 0.0_dp, 3.e-13_dp)
     281          376 :                CALL check_error(ABS(potential_v_dv%array(ig) - kernel*a%array(ig)), 0.0_dp, 3.e-13_dp)
     282         1512 :                DO axis = 1, 3
     283              :                   CALL check_error(ABS(gradient(axis)%array(ig) &
     284         1128 :                                        - CMPLX(0.0_dp, grid%g(axis, ig), dp)*kernel*a%array(ig)), 0.0_dp, 3.e-13_dp)
     285              :                   CALL check_error(ABS(gradient_v_dv(axis)%array(ig) &
     286         1504 :                                        - CMPLX(0.0_dp, grid%g(axis, ig), dp)*kernel*a%array(ig)), 0.0_dp, 3.e-13_dp)
     287              :                END DO
     288              :             END DO
     289              :          END DO
     290            4 :          CALL poisson%release()
     291            4 :          CALL a%release()
     292            4 :          CALL b%release()
     293            4 :          CALL a_real%release()
     294            4 :          CALL b_real%release()
     295            4 :          CALL potential%release()
     296            4 :          CALL potential_v_dv%release()
     297           16 :          DO axis = 1, 3
     298           12 :             CALL gradient(axis)%release()
     299           16 :             CALL gradient_v_dv(axis)%release()
     300              :          END DO
     301            6 :          CALL pw_grid_release(grid)
     302              :       END DO
     303           34 :    END SUBROUTINE check_pw_integral_and_poisson
     304              : 
     305              : ! **************************************************************************************************
     306              : !> \brief Compare the MT2D kernel and in-plane virial with independent strained-cell energies.
     307              : ! **************************************************************************************************
     308            2 :    SUBROUTINE check_mt2d_kernel()
     309              :       REAL(dp), PARAMETER :: step = 3.e-4_dp, shifts(4) = [step, -step, 2.0_dp*step, -2.0_dp*step]
     310              : 
     311              :       INTEGER                                            :: alpha, beta, ip, iz, jp, miller(3), &
     312              :                                                             mode, modes(3, 5), sign_step
     313              :       REAL(dp)                                           :: derivative, g(3), h_inv(3, 3), kernel, &
     314              :                                                             lengths(3), shifted_g(3), &
     315              :                                                             shifted_kernel(4), strain(3, 3)
     316              : 
     317              :       ! Rotate the Cartesian vacuum axis. A shear within the periodic plane is
     318              :       ! permitted; only normal/periodic mixing is excluded by the MT2D kernel.
     319            8 :       DO iz = 1, 3
     320            6 :          ip = MOD(iz, 3) + 1
     321            6 :          jp = MOD(iz + 1, 3) + 1
     322            6 :          lengths = [9.0_dp, 13.0_dp, 17.0_dp]
     323            6 :          h_inv = 0.0_dp
     324           24 :          DO alpha = 1, 3
     325           24 :             h_inv(alpha, alpha) = 1.0_dp/lengths(alpha)
     326              :          END DO
     327            6 :          h_inv(ip, jp) = -1.4_dp/(lengths(ip)*lengths(jp))
     328            6 :          modes = 0
     329              :          ! G=0, both odd and even normal-only modes, and several modes with
     330              :          ! appreciable exponential truncation reject the periodic K**2 formula.
     331           18 :          modes(iz, 2:3) = [1, 2]
     332            6 :          modes(ip, 4) = 1
     333           24 :          modes(:, 5) = [1, 2, 1]
     334           38 :          DO mode = 1, SIZE(modes, 2)
     335          120 :             miller = modes(:, mode)
     336          240 :             g = 2.0_dp*pi*MATMUL(TRANSPOSE(h_inv), REAL(miller, dp))
     337           30 :             CALL mt2d_kernel_strain(g, iz, lengths(iz), kernel, strain)
     338           30 :             CALL check_error(kernel, mt2d_kernel_reference(g, iz, lengths(iz)), 3.e-14_dp)
     339          120 :             CALL check_error(MAXVAL(ABS(strain(iz, :))), 0.0_dp, 0.0_dp)
     340          120 :             CALL check_error(MAXVAL(ABS(strain(:, iz))), 0.0_dp, 0.0_dp)
     341          246 :             IF (mode <= 3) CALL check_error(MAXVAL(ABS(strain)), 0.0_dp, 0.0_dp)
     342          156 :             DO alpha = 1, 3
     343           90 :                IF (alpha == iz) CYCLE
     344          270 :                DO beta = 1, 3
     345          180 :                   IF (beta == iz) CYCLE
     346          600 :                   DO sign_step = 1, 4
     347              :                      ! g'=(I+epsilon_ab)^(-T) g, evaluated independently of dK.
     348          480 :                      shifted_g = g
     349          480 :                      IF (alpha == beta) THEN
     350          240 :                         shifted_g(beta) = g(beta)/(1.0_dp + shifts(sign_step))
     351              :                      ELSE
     352          240 :                         shifted_g(beta) = g(beta) - shifts(sign_step)*g(alpha)
     353              :                      END IF
     354          600 :                      shifted_kernel(sign_step) = mt2d_kernel_reference(shifted_g, iz, lengths(iz))
     355              :                   END DO
     356              :                   derivative = (8.0_dp*(shifted_kernel(1) - shifted_kernel(2)) &
     357          120 :                                 - shifted_kernel(3) + shifted_kernel(4))/(12.0_dp*step)
     358          270 :                   CALL check_error(strain(alpha, beta), derivative, 3.e-9_dp)
     359              :                END DO
     360              :             END DO
     361              :          END DO
     362              :       END DO
     363            2 :    END SUBROUTINE check_mt2d_kernel
     364              : 
     365              : ! **************************************************************************************************
     366              : !> \brief Evaluate the original MT2D influence kernel independently of its strain derivative.
     367              : !> \param g ...
     368              : !> \param iz ...
     369              : !> \param slab_size ...
     370              : !> \return ...
     371              : ! **************************************************************************************************
     372          930 :    FUNCTION mt2d_kernel_reference(g, iz, slab_size) RESULT(kernel)
     373              :       REAL(dp), INTENT(IN)                               :: g(3)
     374              :       INTEGER, INTENT(IN)                                :: iz
     375              :       REAL(dp), INTENT(IN)                               :: slab_size
     376              :       REAL(dp)                                           :: kernel
     377              : 
     378              :       REAL(dp)                                           :: g2, g_parallel(3), radius
     379              : 
     380         3720 :       g2 = SUM(g**2)
     381          930 :       IF (g2 == 0.0_dp) THEN
     382          130 :          kernel = pi*slab_size**2/2.0_dp
     383              :       ELSE
     384          800 :          g_parallel = g
     385          800 :          g_parallel(iz) = 0.0_dp
     386          800 :          radius = slab_size/2.0_dp
     387         3200 :          kernel = fourpi/g2 - fourpi/g2*COS(g(iz)*radius)*EXP(-NORM2(g_parallel)*radius)
     388              :       END IF
     389          930 :    END FUNCTION mt2d_kernel_reference
     390              : 
     391              : ! **************************************************************************************************
     392              : !> \brief Require the scaled error to be finite and within the supplied tolerance.
     393              : !> \param value ...
     394              : !> \param reference ...
     395              : !> \param tolerance ...
     396              : ! **************************************************************************************************
     397         3400 :    SUBROUTINE check_error(value, reference, tolerance)
     398              :       REAL(dp), INTENT(IN)                               :: value, reference, tolerance
     399              : 
     400              :       REAL(dp)                                           :: error
     401              : 
     402         3400 :       error = ABS(value - reference)/MAX(1.0_dp, ABS(reference))
     403         3400 :       max_error = MAX(max_error, error)
     404         3400 :       IF (.NOT. (error <= tolerance)) THEN
     405            0 :          PRINT *, 'Coulomb check: value, reference, error, tolerance:', value, reference, error, tolerance
     406            0 :          ERROR STOP 'PW integral or MT2D kernel check failed'
     407              :       END IF
     408         3400 :    END SUBROUTINE check_error
     409              : 
     410              : END PROGRAM pw_unittest
        

Generated by: LCOV version 2.0-1