(git:1f285aa)
minimax_exp.F
Go to the documentation of this file.
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 Routines to calculate the minimax coefficients in order to
10 !> approximate 1/x as a sum over exponential functions
11 !> 1/x ~ SUM_{i}^{K} w_i EXP(-a_i * x) for x belonging to [1:Rc].
12 !>
13 !> This module is an extension of original minimax module minimax_exp_k15
14 !> (up to K = 15) to provide minimax approximations for larger
15 !> ranges Rc (up to K = 53).
16 !>
17 !> k53 implementation is based on directly tabulated coefficients from
18 !> D. Braess and W. Hackbusch, IMA Journal of Numerical Analysis 25.4 (2005): 685-697
19 !> http://www.mis.mpg.de/scicomp/EXP_SUM/1_x
20 !>
21 !> Note: Due to discrete Rc values, the k53 implementation does not yield
22 !> optimal approximations for arbitrary Rc. If optimal minimax coefficients
23 !> are needed, the minimax_exp_k15 module should be extended by interpolating
24 !> k53 coefficients.
25 !> \par History
26 !> 02.2016 created [Patrick Seewald]
27 ! **************************************************************************************************
28 
30  USE cp_log_handling, ONLY: cp_to_string
31  USE kinds, ONLY: dp
32  USE minimax_exp_k15, ONLY: check_range_k15,&
35  USE minimax_exp_k53, ONLY: r_max,&
36  r_mm,&
37  err_mm,&
39  k_max,&
40  k_mm,&
41  k_p,&
42  n_approx
43 #include "../base/base_uses.f90"
44 
45  IMPLICIT NONE
46 
47  PRIVATE
48 
49  CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'minimax_exp'
50 
51  INTEGER, PARAMETER :: mm_k15 = 0, mm_k53 = 1
52 
54 
55  ! Imported from minimax_k53:
56 
57  ! Number of tabulated minimax approximations:
58  ! INTEGER, PARAMETER :: n_approx
59 
60  ! Number of K values:
61  ! INTEGER, PARAMETER :: n_k
62 
63  ! Maximum K value:
64  ! INTEGER, PARAMETER :: k_max
65 
66  ! Maximum range Rc:
67  ! REAL(KIND=dp), PARAMETER :: R_max
68 
69  ! Values of K:
70  ! INTEGER, PARAMETER, DIMENSION(n_approx) :: k_mm
71 
72  ! Values of Rc:
73  ! REAL(KIND=dp), PARAMETER, DIMENSION(n_approx) :: R_mm
74 
75  ! Values of minimax error:
76  ! REAL(KIND=dp), PARAMETER, DIMENSION(n_approx) :: err_mm
77 
78  ! Note: the coefficients (k_mm, R_mm, err_mm) are sorted w.r.t. 1) k_mm, 2) R_mm
79 
80  ! Given the ith value of K, k_p(i) points to the first minimax
81  ! approximation with K terms:
82  ! INTEGER, PARAMETER, DIMENSION(n_k+1) :: k_p
83 
84  ! Minimax coefficients aw of the ith minimax approximation:
85  ! SUBROUTINE get_minimax_coeff_low(i, aw)
86 
87 CONTAINS
88 
89 ! **************************************************************************************************
90 !> \brief Check that a minimax approximation is available for given input k, Rc.
91 !> ierr == 0: everything ok
92 !> ierr == 1: Rc too small
93 !> ierr == -1: k too large
94 !> \param k ...
95 !> \param Rc ...
96 !> \param ierr ...
97 !> \note: ierr == 1 is not a fatal error since get_exp_minimax_coeff will return
98 !> k53 minimax coefficients with smallest possible range.
99 ! **************************************************************************************************
100  SUBROUTINE check_exp_minimax_range(k, Rc, ierr)
101  INTEGER, INTENT(IN) :: k
102  REAL(kind=dp), INTENT(IN) :: rc
103  INTEGER, INTENT(OUT) :: ierr
104 
105  ierr = 0
106  IF (k .LE. 15) THEN
107  CALL check_range_k15(k, rc, ierr)
108  ELSE
109  IF (k .GT. k_max) ierr = -1
110  END IF
111 
112  END SUBROUTINE check_exp_minimax_range
113 
114 ! **************************************************************************************************
115 !> \brief Get best minimax approximation for given input parameters. Automatically
116 !> chooses the most exact set of minimax coefficients (k15 or k53) for
117 !> given k, Rc.
118 !> \param k Number of minimax terms
119 !> \param Rc Minimax range
120 !> \param aw The a_i and w_i coefficient are stored in aw such that the first 1:K
121 !> elements correspond to a_i and the K+1:2k correspond to w_i.
122 !> \param mm_error Numerical error of minimax approximation for given k, Rc
123 !> \param which_coeffs Whether the coefficients returned have been generated from
124 !> k15 or k53 coefficients (mm_k15 or mm_k53).
125 ! **************************************************************************************************
126  SUBROUTINE get_exp_minimax_coeff(k, Rc, aw, mm_error, which_coeffs)
127  INTEGER, INTENT(IN) :: k
128  REAL(kind=dp), INTENT(IN) :: rc
129  REAL(kind=dp), DIMENSION(2*k), INTENT(OUT) :: aw
130  REAL(kind=dp), INTENT(OUT), OPTIONAL :: mm_error
131  INTEGER, INTENT(OUT), OPTIONAL :: which_coeffs
132 
133  INTEGER :: ierr
134 
135  IF (k .LE. 15) THEN
136  CALL check_range_k15(k, rc, ierr)
137  IF (ierr .EQ. 1) THEN ! Rc too small for k15 coeffs --> use k53
138  CALL get_minimax_coeff_k53(k, rc, aw, mm_error)
139  IF (PRESENT(which_coeffs)) which_coeffs = mm_k53
140  ELSE
141  cpassert(ierr .EQ. 0)
142  CALL get_minimax_coeff_k15(k, rc, aw, mm_error)
143  IF (PRESENT(which_coeffs)) which_coeffs = mm_k15
144  END IF
145  ELSEIF (k .LE. 53) THEN
146  CALL get_minimax_coeff_k53(k, rc, aw, mm_error)
147  IF (PRESENT(which_coeffs)) which_coeffs = mm_k53
148  ELSE
149  cpabort("No minimax approximations available for k = "//cp_to_string(k))
150  END IF
151  END SUBROUTINE get_exp_minimax_coeff
152 
153 ! **************************************************************************************************
154 !> \brief Get minimax coefficients: k53 implementation (coefficients up to k=53 terms).
155 !> All a_i and w_i for a set of discrete values Rc, k are tabulated and
156 !> the most accurate coefficients for given input k, Rc are returned.
157 !> \param k ...
158 !> \param Rc ...
159 !> \param aw ...
160 !> \param mm_error ...
161 ! **************************************************************************************************
162  SUBROUTINE get_minimax_coeff_k53(k, Rc, aw, mm_error)
163  INTEGER, INTENT(IN) :: k
164  REAL(kind=dp), INTENT(IN) :: rc
165  REAL(kind=dp), DIMENSION(2*k), INTENT(OUT) :: aw
166  REAL(kind=dp), INTENT(OUT), OPTIONAL :: mm_error
167 
168  INTEGER :: i_mm
169 
170  CALL get_best_minimax_approx_k53(k, rc, i_mm)
171  CALL get_minimax_coeff_low(i_mm, aw)
172  IF (PRESENT(mm_error)) mm_error = get_minimax_numerical_error(rc, aw)
173 
174  END SUBROUTINE get_minimax_coeff_k53
175 
176 ! **************************************************************************************************
177 !> \brief find minimax approx. with k terms that is most accurate for range Rc.
178 !> \param k ...
179 !> \param Rc ...
180 !> \param i_mm ...
181 !> \param ge_Rc Whether the tabulated range of the returned minimax approximation
182 !> must be strictly greater than or equal to Rc. Default is .FALSE.
183 ! **************************************************************************************************
184  SUBROUTINE get_best_minimax_approx_k53(k, Rc, i_mm, ge_Rc)
185  INTEGER, INTENT(IN) :: k
186  REAL(kind=dp), INTENT(IN) :: rc
187  INTEGER, INTENT(OUT) :: i_mm
188  LOGICAL, INTENT(IN), OPTIONAL :: ge_rc
189 
190  INTEGER :: i, i_k, i_l, i_r
191  REAL(kind=dp) :: error_l, error_r, r_k_max, r_k_min
192  REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: aw
193 
194  cpassert(k .LE. k_max)
195 
196  ! find k pointer and smallest and largest R_mm value for this k
197  i_k = 1
198  DO WHILE (k_mm(k_p(i_k)) .LT. k)
199  i_k = i_k + 1
200  END DO
201  cpassert(k_mm(k_p(i_k)) .EQ. k)
202 
203  r_k_min = r_mm(k_p(i_k))
204  r_k_max = r_mm(k_p(i_k + 1) - 1)
205 
206  IF (rc .GE. r_k_max) THEN
207  i_mm = k_p(i_k + 1) - 1 ! pointer to largest Rc for current k
208  ELSE IF (rc .LE. r_k_min) THEN
209  i_mm = k_p(i_k) ! pointer to smallest Rc for current k
210  ELSE
211  i = k_p(i_k)
212  DO WHILE (rc .GT. r_mm(i))
213  i = i + 1
214  END DO
215  i_r = i ! pointer to closest R_mm >= Rc
216  i_l = i - 1 ! pointer to closest R_mm < Rc
217 
218  IF (PRESENT(ge_rc)) THEN
219  IF (ge_rc) THEN
220  i_mm = i_r
221  RETURN
222  END IF
223  END IF
224 
225  ALLOCATE (aw(2*k_mm(i_r)))
226  CALL get_minimax_coeff_low(i_r, aw)
227  error_l = get_minimax_numerical_error(rc, aw)
228  DEALLOCATE (aw)
229  ALLOCATE (aw(2*k_mm(i_l)))
230  CALL get_minimax_coeff_low(i_l, aw)
231  error_r = get_minimax_numerical_error(rc, aw)
232  DEALLOCATE (aw)
233  i_mm = merge(i_r, i_l, error_l .LE. error_r)
234  END IF
235 
236  END SUBROUTINE get_best_minimax_approx_k53
237 
238 ! **************************************************************************************************
239 !> \brief Unit test checking that numerical error of minimax approximations
240 !> generated using any k15 or k53 coefficients is consistent with
241 !> tabulated error.
242 !> \param n_R Number of Rc values to be tested.
243 !> \param iw ...
244 ! **************************************************************************************************
245  SUBROUTINE validate_exp_minimax(n_R, iw)
246  INTEGER, INTENT(IN) :: n_r, iw
247 
248  INTEGER :: i_mm, i_r, ierr, k, which_coeffs
249  LOGICAL :: do_exit
250  REAL(kind=dp) :: dr, mm_error, r, ref_error
251  REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: aw
252 
253  IF (iw > 0) THEN
254  WRITE (iw, '(//T2,A)') &
255  "Unit tests for minimax 1/x ~ SUM_{i}^{K} w_i EXP(-a_i * x) for x belonging to [1:Rc]"
256  WRITE (iw, '(T2,84("*"))')
257  END IF
258 
259  IF (iw > 0) THEN
260  WRITE (iw, '(/T2,A)') &
261  "1) checking numerical error against tabulated error at tabulated values Rc"
262  WRITE (iw, '(/T2,A)') &
263  "which coeffs, K, Rc, num. error, ref. error, rel. diff (num. error - ref. error)/(ref. error)"
264  WRITE (iw, '(T2,54("-"))')
265  END IF
266  DO i_mm = 1, n_approx
267  r = r_mm(i_mm)
268  k = k_mm(i_mm)
269  CALL check_exp_minimax_range(k, r, ierr)
270  IF (ierr .EQ. 0) THEN
271  ALLOCATE (aw(2*k))
272  CALL get_exp_minimax_coeff(k, r, aw, mm_error, which_coeffs)
273  ref_error = err_mm(i_mm)
274  DEALLOCATE (aw)
275  IF (iw > 0) WRITE (iw, '(T2,A4, I3, ES10.1, ES12.3, ES12.3, ES12.3)') &
276  merge("k15", "k53", which_coeffs .EQ. mm_k15), k, r, &
277  mm_error, ref_error, (mm_error - ref_error)/ref_error
278  cpassert(mm_error .LE. ref_error*1.05_dp + 1.0e-15_dp)
279  ELSE
280  IF (iw > 0) WRITE (iw, '(T2,A4, I3, ES10.1, 3X, A)') "k15", k, r, "missing"
281  END IF
282 
283  IF (k .LE. 15) THEN
284  ALLOCATE (aw(2*k))
285  CALL get_minimax_coeff_k53(k, r, aw, mm_error)
286  ref_error = err_mm(i_mm)
287  DEALLOCATE (aw)
288  IF (iw > 0) WRITE (iw, '(T2,A4,I3, ES10.1, ES12.3, ES12.3, ES12.3)') &
289  "k53", k, r, mm_error, ref_error, (mm_error - ref_error)/ref_error
290  IF (mm_error .GT. ref_error*1.05_dp + 1.0e-15_dp) THEN
291  cpabort("Test 1 failed: numerical error is larger than tabulated error")
292  END IF
293  END IF
294  END DO
295 
296  IF (iw > 0 .AND. n_r .GT. 0) THEN
297  WRITE (iw, '(T2,54("-"))')
298  WRITE (iw, '(/T2,A)') "Test 1 OK"
299  WRITE (iw, '(/T2,A)') &
300  "2) checking numerical error against tabulated error at arbitrary values Rc"
301  WRITE (iw, '(/T2,A)') &
302  "which coeffs, K, Rc, num. error, ref. error, rel. diff (num. error - ref. error)/(ref. error)"
303  WRITE (iw, '(T2,54("-"))')
304  END IF
305  dr = r_max**(1.0_dp/n_r)
306 
307  DO k = 1, k_max
308  ALLOCATE (aw(2*k))
309  do_exit = .false.
310  DO i_r = 1, n_r
311  r = dr**i_r
312  CALL get_exp_minimax_coeff(k, r, aw, mm_error, which_coeffs)
313  CALL get_best_minimax_approx_k53(k, r, i_mm, ge_rc=.true.)
314  IF (r .GT. r_mm(i_mm)) THEN
315  r = r_max
316  do_exit = .true.
317  END IF
318  ref_error = err_mm(i_mm)
319  IF (iw > 0) WRITE (iw, '(T2, A4, I3, ES10.1, ES12.3, ES12.3, ES12.3)') &
320  merge("k15", "k53", which_coeffs .EQ. mm_k15), k, r, &
321  mm_error, ref_error, (mm_error - ref_error)/ref_error
322  IF (mm_error .GT. ref_error*1.05_dp + 1.0e-15_dp) THEN
323  cpabort("Test 2 failed: numerical error is larger than tabulated error")
324  END IF
325  IF (do_exit) EXIT
326  END DO
327  DEALLOCATE (aw)
328  END DO
329  IF (iw > 0) THEN
330  WRITE (iw, '(T2,54("-"))')
331  WRITE (iw, '(/T2,A)') "Test 2 OK"
332  END IF
333  END SUBROUTINE validate_exp_minimax
334 
335 END MODULE minimax_exp
various routines to log and control the output. The idea is that decisions about where to log should ...
Defines the basic variable types.
Definition: kinds.F:23
integer, parameter, public dp
Definition: kinds.F:34
Routines to calculate the minimax coefficients in order to approximate 1/x as a sum over exponential ...
subroutine, public get_minimax_coeff_k15(k, Rc, aw, mm_error)
Get minimax coefficients: k15 implementation (coefficients up to k=15 terms). All a_i and w_i have be...
subroutine, public check_range_k15(k, Rc, ierr)
Check that the range for the minimax approximation is not too small for the chosen number of integrat...
real(kind=dp) function, public get_minimax_numerical_error(Rc, aw)
Sample numerical error and return its maximum.
Routines to calculate the minimax coefficients in order to approximate 1/x as a sum over exponential ...
subroutine, public get_minimax_coeff_low(i, aw)
...
integer, parameter, public k_max
integer, dimension(n_approx), parameter, public k_mm
real(kind=dp), parameter, public r_max
integer, parameter, public n_approx
real(kind=dp), dimension(n_approx), parameter, public r_mm
integer, dimension(n_k+1), parameter, public k_p
real(kind=dp), dimension(n_approx), parameter, public err_mm
Routines to calculate the minimax coefficients in order to approximate 1/x as a sum over exponential ...
Definition: minimax_exp.F:29
subroutine, public get_exp_minimax_coeff(k, Rc, aw, mm_error, which_coeffs)
Get best minimax approximation for given input parameters. Automatically chooses the most exact set o...
Definition: minimax_exp.F:127
subroutine, public check_exp_minimax_range(k, Rc, ierr)
Check that a minimax approximation is available for given input k, Rc. ierr == 0: everything ok ierr ...
Definition: minimax_exp.F:101
subroutine, public validate_exp_minimax(n_R, iw)
Unit test checking that numerical error of minimax approximations generated using any k15 or k53 coef...
Definition: minimax_exp.F:246