(git:374b731)
Loading...
Searching...
No Matches
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
31 USE kinds, ONLY: dp
35 USE minimax_exp_k53, ONLY: r_max,&
36 r_mm,&
37 err_mm,&
39 k_max,&
40 k_mm,&
41 k_p,&
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
87CONTAINS
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
335END 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 ...
real(kind=dp) function, public get_minimax_numerical_error(rc, aw)
Sample numerical error and return its maximum.
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...
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...
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 check_exp_minimax_range(k, rc, ierr)
Check that a minimax approximation is available for given input k, Rc. ierr == 0: everything ok ierr ...
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...
subroutine, public validate_exp_minimax(n_r, iw)
Unit test checking that numerical error of minimax approximations generated using any k15 or k53 coef...