LCOV - code coverage report
Current view: top level - src - negf_integr_simpson.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 91.5 % 246 225
Test Date: 2026-07-25 06:35:44 Functions: 60.0 % 10 6

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

Generated by: LCOV version 2.0-1