LCOV - code coverage report
Current view: top level - src - negf_integr_simpson.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:9843133) Lines: 225 246 91.5 %
Date: 2024-05-10 06:53:45 Functions: 6 10 60.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief Adaptive Simpson's rule algorithm to integrate a complex-valued function in a complex plane
      10             : ! **************************************************************************************************
      11             : MODULE negf_integr_simpson
      12             :    USE cp_cfm_basic_linalg,             ONLY: cp_cfm_scale,&
      13             :                                               cp_cfm_scale_and_add
      14             :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      15             :                                               cp_cfm_get_info,&
      16             :                                               cp_cfm_release,&
      17             :                                               cp_cfm_set_all,&
      18             :                                               cp_cfm_to_cfm,&
      19             :                                               cp_cfm_type
      20             :    USE cp_fm_basic_linalg,              ONLY: cp_fm_trace
      21             :    USE cp_fm_struct,                    ONLY: cp_fm_struct_type
      22             :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      23             :                                               cp_fm_get_info,&
      24             :                                               cp_fm_release,&
      25             :                                               cp_fm_type
      26             :    USE kinds,                           ONLY: dp
      27             :    USE mathconstants,                   ONLY: pi,&
      28             :                                               z_one,&
      29             :                                               z_zero
      30             :    USE negf_integr_utils,               ONLY: contour_shape_arc,&
      31             :                                               contour_shape_linear,&
      32             :                                               equidistant_nodes_a_b,&
      33             :                                               rescale_normalised_nodes
      34             :    USE util,                            ONLY: sort
      35             : #include "./base/base_uses.f90"
      36             : 
      37             :    IMPLICIT NONE
      38             :    PRIVATE
      39             : 
      40             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'negf_integr_simpson'
      41             :    ! adaptive Simpson method requires 5 points per subinterval for the error estimate.
      42             :    ! So, in principle, at the end we can compute the value of the integral using
      43             :    ! Boole's rule and possibly improve the actual accuracy by up to one order of magnitude.
      44             :    LOGICAL, PARAMETER, PRIVATE          :: is_boole = .FALSE.
      45             : 
      46             :    INTEGER, PARAMETER, PUBLIC :: sr_shape_linear = contour_shape_linear, &
      47             :                                  sr_shape_arc = contour_shape_arc
      48             : 
      49             :    PUBLIC :: simpsonrule_type
      50             :    PUBLIC :: simpsonrule_init, simpsonrule_release, simpsonrule_get_next_nodes, simpsonrule_refine_integral
      51             : 
      52             : ! **************************************************************************************************
      53             : !> \brief A structure to store data for non-converged sub-interval.
      54             : ! **************************************************************************************************
      55             :    TYPE simpsonrule_subinterval_type
      56             :       !> unscaled lower and upper boundaries within the interval [-1 .. 1]
      57             :       REAL(kind=dp)                                      :: lb, ub
      58             :       !> target accuracy for this sub-interval
      59             :       REAL(kind=dp)                                      :: conv
      60             :       !> estimated error value on this sub-interval
      61             :       REAL(kind=dp)                                      :: error
      62             :       !> integrand values at equally spaced points [a, b, c, d, e] located on the curve shape([lb .. ub])
      63             :       TYPE(cp_cfm_type)                        :: fa, fb, fc, fd, fe
      64             :    END TYPE simpsonrule_subinterval_type
      65             : 
      66             : ! **************************************************************************************************
      67             : !> \brief A structure to store data needed for adaptive Simpson's rule algorithm.
      68             : ! **************************************************************************************************
      69             :    TYPE simpsonrule_type
      70             :       !> lower and upper boundaries of the curve on the complex plane
      71             :       COMPLEX(kind=dp)                                   :: a, b
      72             :       !> ID number which determines the shape of a curve along which the integral will be evaluated
      73             :       INTEGER                                            :: shape_id
      74             :       !> target accuracy
      75             :       REAL(kind=dp)                                      :: conv
      76             :       !> estimated error value on the entire integration interval,
      77             :       !> as well as on converged sub-intervals only
      78             :       REAL(kind=dp)                                      :: error, error_conv
      79             :       !> the estimated value of the integral on the entire interval
      80             :       TYPE(cp_cfm_type), POINTER                         :: integral
      81             :       !> work matrix to store the contribution to the integral on converged sub-intervals
      82             :       TYPE(cp_cfm_type), POINTER                         :: integral_conv
      83             :       !> work matrices which stores approximated integral computed by using a/b/c, c/d/e, and a/c/e points respectively
      84             :       TYPE(cp_cfm_type), POINTER                         :: integral_abc, integral_cde, integral_ace
      85             :       !> work matrix to temporarily store error estimate of the integral on a sub-interval for every matrix element
      86             :       TYPE(cp_fm_type), POINTER                          :: error_fm
      87             :       !> weights associated with matrix elements; the final error is computed as Trace(error_fm * weights)
      88             :       TYPE(cp_fm_type), POINTER                          :: weights
      89             :       ! non-converged sub-intervals
      90             :       TYPE(simpsonrule_subinterval_type), ALLOCATABLE, &
      91             :          DIMENSION(:)                                    :: subintervals
      92             :       !> complete list of nodes over the normalised interval [-1 .. 1] needed to restart
      93             :       !> Useful when a series of similar integrals need to be computed at an identical set
      94             :       !> of points, so intermediate quantities can be saved and reused.
      95             :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: tnodes
      96             :    END TYPE simpsonrule_type
      97             : 
      98             :    COMPLEX(kind=dp), PARAMETER, PRIVATE :: z_four = 4.0_dp*z_one
      99             : 
     100             : CONTAINS
     101             : 
     102             : ! **************************************************************************************************
     103             : !> \brief Initialise a Simpson's rule environment variable.
     104             : !> \param sr_env   Simpson's rule environment (initialised on exit)
     105             : !> \param xnodes   points at which an integrand needs to be computed (initialised on exit)
     106             : !> \param nnodes   initial number of points to compute (initialised on exit)
     107             : !> \param a        integral lower boundary
     108             : !> \param b        integral upper boundary
     109             : !> \param shape_id shape of a curve along which the integral will be evaluated
     110             : !> \param conv     convergence threshold
     111             : !> \param weights  weights associated with matrix elements; used to compute cumulative error
     112             : !> \param tnodes_restart list of nodes over the interval [-1 .. 1] from a previous integral evaluation.
     113             : !>                       If present, the same set of 'xnodes' will be used to compute this integral.
     114             : !> \par History
     115             : !>   * 05.2017 created [Sergey Chulkov]
     116             : !> \note When we integrate the retarded Green's function times the Fermi function over the energy
     117             : !>       domain and pass the overlap matrix (S) as the 'weights' matrix, the convergence threshold
     118             : !>       ('conv') becomes the maximum error in the total number of electrons multiplied by pi.
     119             : ! **************************************************************************************************
     120          50 :    SUBROUTINE simpsonrule_init(sr_env, xnodes, nnodes, a, b, shape_id, conv, weights, tnodes_restart)
     121             :       TYPE(simpsonrule_type), INTENT(out)                :: sr_env
     122             :       INTEGER, INTENT(inout)                             :: nnodes
     123             :       COMPLEX(kind=dp), DIMENSION(nnodes), INTENT(out)   :: xnodes
     124             :       COMPLEX(kind=dp), INTENT(in)                       :: a, b
     125             :       INTEGER, INTENT(in)                                :: shape_id
     126             :       REAL(kind=dp), INTENT(in)                          :: conv
     127             :       TYPE(cp_fm_type), INTENT(IN)                       :: weights
     128             :       REAL(kind=dp), DIMENSION(nnodes), INTENT(in), &
     129             :          OPTIONAL                                        :: tnodes_restart
     130             : 
     131             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'simpsonrule_init'
     132             : 
     133             :       INTEGER                                            :: handle, icol, irow, ncols, nrows
     134             :       REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
     135          30 :          POINTER                                         :: w_data, w_data_my
     136             :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct
     137             : 
     138          30 :       CALL timeset(routineN, handle)
     139             : 
     140          30 :       CPASSERT(nnodes > 4)
     141             : 
     142             :       ! ensure that MOD(nnodes-1, 4) == 0
     143          30 :       nnodes = 4*((nnodes - 1)/4) + 1
     144             : 
     145          30 :       sr_env%shape_id = shape_id
     146          30 :       sr_env%a = a
     147          30 :       sr_env%b = b
     148          30 :       sr_env%conv = conv
     149          30 :       sr_env%error = HUGE(0.0_dp)
     150          30 :       sr_env%error_conv = 0.0_dp
     151             : 
     152          30 :       NULLIFY (sr_env%error_fm, sr_env%weights)
     153          30 :       CALL cp_fm_get_info(weights, local_data=w_data, nrow_local=nrows, ncol_local=ncols, matrix_struct=fm_struct)
     154          30 :       ALLOCATE (sr_env%error_fm, sr_env%weights)
     155          30 :       CALL cp_fm_create(sr_env%error_fm, fm_struct)
     156          30 :       CALL cp_fm_create(sr_env%weights, fm_struct)
     157          30 :       CALL cp_fm_get_info(sr_env%weights, local_data=w_data_my)
     158             : 
     159             :       ! use the explicit loop to avoid temporary arrays. The magic constant 15.0 is due to Simpson's rule error analysis.
     160         704 :       DO icol = 1, ncols
     161        8769 :          DO irow = 1, nrows
     162        8739 :             w_data_my(irow, icol) = ABS(w_data(irow, icol))/15.0_dp
     163             :          END DO
     164             :       END DO
     165             : 
     166          30 :       NULLIFY (sr_env%integral, sr_env%integral_conv)
     167          30 :       NULLIFY (sr_env%integral_abc, sr_env%integral_cde, sr_env%integral_ace)
     168             : 
     169          90 :       ALLOCATE (sr_env%tnodes(nnodes))
     170             : 
     171          30 :       IF (PRESENT(tnodes_restart)) THEN
     172        2880 :          sr_env%tnodes(1:nnodes) = tnodes_restart(1:nnodes)
     173             :       ELSE
     174          10 :          CALL equidistant_nodes_a_b(-1.0_dp, 1.0_dp, nnodes, sr_env%tnodes)
     175             :       END IF
     176          30 :       CALL rescale_normalised_nodes(nnodes, sr_env%tnodes, a, b, shape_id, xnodes)
     177             : 
     178          30 :       CALL timestop(handle)
     179         110 :    END SUBROUTINE simpsonrule_init
     180             : 
     181             : ! **************************************************************************************************
     182             : !> \brief Release a Simpson's rule environment variable.
     183             : !> \param sr_env   Simpson's rule environment (modified on exit)
     184             : !> \par History
     185             : !>   * 05.2017 created [Sergey Chulkov]
     186             : ! **************************************************************************************************
     187          30 :    SUBROUTINE simpsonrule_release(sr_env)
     188             :       TYPE(simpsonrule_type), INTENT(inout)              :: sr_env
     189             : 
     190             :       CHARACTER(len=*), PARAMETER :: routineN = 'simpsonrule_release'
     191             : 
     192             :       INTEGER                                            :: handle, interval
     193             : 
     194          30 :       CALL timeset(routineN, handle)
     195          30 :       IF (ALLOCATED(sr_env%subintervals)) THEN
     196           0 :          DO interval = SIZE(sr_env%subintervals), 1, -1
     197           0 :             CALL cp_cfm_release(sr_env%subintervals(interval)%fa)
     198           0 :             CALL cp_cfm_release(sr_env%subintervals(interval)%fb)
     199           0 :             CALL cp_cfm_release(sr_env%subintervals(interval)%fc)
     200           0 :             CALL cp_cfm_release(sr_env%subintervals(interval)%fd)
     201           0 :             CALL cp_cfm_release(sr_env%subintervals(interval)%fe)
     202             :          END DO
     203             : 
     204           0 :          DEALLOCATE (sr_env%subintervals)
     205             :       END IF
     206             : 
     207          30 :       IF (ASSOCIATED(sr_env%integral)) THEN
     208          30 :          CALL cp_cfm_release(sr_env%integral)
     209          30 :          DEALLOCATE (sr_env%integral)
     210             :          NULLIFY (sr_env%integral)
     211             :       END IF
     212          30 :       IF (ASSOCIATED(sr_env%integral_conv)) THEN
     213          30 :          CALL cp_cfm_release(sr_env%integral_conv)
     214          30 :          DEALLOCATE (sr_env%integral_conv)
     215             :          NULLIFY (sr_env%integral_conv)
     216             :       END IF
     217          30 :       IF (ASSOCIATED(sr_env%integral_abc)) THEN
     218          30 :          CALL cp_cfm_release(sr_env%integral_abc)
     219          30 :          DEALLOCATE (sr_env%integral_abc)
     220             :          NULLIFY (sr_env%integral_abc)
     221             :       END IF
     222          30 :       IF (ASSOCIATED(sr_env%integral_cde)) THEN
     223          30 :          CALL cp_cfm_release(sr_env%integral_cde)
     224          30 :          DEALLOCATE (sr_env%integral_cde)
     225             :          NULLIFY (sr_env%integral_cde)
     226             :       END IF
     227          30 :       IF (ASSOCIATED(sr_env%integral_ace)) THEN
     228          30 :          CALL cp_cfm_release(sr_env%integral_ace)
     229          30 :          DEALLOCATE (sr_env%integral_ace)
     230             :          NULLIFY (sr_env%integral_ace)
     231             :       END IF
     232          30 :       IF (ASSOCIATED(sr_env%error_fm)) THEN
     233          30 :          CALL cp_fm_release(sr_env%error_fm)
     234          30 :          DEALLOCATE (sr_env%error_fm)
     235             :          NULLIFY (sr_env%error_fm)
     236             :       END IF
     237          30 :       IF (ASSOCIATED(sr_env%weights)) THEN
     238          30 :          CALL cp_fm_release(sr_env%weights)
     239          30 :          DEALLOCATE (sr_env%weights)
     240             :          NULLIFY (sr_env%weights)
     241             :       END IF
     242             : 
     243          30 :       IF (ALLOCATED(sr_env%tnodes)) DEALLOCATE (sr_env%tnodes)
     244             : 
     245          30 :       CALL timestop(handle)
     246          30 :    END SUBROUTINE simpsonrule_release
     247             : 
     248             : ! **************************************************************************************************
     249             : !> \brief Get the next set of nodes where to compute integrand.
     250             : !> \param sr_env      Simpson's rule environment (modified on exit)
     251             : !> \param xnodes_next list of additional points (initialised on exit)
     252             : !> \param nnodes      actual number of points to compute (modified on exit)
     253             : !> \par History
     254             : !>   * 05.2017 created [Sergey Chulkov]
     255             : !> \note The number of nodes returned is limited by the initial value of the nnodes variable;
     256             : !>       un exit nnodes == 0 means that the target accuracy has been achieved.
     257             : ! **************************************************************************************************
     258          68 :    SUBROUTINE simpsonrule_get_next_nodes(sr_env, xnodes_next, nnodes)
     259             :       TYPE(simpsonrule_type), INTENT(inout)              :: sr_env
     260             :       INTEGER, INTENT(inout)                             :: nnodes
     261             :       COMPLEX(kind=dp), DIMENSION(nnodes), INTENT(out)   :: xnodes_next
     262             : 
     263             :       CHARACTER(len=*), PARAMETER :: routineN = 'simpsonrule_get_next_nodes'
     264             : 
     265             :       INTEGER                                            :: handle, nnodes_old
     266          68 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: tnodes, tnodes_old
     267             : 
     268          68 :       CALL timeset(routineN, handle)
     269         204 :       ALLOCATE (tnodes(nnodes))
     270             : 
     271          68 :       CALL simpsonrule_get_next_nodes_real(sr_env, tnodes, nnodes)
     272          68 :       IF (nnodes > 0) THEN
     273          68 :          CALL MOVE_ALLOC(sr_env%tnodes, tnodes_old)
     274          68 :          nnodes_old = SIZE(tnodes_old)
     275             : 
     276         204 :          ALLOCATE (sr_env%tnodes(nnodes_old + nnodes))
     277        6040 :          sr_env%tnodes(1:nnodes_old) = tnodes_old(1:nnodes_old)
     278        1108 :          sr_env%tnodes(nnodes_old + 1:nnodes_old + nnodes) = tnodes(1:nnodes)
     279          68 :          DEALLOCATE (tnodes_old)
     280             : 
     281          68 :          CALL rescale_normalised_nodes(nnodes, tnodes, sr_env%a, sr_env%b, sr_env%shape_id, xnodes_next)
     282             :       END IF
     283             : 
     284          68 :       DEALLOCATE (tnodes)
     285          68 :       CALL timestop(handle)
     286         136 :    END SUBROUTINE simpsonrule_get_next_nodes
     287             : 
     288             : ! **************************************************************************************************
     289             : !> \brief Low level routine that returns unscaled nodes on interval [-1 .. 1].
     290             : !> \param sr_env       Simpson's rule environment
     291             : !> \param xnodes_unity list of additional unscaled nodes (initialised on exit)
     292             : !> \param nnodes       actual number of points to compute (initialised on exit)
     293             : !> \par History
     294             : !>   * 05.2017 created [Sergey Chulkov]
     295             : ! **************************************************************************************************
     296          68 :    SUBROUTINE simpsonrule_get_next_nodes_real(sr_env, xnodes_unity, nnodes)
     297             :       TYPE(simpsonrule_type), INTENT(in)                 :: sr_env
     298             :       REAL(kind=dp), DIMENSION(:), INTENT(out)           :: xnodes_unity
     299             :       INTEGER, INTENT(out)                               :: nnodes
     300             : 
     301             :       CHARACTER(len=*), PARAMETER :: routineN = 'simpsonrule_get_next_nodes_real'
     302             : 
     303             :       INTEGER                                            :: handle, interval, nintervals
     304             : 
     305          68 :       CALL timeset(routineN, handle)
     306             : 
     307          68 :       IF (ALLOCATED(sr_env%subintervals)) THEN
     308          68 :          nintervals = SIZE(sr_env%subintervals)
     309             :       ELSE
     310             :          nintervals = 0
     311             :       END IF
     312             : 
     313          68 :       IF (nintervals > 0) THEN
     314          68 :          IF (SIZE(xnodes_unity) < 4*nintervals) &
     315          52 :             nintervals = SIZE(xnodes_unity)/4
     316             : 
     317         328 :          DO interval = 1, nintervals
     318             :             xnodes_unity(4*interval - 3) = 0.125_dp* &
     319         260 :                                            (7.0_dp*sr_env%subintervals(interval)%lb + sr_env%subintervals(interval)%ub)
     320             :             xnodes_unity(4*interval - 2) = 0.125_dp* &
     321         260 :                                            (5.0_dp*sr_env%subintervals(interval)%lb + 3.0_dp*sr_env%subintervals(interval)%ub)
     322             :             xnodes_unity(4*interval - 1) = 0.125_dp* &
     323         260 :                                            (3.0_dp*sr_env%subintervals(interval)%lb + 5.0_dp*sr_env%subintervals(interval)%ub)
     324         328 :             xnodes_unity(4*interval) = 0.125_dp*(sr_env%subintervals(interval)%lb + 7.0_dp*sr_env%subintervals(interval)%ub)
     325             :          END DO
     326             :       END IF
     327             : 
     328          68 :       nnodes = 4*nintervals
     329          68 :       CALL timestop(handle)
     330          68 :    END SUBROUTINE simpsonrule_get_next_nodes_real
     331             : 
     332             : ! **************************************************************************************************
     333             : !> \brief Compute integral using the simpson's rules.
     334             : !> \param sr_env     Simpson's rule environment
     335             : !> \param zdata_next precomputed integrand values at points xnodes_next (nullified on exit)
     336             : !> \par History
     337             : !>   * 05.2017 created [Sergey Chulkov]
     338             : ! **************************************************************************************************
     339          98 :    SUBROUTINE simpsonrule_refine_integral(sr_env, zdata_next)
     340             :       TYPE(simpsonrule_type), INTENT(inout)              :: sr_env
     341             :       TYPE(cp_cfm_type), DIMENSION(:), INTENT(inout)     :: zdata_next
     342             : 
     343             :       CHARACTER(len=*), PARAMETER :: routineN = 'simpsonrule_refine_integral'
     344             :       TYPE(cp_cfm_type), PARAMETER                       :: cfm_null = cp_cfm_type()
     345             : 
     346          98 :       COMPLEX(kind=dp), ALLOCATABLE, DIMENSION(:)        :: zscale
     347             :       COMPLEX(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
     348          98 :          POINTER                                         :: error_zdata
     349             :       INTEGER                                            :: handle, interval, ipoint, jpoint, &
     350             :                                                             nintervals, nintervals_exist, npoints
     351          98 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: inds
     352             :       LOGICAL                                            :: interval_converged, interval_exists
     353             :       REAL(kind=dp)                                      :: my_bound, rscale
     354          98 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: errors
     355             :       REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
     356          98 :          POINTER                                         :: error_rdata
     357             :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct
     358             :       TYPE(simpsonrule_subinterval_type), ALLOCATABLE, &
     359          98 :          DIMENSION(:)                                    :: subintervals
     360             : 
     361          98 :       CALL timeset(routineN, handle)
     362             : 
     363          98 :       npoints = SIZE(zdata_next)
     364          98 :       IF (ASSOCIATED(sr_env%integral)) THEN
     365             :          ! we need 4 new points per subinterval (p, q, r, s)
     366             :          !   p   q   r   s
     367             :          ! a . b . c . d . e
     368          68 :          CPASSERT(npoints > 0 .AND. MOD(npoints, 4) == 0)
     369             :       ELSE
     370             :          ! first call: need 4*n+1 points
     371             :          ! a1 b1 c1 d1 e1
     372             :          !             a2 b2 c2 d2 e2
     373             :          !                         a3 b3 c3 d3 e3
     374          30 :          CPASSERT(npoints > 1 .AND. MOD(npoints, 4) == 1)
     375             :       END IF
     376             : 
     377             :       ! compute weights of new points on a complex contour according to their values of the 't' parameter
     378          98 :       nintervals_exist = SIZE(sr_env%tnodes)
     379          98 :       CPASSERT(nintervals_exist >= npoints)
     380         294 :       ALLOCATE (zscale(npoints))
     381             : 
     382             :       CALL rescale_normalised_nodes(npoints, sr_env%tnodes(nintervals_exist - npoints + 1:nintervals_exist), &
     383          98 :                                     sr_env%a, sr_env%b, sr_env%shape_id, weights=zscale)
     384             : 
     385             :       ! rescale integrand values
     386        4128 :       DO ipoint = 1, npoints
     387        4128 :          CALL cp_cfm_scale(zscale(ipoint), zdata_next(ipoint))
     388             :       END DO
     389             : 
     390          98 :       DEALLOCATE (zscale)
     391             : 
     392             :       ! insert new points
     393          98 :       nintervals = npoints/4
     394          98 :       IF (ASSOCIATED(sr_env%integral)) THEN
     395             :          ! subdivide existing intervals
     396          68 :          nintervals_exist = SIZE(sr_env%subintervals)
     397          68 :          CPASSERT(nintervals <= nintervals_exist)
     398             : 
     399        1624 :          ALLOCATE (subintervals(nintervals_exist + nintervals))
     400             : 
     401         328 :          DO interval = 1, nintervals
     402         260 :             subintervals(2*interval - 1)%lb = sr_env%subintervals(interval)%lb
     403         260 :             subintervals(2*interval - 1)%ub = 0.5_dp*(sr_env%subintervals(interval)%lb + sr_env%subintervals(interval)%ub)
     404         260 :             subintervals(2*interval - 1)%conv = 0.5_dp*sr_env%subintervals(interval)%conv
     405         260 :             subintervals(2*interval - 1)%fa = sr_env%subintervals(interval)%fa
     406         260 :             subintervals(2*interval - 1)%fb = zdata_next(4*interval - 3)
     407         260 :             subintervals(2*interval - 1)%fc = sr_env%subintervals(interval)%fb
     408         260 :             subintervals(2*interval - 1)%fd = zdata_next(4*interval - 2)
     409         260 :             subintervals(2*interval - 1)%fe = sr_env%subintervals(interval)%fc
     410             : 
     411         260 :             subintervals(2*interval)%lb = subintervals(2*interval - 1)%ub
     412         260 :             subintervals(2*interval)%ub = sr_env%subintervals(interval)%ub
     413         260 :             subintervals(2*interval)%conv = subintervals(2*interval - 1)%conv
     414         260 :             subintervals(2*interval)%fa = sr_env%subintervals(interval)%fc
     415         260 :             subintervals(2*interval)%fb = zdata_next(4*interval - 1)
     416         260 :             subintervals(2*interval)%fc = sr_env%subintervals(interval)%fd
     417         260 :             subintervals(2*interval)%fd = zdata_next(4*interval)
     418         260 :             subintervals(2*interval)%fe = sr_env%subintervals(interval)%fe
     419             : 
     420        1368 :             zdata_next(4*interval - 3:4*interval) = cfm_null
     421             :          END DO
     422             : 
     423         968 :          DO interval = nintervals + 1, nintervals_exist
     424         968 :             subintervals(interval + nintervals) = sr_env%subintervals(interval)
     425             :          END DO
     426          68 :          DEALLOCATE (sr_env%subintervals)
     427             :       ELSE
     428             :          ! first time -- allocate matrices and create a new set of intervals
     429          30 :          CALL cp_cfm_get_info(zdata_next(1), matrix_struct=fm_struct)
     430             :          ALLOCATE (sr_env%integral, sr_env%integral_conv, &
     431          30 :                    sr_env%integral_abc, sr_env%integral_cde, sr_env%integral_ace)
     432          30 :          CALL cp_cfm_create(sr_env%integral, fm_struct)
     433          30 :          CALL cp_cfm_create(sr_env%integral_conv, fm_struct)
     434          30 :          CALL cp_cfm_create(sr_env%integral_abc, fm_struct)
     435          30 :          CALL cp_cfm_create(sr_env%integral_cde, fm_struct)
     436          30 :          CALL cp_cfm_create(sr_env%integral_ace, fm_struct)
     437             : 
     438          30 :          CALL cp_cfm_set_all(sr_env%integral_conv, z_zero)
     439             : 
     440         830 :          ALLOCATE (subintervals(nintervals))
     441             : 
     442          30 :          rscale = 1.0_dp/REAL(nintervals, kind=dp)
     443             : 
     444         770 :          DO interval = 1, nintervals
     445             :             ! lower bound: point with indices 1, 5, 9, ..., 4*nintervals+1
     446         740 :             subintervals(interval)%lb = sr_env%tnodes(4*interval - 3)
     447         740 :             subintervals(interval)%ub = sr_env%tnodes(4*interval + 1)
     448         740 :             subintervals(interval)%conv = rscale*sr_env%conv
     449             : 
     450         740 :             subintervals(interval)%fa = zdata_next(4*interval - 3)
     451         740 :             subintervals(interval)%fb = zdata_next(4*interval - 2)
     452         740 :             subintervals(interval)%fc = zdata_next(4*interval - 1)
     453         740 :             subintervals(interval)%fd = zdata_next(4*interval)
     454         770 :             subintervals(interval)%fe = zdata_next(4*interval + 1)
     455             :          END DO
     456             :       END IF
     457             : 
     458             :       ! we kept the originals matrices for internal use, so set the matrix to null
     459             :       ! to prevent  alteration of the matrices from the outside
     460        4128 :       zdata_next(1:npoints) = cfm_null
     461             : 
     462          98 :       CALL cp_fm_get_info(sr_env%error_fm, local_data=error_rdata)
     463          98 :       CALL cp_cfm_get_info(sr_env%integral_ace, local_data=error_zdata)
     464             : 
     465             :       ! do actual integration
     466          98 :       CALL cp_cfm_to_cfm(sr_env%integral_conv, sr_env%integral)
     467          98 :       sr_env%error = sr_env%error_conv
     468          98 :       nintervals_exist = SIZE(subintervals)
     469             : 
     470        2258 :       DO interval = 1, nintervals_exist
     471        2160 :          rscale = subintervals(interval)%ub - subintervals(interval)%lb
     472             :          CALL do_simpson_rule(sr_env%integral_ace, &
     473             :                               subintervals(interval)%fa, &
     474             :                               subintervals(interval)%fc, &
     475             :                               subintervals(interval)%fe, &
     476        2160 :                               -0.5_dp*rscale)
     477             :          CALL do_simpson_rule(sr_env%integral_abc, &
     478             :                               subintervals(interval)%fa, &
     479             :                               subintervals(interval)%fb, &
     480             :                               subintervals(interval)%fc, &
     481        2160 :                               0.25_dp*rscale)
     482             :          CALL do_simpson_rule(sr_env%integral_cde, &
     483             :                               subintervals(interval)%fc, &
     484             :                               subintervals(interval)%fd, &
     485             :                               subintervals(interval)%fe, &
     486        2160 :                               0.25_dp*rscale)
     487             : 
     488        2160 :          CALL cp_cfm_scale_and_add(z_one, sr_env%integral_abc, z_one, sr_env%integral_cde)
     489        2160 :          CALL cp_cfm_scale_and_add(z_one, sr_env%integral_ace, z_one, sr_env%integral_abc)
     490             : 
     491             :          IF (is_boole) THEN
     492             :             CALL do_boole_rule(sr_env%integral_abc, &
     493             :                                subintervals(interval)%fa, &
     494             :                                subintervals(interval)%fb, &
     495             :                                subintervals(interval)%fc, &
     496             :                                subintervals(interval)%fd, &
     497             :                                subintervals(interval)%fe, &
     498             :                                0.5_dp*rscale, sr_env%integral_cde)
     499             :          END IF
     500             : 
     501        2160 :          CALL cp_cfm_scale_and_add(z_one, sr_env%integral, z_one, sr_env%integral_abc)
     502             : 
     503             :          ! sr_env%error_fm = ABS(sr_env%integral_ace); no temporary arrays as pointers have different types
     504      674220 :          error_rdata(:, :) = ABS(error_zdata(:, :))
     505        2160 :          CALL cp_fm_trace(sr_env%error_fm, sr_env%weights, subintervals(interval)%error)
     506             : 
     507        2160 :          sr_env%error = sr_env%error + subintervals(interval)%error
     508             : 
     509             :          ! add contributions from converged subintervals, so we could drop them afterward
     510        2258 :          IF (subintervals(interval)%error <= subintervals(interval)%conv) THEN
     511         766 :             CALL cp_cfm_scale_and_add(z_one, sr_env%integral_conv, z_one, sr_env%integral_abc)
     512         766 :             sr_env%error_conv = sr_env%error_conv + subintervals(interval)%error
     513             :          END IF
     514             :       END DO
     515             : 
     516          98 :       IF (sr_env%error <= sr_env%conv) THEN
     517             :          ! We have already reached the target accuracy, so we can drop all subintervals
     518             :          ! (even those where local convergence has not been achieved). From now on environment
     519             :          ! components 'sr_env%error' and 'sr_env%integral_conv' hold incorrect values,
     520             :          ! but they should not been accessed from the outside anyway
     521             :          ! (uncomment the following two lines if they are actually need)
     522             : 
     523             :          ! sr_env%error_conv = sr_env%error
     524             :          ! CALL cp_cfm_to_cfm(sr_env%integral, sr_env%integral_conv)
     525             : 
     526             :          ! Only deallocate the fa component explicitly if there is no interval to the left from it
     527         894 :          DO interval = nintervals_exist, 1, -1
     528         864 :             interval_exists = .FALSE.
     529         864 :             my_bound = subintervals(interval)%lb
     530       18824 :             DO jpoint = 1, nintervals_exist
     531       18824 :                IF (subintervals(jpoint)%ub == my_bound) THEN
     532             :                   interval_exists = .TRUE.
     533             :                   EXIT
     534             :                END IF
     535             :             END DO
     536         864 :             IF (.NOT. interval_exists) THEN
     537             :                ! interval does not exist anymore, so it is safe to release the matrix
     538          58 :                CALL cp_cfm_release(subintervals(interval)%fa)
     539             :             ELSE IF (interval_converged) THEN
     540             :                ! the interval still exists and will be released with fe
     541             :             END IF
     542         864 :             CALL cp_cfm_release(subintervals(interval)%fb)
     543         864 :             CALL cp_cfm_release(subintervals(interval)%fc)
     544         864 :             CALL cp_cfm_release(subintervals(interval)%fd)
     545         894 :             CALL cp_cfm_release(subintervals(interval)%fe)
     546             :          END DO
     547             :       ELSE
     548             :          ! sort subinterval according to their convergence, and drop convergent ones
     549         340 :          ALLOCATE (errors(nintervals_exist), inds(nintervals_exist))
     550             : 
     551        1364 :          nintervals = 0
     552        1364 :          DO interval = 1, nintervals_exist
     553        1296 :             errors(interval) = subintervals(interval)%error
     554             : 
     555        1296 :             IF (subintervals(interval)%error > subintervals(interval)%conv) &
     556        1228 :                nintervals = nintervals + 1
     557             :          END DO
     558             : 
     559          68 :          CALL sort(errors, nintervals_exist, inds)
     560             : 
     561          68 :          IF (nintervals > 0) &
     562        1364 :             ALLOCATE (sr_env%subintervals(nintervals))
     563             : 
     564             :          nintervals = 0
     565        1364 :          DO ipoint = nintervals_exist, 1, -1
     566        1296 :             interval = inds(ipoint)
     567             : 
     568        1364 :             IF (subintervals(interval)%error > subintervals(interval)%conv) THEN
     569        1160 :                nintervals = nintervals + 1
     570             : 
     571        1160 :                sr_env%subintervals(nintervals) = subintervals(interval)
     572             :             ELSE
     573             :                ! Release matrices of converged intervals. Special cases: left and right boundary
     574             :                ! Check whether the neighboring interval still exists and if it does, check for its convergence
     575         136 :                interval_exists = .FALSE.
     576         136 :                my_bound = subintervals(interval)%lb
     577        1538 :                DO jpoint = 1, nintervals_exist
     578        1538 :                   IF (subintervals(jpoint)%ub == my_bound) THEN
     579             :                      interval_exists = .TRUE.
     580             :                      EXIT
     581             :                   END IF
     582             :                END DO
     583         136 :                IF (.NOT. interval_exists) THEN
     584             :                   ! interval does not exist anymore, so it is safe to release the matrix
     585          24 :                   CALL cp_cfm_release(subintervals(interval)%fa)
     586             :                ELSE IF (interval_converged) THEN
     587             :                   ! the interval still exists and will be released with fe
     588             :                END IF
     589         136 :                CALL cp_cfm_release(subintervals(interval)%fb)
     590         136 :                CALL cp_cfm_release(subintervals(interval)%fc)
     591         136 :                CALL cp_cfm_release(subintervals(interval)%fd)
     592             : 
     593             :                ! Right border: Check for the existence and the convergence of the interval
     594             :                ! If the right interval does not exist or has converged, release the matrix
     595         136 :                interval_exists = .FALSE.
     596         136 :                interval_converged = .FALSE.
     597         136 :                my_bound = subintervals(interval)%ub
     598        1670 :                DO jpoint = 1, nintervals_exist
     599        1670 :                   IF (subintervals(jpoint)%lb == my_bound) THEN
     600         112 :                      interval_exists = .TRUE.
     601         112 :                      IF (subintervals(jpoint)%error <= subintervals(jpoint)%conv) interval_converged = .TRUE.
     602             :                      EXIT
     603             :                   END IF
     604             :                END DO
     605         136 :                IF (.NOT. interval_exists .OR. interval_converged) THEN
     606          84 :                   CALL cp_cfm_release(subintervals(interval)%fe)
     607             :                END IF
     608             :             END IF
     609             :          END DO
     610             : 
     611          68 :          DEALLOCATE (errors, inds)
     612             :       END IF
     613             : 
     614          98 :       DEALLOCATE (subintervals)
     615             : 
     616          98 :       CALL timestop(handle)
     617          98 :    END SUBROUTINE simpsonrule_refine_integral
     618             : 
     619             : ! **************************************************************************************************
     620             : !> \brief Approximate value of the integral on subinterval [a .. c] using the Simpson's rule.
     621             : !> \param integral   approximated integral = length / 6 * (fa + 4*fb + fc) (initialised on exit)
     622             : !> \param fa         integrand value at point a
     623             : !> \param fb         integrand value at point b = (a + c) / 2
     624             : !> \param fc         integrand value at point c
     625             : !> \param length     distance between points a and c [ABS(c-a)]
     626             : !> \par History
     627             : !>   * 05.2017 created [Sergey Chulkov]
     628             : ! **************************************************************************************************
     629        6480 :    SUBROUTINE do_simpson_rule(integral, fa, fb, fc, length)
     630             :       TYPE(cp_cfm_type), INTENT(IN)                      :: integral, fa, fb, fc
     631             :       REAL(kind=dp), INTENT(in)                          :: length
     632             : 
     633        6480 :       CALL cp_cfm_to_cfm(fa, integral)
     634        6480 :       CALL cp_cfm_scale_and_add(z_one, integral, z_four, fb)
     635        6480 :       CALL cp_cfm_scale_and_add(z_one, integral, z_one, fc)
     636        6480 :       CALL cp_cfm_scale(length/6.0_dp, integral)
     637        6480 :    END SUBROUTINE do_simpson_rule
     638             : 
     639             : ! **************************************************************************************************
     640             : !> \brief Approximate value of the integral on subinterval [a .. e] using the Boole's rule.
     641             : !> \param integral   approximated integral = length / 90 * (7*fa + 32*fb + 12*fc + 32*fd + 7*fe)
     642             : !>                   (initialised on exit)
     643             : !> \param fa         integrand value at point a
     644             : !> \param fb         integrand value at point b = a + (e-a)/4
     645             : !> \param fc         integrand value at point c = a + (e-a)/2
     646             : !> \param fd         integrand value at point d = a + 3*(e-a)/4
     647             : !> \param fe         integrand value at point e
     648             : !> \param length     distance between points a and e [ABS(e-a)]
     649             : !> \param work       work matrix
     650             : !> \par History
     651             : !>   * 05.2017 created [Sergey Chulkov]
     652             : ! **************************************************************************************************
     653           0 :    SUBROUTINE do_boole_rule(integral, fa, fb, fc, fd, fe, length, work)
     654             :       TYPE(cp_cfm_type), INTENT(IN)                      :: integral, fa, fb, fc, fd, fe
     655             :       REAL(kind=dp), INTENT(in)                          :: length
     656             :       TYPE(cp_cfm_type), INTENT(IN)                      :: work
     657             : 
     658             :       REAL(kind=dp)                                      :: rscale
     659             : 
     660           0 :       rscale = length/90.0_dp
     661             : 
     662           0 :       CALL cp_cfm_to_cfm(fc, integral)
     663           0 :       CALL cp_cfm_scale(12.0_dp*rscale, integral)
     664             : 
     665           0 :       CALL cp_cfm_to_cfm(fa, work)
     666           0 :       CALL cp_cfm_scale_and_add(z_one, work, z_one, fe)
     667           0 :       CALL cp_cfm_scale(7.0_dp*rscale, work)
     668           0 :       CALL cp_cfm_scale_and_add(z_one, integral, z_one, work)
     669             : 
     670           0 :       CALL cp_cfm_to_cfm(fb, work)
     671           0 :       CALL cp_cfm_scale_and_add(z_one, work, z_one, fd)
     672           0 :       CALL cp_cfm_scale(32.0_dp*rscale, work)
     673           0 :       CALL cp_cfm_scale_and_add(z_one, integral, z_one, work)
     674           0 :    END SUBROUTINE do_boole_rule
     675           0 : END MODULE negf_integr_simpson

Generated by: LCOV version 1.15