LCOV - code coverage report
Current view: top level - src - wannier90_projections_unittest.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 98.4 % 124 122
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 2 2

            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              : ! **************************************************************************************************
       9              : !> \brief Test mesh-wide rank validation and unitary covariance of AO trial selection.
      10              : !> \author Thomas D. Kuehne, tkuehne@cp2k.org
      11              : ! **************************************************************************************************
      12            6 : PROGRAM wannier90_projections_unittest
      13              : #if defined (__HAS_IEEE_EXCEPTIONS)
      14              :    USE ieee_exceptions, ONLY: IEEE_ALL, IEEE_DIVIDE_BY_ZERO, IEEE_INVALID, IEEE_OVERFLOW, &
      15              :                               ieee_get_halting_mode, ieee_set_halting_mode, ieee_support_halting
      16              : #endif
      17            2 :    USE kinds, ONLY: dp
      18              :    USE message_passing, ONLY: mp_comm_type, &
      19              :                               mp_para_env_release, &
      20              :                               mp_para_env_type, &
      21              :                               mp_world_finalize, &
      22              :                               mp_world_init
      23              :    USE wannier90_projections, ONLY: wannier90_hybrid_candidates, &
      24              :                                     wannier90_window_mask, &
      25              :                                     wannier90_projection_rank, &
      26              :                                     wannier90_select_projections
      27              : #include "./base/base_uses.f90"
      28              : 
      29              :    IMPLICIT NONE
      30              : 
      31            2 :    COMPLEX(KIND=dp), ALLOCATABLE :: candidates(:, :, :)
      32            2 :    COMPLEX(KIND=dp), ALLOCATABLE :: hybrids(:, :, :), rotated_hybrids(:, :, :)
      33              :    COMPLEX(KIND=dp) :: quartet(2, 4)
      34              :    COMPLEX(KIND=dp) :: amn(2, 2, 2), reference(2, 2, 2), rotation(2, 2, 2)
      35              :    COMPLEX(KIND=dp) :: repaired(2, 2, 3)
      36              :    COMPLEX(KIND=dp) :: mixed_reference(4, 3, 2)
      37              :    LOGICAL :: allowed(4)
      38              :    INTEGER :: anchor, ik, nfail, pivots(2), reference_pivots(2)
      39              :    REAL(KIND=dp) :: angle, min_svalue
      40              :    TYPE(mp_comm_type) :: comm
      41              :    TYPE(mp_para_env_type), POINTER :: para_env
      42              : #if defined (__HAS_IEEE_EXCEPTIONS)
      43              :    LOGICAL :: saved_halt(SIZE(IEEE_ALL)), expected_halt(SIZE(IEEE_ALL)), actual_halt(SIZE(IEEE_ALL))
      44              : #endif
      45              : 
      46            2 :    CALL mp_world_init(comm)
      47            2 :    ALLOCATE (para_env)
      48            2 :    CALL para_env%from_dup(comm)
      49            2 :    nfail = 0
      50              : #if defined (__HAS_IEEE_EXCEPTIONS)
      51              :    CALL ieee_get_halting_mode(IEEE_ALL, saved_halt)
      52              :    IF (ieee_support_halting(IEEE_INVALID)) CALL ieee_set_halting_mode(IEEE_INVALID, .TRUE.)
      53              :    IF (ieee_support_halting(IEEE_DIVIDE_BY_ZERO)) CALL ieee_set_halting_mode(IEEE_DIVIDE_BY_ZERO, .TRUE.)
      54              :    IF (ieee_support_halting(IEEE_OVERFLOW)) CALL ieee_set_halting_mode(IEEE_OVERFLOW, .TRUE.)
      55              :    CALL ieee_get_halting_mode(IEEE_ALL, expected_halt)
      56              : #endif
      57            2 :    IF (para_env%is_source()) THEN
      58            1 :       ALLOCATE (candidates(2, 3, 2))
      59            1 :       candidates = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
      60              :       ! The strongest stacked column vanishes at the second k-point.
      61            1 :       candidates(1, 1, 1) = 5.0_dp
      62            3 :       candidates(2, 2, :) = 2.0_dp
      63            3 :       candidates(1, 3, :) = 0.4_dp
      64              :    ELSE
      65            1 :       ALLOCATE (candidates(0, 0, 0))
      66              :    END IF
      67            2 :    CALL wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue, anchor_kpoint=0)
      68            2 :    IF (min_svalue > 1.0e-14_dp) nfail = nfail + 1
      69            2 :    CALL wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue)
      70            2 :    IF (ABS(min_svalue - 0.4_dp) > 1.0e-14_dp) nfail = nfail + 1
      71            6 :    IF (.NOT. ALL(pivots == [2, 3])) nfail = nfail + 1
      72            2 :    reference = amn
      73            2 :    reference_pivots = pivots
      74              : 
      75            6 :    DO ik = 1, 2
      76            4 :       angle = 0.31_dp*REAL(ik, KIND=dp)
      77            4 :       rotation(1, 1, ik) = COS(angle)
      78            4 :       rotation(2, 2, ik) = COS(angle)
      79            4 :       rotation(1, 2, ik) = CMPLX(0.0_dp, SIN(angle), KIND=dp)
      80            4 :       rotation(2, 1, ik) = CMPLX(0.0_dp, SIN(angle), KIND=dp)
      81          108 :       IF (para_env%is_source()) candidates(:, :, ik) = MATMUL(rotation(:, :, ik), candidates(:, :, ik))
      82              :    END DO
      83            2 :    CALL wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue)
      84            2 :    IF (ABS(min_svalue - 0.4_dp) > 1.0e-14_dp) nfail = nfail + 1
      85            6 :    IF (.NOT. ALL(pivots == reference_pivots)) nfail = nfail + 1
      86            6 :    DO ik = 1, 2
      87           86 :       IF (MAXVAL(ABS(amn(:, :, ik) - MATMUL(rotation(:, :, ik), reference(:, :, ik)))) > 1.0e-14_dp) THEN
      88            0 :          nfail = nfail + 1
      89              :       END IF
      90              :    END DO
      91            2 :    IF (para_env%is_source()) candidates(:, :, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
      92            2 :    CALL wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue)
      93            2 :    IF (min_svalue /= 0.0_dp) nfail = nfail + 1
      94            2 :    DEALLOCATE (candidates)
      95            2 :    IF (para_env%is_source()) THEN
      96            1 :       ALLOCATE (candidates(2, 5, 2))
      97            3 :       DO ik = 1, 2
      98           13 :          DO anchor = 1, 5
      99           10 :             candidates(1, anchor, ik) = CMPLX(0.1_dp*anchor, 0.2_dp*ik, KIND=dp)
     100           12 :             candidates(2, anchor, ik) = CMPLX(-0.3_dp*anchor, 0.05_dp*ik*anchor, KIND=dp)
     101              :          END DO
     102              :       END DO
     103              :    ELSE
     104            1 :       ALLOCATE (candidates(0, 0, 0))
     105              :    END IF
     106            2 :    CALL wannier90_hybrid_candidates(candidates, RESHAPE([1, 2, 3, 4], [4, 1]), hybrids)
     107            2 :    IF (para_env%is_source()) THEN
     108            4 :       IF (.NOT. ALL(SHAPE(hybrids) == [2, 5, 2])) nfail = nfail + 1
     109            7 :       IF (MAXVAL(ABS(hybrids(:, 1, :) - candidates(:, 5, :))) > 1.0e-14_dp) nfail = nfail + 1
     110            3 :       DO ik = 1, 2
     111              :          ! A full tetrahedral quartet is a unitary change of the four AO trials.
     112           26 :          quartet = hybrids(:, 2:5, ik)
     113           14 :          IF (MAXVAL(ABS(MATMUL(quartet, CONJG(TRANSPOSE(quartet))) - &
     114            8 :                         MATMUL(candidates(:, 1:4, ik), CONJG(TRANSPOSE(candidates(:, 1:4, ik)))))) &
     115          108 :              > 1.0e-14_dp) nfail = nfail + 1
     116          173 :          candidates(:, :, ik) = MATMUL(rotation(:, :, ik), candidates(:, :, ik))
     117              :       END DO
     118              :    END IF
     119            2 :    CALL wannier90_hybrid_candidates(candidates, RESHAPE([1, 2, 3, 4], [4, 1]), rotated_hybrids)
     120            2 :    IF (para_env%is_source()) THEN
     121            3 :       DO ik = 1, 2
     122           38 :          IF (MAXVAL(ABS(rotated_hybrids(:, :, ik) - MATMUL(rotation(:, :, ik), hybrids(:, :, ik)))) &
     123          115 :              > 1.0e-14_dp) nfail = nfail + 1
     124              :       END DO
     125              :    ELSE
     126            7 :       IF (SIZE(hybrids) /= 0 .OR. SIZE(rotated_hybrids) /= 0) nfail = nfail + 1
     127              :    END IF
     128            2 :    DEALLOCATE (hybrids, rotated_hybrids)
     129            2 :    CALL wannier90_hybrid_candidates(candidates, RESHAPE([1, 2, 3, 4, 5, 2, 3, 4], [4, 2]), hybrids)
     130            2 :    IF (para_env%is_source()) THEN
     131            4 :       IF (.NOT. ALL(SHAPE(hybrids) == [2, 8, 2])) nfail = nfail + 1
     132            3 :       DO ik = 1, 2
     133           22 :          IF (MAXVAL(ABS(SUM(hybrids(:, 1:4, ik), DIM=2)*0.5_dp - candidates(:, 1, ik))) &
     134            0 :              > 1.0e-14_dp) nfail = nfail + 1
     135           22 :          IF (MAXVAL(ABS(SUM(hybrids(:, 5:8, ik), DIM=2)*0.5_dp - candidates(:, 5, ik))) &
     136            1 :              > 1.0e-14_dp) nfail = nfail + 1
     137              :       END DO
     138              :    END IF
     139            2 :    DEALLOCATE (hybrids)
     140            2 :    CALL wannier90_hybrid_candidates(candidates, RESHAPE([1, 2, 3, 4], [4, 0]), hybrids)
     141            2 :    IF (para_env%is_source()) THEN
     142           33 :       IF (MAXVAL(ABS(hybrids - candidates)) > 1.0e-14_dp) nfail = nfail + 1
     143              :    END IF
     144            2 :    DEALLOCATE (hybrids, candidates)
     145            2 :    IF (para_env%is_source()) THEN
     146            1 :       ALLOCATE (candidates(2, 4, 3))
     147            1 :       candidates = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     148              :       ! Every QR anchor chooses at least one column that vanishes elsewhere.
     149              :       ! Exchanging both of those columns recovers the fixed full-rank pair.
     150            1 :       candidates(1, 1, 1) = 10.0_dp
     151            1 :       candidates(1, 1, 3) = 10.0_dp
     152            1 :       candidates(2, 2, 2) = 10.0_dp
     153            1 :       candidates(2, 2, 3) = 10.0_dp
     154            4 :       candidates(1, 3, :) = 1.0_dp
     155            4 :       candidates(2, 4, :) = 1.0_dp
     156              :    ELSE
     157            1 :       ALLOCATE (candidates(0, 0, 0))
     158              :    END IF
     159           10 :    DO anchor = 0, 3
     160            8 :       CALL wannier90_select_projections(candidates, para_env, repaired, pivots, min_svalue, anchor)
     161           10 :       IF (min_svalue > 1.0e-14_dp) nfail = nfail + 1
     162              :    END DO
     163            2 :    CALL wannier90_select_projections(candidates, para_env, repaired, pivots, min_svalue)
     164            2 :    IF (ABS(min_svalue - 1.0_dp) > 1.0e-14_dp) nfail = nfail + 1
     165            6 :    IF (.NOT. ALL(pivots == [3, 4])) nfail = nfail + 1
     166            2 :    DEALLOCATE (candidates)
     167            2 :    IF (para_env%is_source()) THEN
     168            1 :       ALLOCATE (candidates(2, 3, 2))
     169            1 :       candidates = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     170            3 :       candidates(1, 1, :) = 1.0_dp
     171            3 :       candidates(1, 2, :) = 1.0_dp + 1.0e-14_dp
     172            3 :       candidates(2, 3, :) = 1.0_dp
     173              :    ELSE
     174            1 :       ALLOCATE (candidates(0, 0, 0))
     175              :    END IF
     176            2 :    CALL wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue)
     177            6 :    IF (.NOT. ALL(pivots == [1, 3])) nfail = nfail + 1
     178            2 :    IF (para_env%is_source()) THEN
     179            3 :       DO ik = 1, 2
     180          105 :          candidates(:, :, ik) = MATMUL(rotation(:, :, ik), candidates(:, :, ik))
     181              :       END DO
     182              :    END IF
     183            2 :    CALL wannier90_select_projections(candidates, para_env, amn, pivots, min_svalue)
     184            6 :    IF (.NOT. ALL(pivots == [1, 3])) nfail = nfail + 1
     185            2 :    DEALLOCATE (candidates)
     186            6 :    DO ik = 1, 2
     187           18 :       DO anchor = 1, 3
     188           12 :          mixed_reference(1, anchor, ik) = CMPLX(0.1_dp*anchor, 0.2_dp*ik, KIND=dp)
     189           12 :          mixed_reference(2, anchor, ik) = CMPLX(-0.3_dp*anchor, 0.05_dp*ik*anchor, KIND=dp)
     190           12 :          mixed_reference(3, anchor, ik) = CMPLX(0.2_dp*anchor**2, 0.3_dp*ik, KIND=dp)
     191           16 :          mixed_reference(4, anchor, ik) = CMPLX(0.7_dp, -0.1_dp*ik*anchor, KIND=dp)
     192              :       END DO
     193              :    END DO
     194            2 :    min_svalue = wannier90_projection_rank(mixed_reference)
     195              : #if defined (__HAS_IEEE_EXCEPTIONS)
     196              :    CALL ieee_get_halting_mode(IEEE_ALL, actual_halt)
     197              :    IF (ANY(actual_halt .NEQV. expected_halt)) nfail = nfail + 1
     198              :    ! Also check that a caller with traps disabled is left unchanged.
     199              :    CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     200              :    IF (ABS(wannier90_projection_rank(mixed_reference) - min_svalue) > 1.0e-14_dp) nfail = nfail + 1
     201              :    CALL ieee_get_halting_mode(IEEE_ALL, actual_halt)
     202              :    IF (ANY(actual_halt)) nfail = nfail + 1
     203              :    CALL ieee_set_halting_mode(IEEE_ALL, expected_halt)
     204              : #endif
     205              :    CALL wannier90_window_mask([-2.0_dp, -1.0_dp, 1.0_dp, 2.0_dp], 2, &
     206              :                               [-1.5_dp, 1.5_dp, 0.0_dp, 0.0_dp], &
     207            2 :                               [.TRUE., .TRUE., .FALSE., .FALSE.], allowed)
     208           10 :    IF (ANY(allowed .NEQV. [.FALSE., .TRUE., .TRUE., .FALSE.])) nfail = nfail + 1
     209              :    CALL wannier90_window_mask([-2.0_dp, -1.0_dp, 1.0_dp, 2.0_dp], 2, &
     210              :                               [-3.0_dp, 3.0_dp, 0.0_dp, 0.0_dp], &
     211            2 :                               [.TRUE., .TRUE., .FALSE., .TRUE.], allowed)
     212           10 :    IF (ANY(allowed .NEQV. [.TRUE., .TRUE., .FALSE., .FALSE.])) nfail = nfail + 1
     213              : #if defined (__HAS_IEEE_EXCEPTIONS)
     214              :    CALL ieee_get_halting_mode(IEEE_ALL, actual_halt)
     215              :    IF (ANY(actual_halt .NEQV. expected_halt)) nfail = nfail + 1
     216              :    CALL ieee_set_halting_mode(IEEE_ALL, saved_halt)
     217              : #endif
     218            2 :    CALL comm%sum(nfail)
     219            2 :    CPASSERT(nfail == 0)
     220            2 :    CALL mp_para_env_release(para_env)
     221            2 :    CALL mp_world_finalize()
     222            2 : END PROGRAM wannier90_projections_unittest
        

Generated by: LCOV version 2.0-1