(git:97501a3)
Loading...
Searching...
No Matches
erf_complex.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2025 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! Copyright (c) 2016 Anton Shterenlikht, The University of Bristol, UK
9!
10! Redistribution and use in source and binary forms, with or without
11! modification, are permitted provided that the following conditions are
12! met:
13!
14! 1. Redistributions of source code must retain the above copyright
15! notice, this list of conditions and the following disclaimer.
16!
17! 2. Redistributions in binary form must reproduce the above copyright
18! notice, this list of conditions and the following disclaimer in the
19! documentation and/or other materials provided with the distribution.
20!
21! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22! IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23! TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24! PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25! HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27! TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28! PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29! LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30! NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31! SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32!
33! Module with error function and related functions.
34! Two algorithms are implemented: Poppe and Wijers (faddeyeva_fast, erfz_fast)
35! and Zaghloul and Ali (faddeyeva_accurate, erfz_accurate). The second algorithm is
36! supposed to be more accurate for some values. However, the first
37! algorithm can be over an order of magnitude faster.
38!
39! This code was written before the author became aware of
40! M. R. Zaghloul, Remark on "Algorithm 916: Computing the
41! Faddeyeva and Voight functions": efficiency improvements and
42! Fortran translation, ACM Trans. Math. Software 42, Article 26, 2016.
43
44! **************************************************************************************************
45!> \brief Module to compute the error function of a complex argument
46!> \par History
47!> 08.2025 Adapted to use CP2K intrinsics and constants
48!> \author Stefano Battaglia
49! **************************************************************************************************
51
52 USE kinds, ONLY: dp
53 USE mathconstants, ONLY: pi
54
55 IMPLICIT NONE
56
57 REAL(kind=dp), PARAMETER :: zero = 0.0_dp, one = 1.0_dp, &
58 two = 2.0_dp, half = 0.5_dp, rmin = tiny(one), &
59 eps0 = epsilon(one), sqrt_log_rmin = sqrt(-log(rmin)), &
60 pi2 = pi*pi, one_sqrt_pi = one/sqrt(pi)
61 COMPLEX(kind=dp), PARAMETER :: &
62 cmplxj = cmplx(zero, one, kind=dp), &
63 cmplx0 = cmplx(zero, zero, kind=dp)
64
65 PRIVATE
67
68CONTAINS
69
70! **************************************************************************************************
71!> \brief Computes the Faddeyeva function w(z) = exp(-z**2) * erfc(-i*z)
72!> \param z complex argument
73!> \param err desired accuracy (positive)
74!> \return Faddeyeva function w(z)
75! **************************************************************************************************
76 elemental COMPLEX(kind=dp) FUNCTION faddeyeva_accurate(z, err)
77
78 ! This is the Faddeyeva or the plasma dispersion function,
79 ! w(z) = exp(-z**2) * erfc(-i*z). erfc(z) is the complex complementary
80 ! error function of z. z is a complex number.
81 !
82 ! Adapted from the Matlab code implementing TOMS
83 ! Algorithm 916: http://www.netlib.org/toms/
84 !
85 ! file: 916.zip
86 ! ref: TOMS 38,2 (Dec 2011) Article: 15
87 ! for: Computing the Faddeyeva and Voigt Functions
88 ! by: Mofreh R. Zaghloul and Ahmed N. Ali
89 !
90 ! Most of the code is calculation of equations (13)-(19) of the
91 ! above paper.
92 !
93 ! Inputs:
94 ! z - the argument of the function
95 ! err - The desired accuracy (positive). err must be (err .le. 1.0e-4)
96 ! and (err .ge. 0.06447*epsilon). For efficiency,
97 ! no checks are made!
98 ! The lowest accuracy and the fastest calculation are obtained
99 ! with err .eq. 1.0e-4. For higher accuracy use smaller err.
100
101 COMPLEX(kind=dp), INTENT(in) :: z
102 REAL(kind=dp), INTENT(in) :: err
103
104 INTEGER :: n, n3, n3_3
105 REAL(kind=dp) :: a, a_pi, a_sqr, aux13, cos_2yx, del2_tmp, del3, del3_3_tmp, del3_tmp, del5, &
106 delta3, delta5, den1, erfcsy, exp1, exp2, exp3, exp3_3_den, exp3_den, exp_del1, &
107 exp_x_sqr, four_a_sqr, half_a, l_old, myerr, sigma1, sigma2, sigma3, sigma4, sigma4_5, &
108 sigma5, sin_2yx, two_a, two_a_pi, two_a_sqr, two_a_x, two_exp_x_sqr_ysqr, two_yx, v_old, &
109 x, x_sqr, xsign, y, y_sqr, ysign
110
111 x = real(z)
112 y = aimag(z)
113
114 ! For purely imaginary z, use intrinsic scaled complement of
115 ! the error function, erfc_scaled (F2008 and beyond).
116 ! Return immediately.
117 IF (abs(x) .EQ. zero) THEN
118 faddeyeva_accurate = erfc_scaled(y)
119 RETURN
120 END IF
121
122 myerr = max(err, eps0)
123 a = sqrt(-pi2/log(err/2.0_dp))
124 half_a = half*a
125 a_sqr = a**2
126 two_a = 2*a
127 two_a_sqr = 2*a_sqr
128 four_a_sqr = 4*a_sqr
129 a_pi = a/pi
130 two_a_pi = 2*a_pi
131 erfcsy = erfc_scaled(abs(y))
132 xsign = sign(one, x)
133 ysign = sign(one, y)
134 x = abs(x)
135 y = max(rmin, abs(y))
136 x_sqr = x**2
137 y_sqr = y**2
138 two_yx = 2*y*x
139 two_a_x = two_a*x
140 exp_x_sqr = exp(-x_sqr)
141 cos_2yx = cos(two_yx)
142 sin_2yx = sin(two_yx)
143 v_old = exp_x_sqr* &
144 (erfcsy*cos_2yx + two_a_pi*sin(two_yx/2)**2/y)
145 l_old = -erfcsy + a_pi/y
146 sigma3 = rmin
147 sigma5 = rmin
148 sigma4_5 = zero
149 delta3 = one
150 delta5 = one
151 n = 0
152 n3 = ceiling(x/a)
153 n3_3 = n3 - 1
154
155 outer: IF ((sqrt_log_rmin - x) .GT. 0) THEN
156 sigma1 = rmin
157 sigma2 = rmin
158 sigma4 = rmin
159 exp1 = exp(-two_a_x)
160 exp2 = exp(four_a_sqr*n3 - 2*two_a_x - two_a_sqr)
161 exp3 = exp(-(two_a_sqr*n3 - two_a_x - two_a_sqr))
162 del2_tmp = one
163 del3_tmp = exp(-(a_sqr*n3**2 - two_a_x*n3 &
164 - two_a_sqr*n3 + x_sqr + two_a_x + a_sqr))
165 del3_3_tmp = exp(a_sqr - (two_a_sqr*n3 - two_a_x))
166
167 loop1: DO
168 IF (delta3 .LT. myerr .AND. delta5 .LT. myerr .AND. &
169 n .GT. 50) EXIT loop1
170 n = n + 1
171 den1 = a_sqr*n**2 + y_sqr
172 exp_del1 = exp(-(a_sqr*n**2))/den1
173 del2_tmp = del2_tmp*exp1
174
175 minor: IF (n3_3 .GE. 1) THEN
176 del3_tmp = del3_tmp*exp3
177 exp3_den = del3_tmp*exp_del1* &
178 (den1/(a_sqr*n3**2 + y_sqr))
179 del3_3_tmp = del3_3_tmp*exp2
180 exp3_3_den = exp3_den*del3_3_tmp* &
181 ((a_sqr*n3**2 + y_sqr)/ &
182 (a_sqr*n3_3**2 + y_sqr))
183 del5 = n3_3*exp3_3_den + n3*exp3_den
184 del3 = exp3_3_den + exp3_den
185 ELSE
186 del3_tmp = del3_tmp*exp3
187 del3 = del3_tmp*exp_del1* &
188 (den1/(a_sqr*n3**2 + y_sqr))
189 del5 = n3*del3
190 END IF minor
191
192 delta3 = del3/sigma3
193 delta5 = del5/sigma5
194 sigma1 = sigma1 + exp_del1
195 sigma2 = sigma2 + del2_tmp*exp_x_sqr*exp_del1
196 sigma3 = sigma3 + del3
197 sigma4 = sigma4 + n*del2_tmp*exp_x_sqr*exp_del1
198 sigma5 = sigma5 + del5
199
200 IF (x .GE. 5.0e-4_dp) THEN
201 sigma4_5 = -sigma4 + sigma5
202 ELSE
203 sigma4_5 = sigma4_5 + 2*n**2*two_a_x*exp_x_sqr &
204 *exp_del1*(one + 1.666666666666667e-1_dp &
205 *(two_a_x*n)**2 + 8.333333333333333e-3_dp &
206 *(two_a_x*n)**4)
207 END IF
208
209 n3 = n3 + 1
210 n3_3 = n3_3 - 1
211
212 END DO loop1
213
214 ! Second line of Eqn (13)
215 aux13 = y*two_a_pi* &
216 (-cos_2yx*exp_x_sqr*sigma1 + half*(sigma2 + sigma3))
217 mumu: IF (y .LE. 5.0_dp .AND. two_yx .GT. rmin) THEN
218 faddeyeva_accurate = v_old + aux13 + cmplxj*xsign &
219 *(sin_2yx*exp_x_sqr*(l_old + two_a_pi*y*sigma1) &
220 + two_a_pi*half_a*sigma4_5)
221 ELSE IF (y .LE. 5.0_dp .AND. two_yx .LE. rmin) THEN
222 faddeyeva_accurate = v_old + aux13 + cmplxj*xsign &
223 *(two*y*exp_x_sqr*(x*l_old + x*two_a_pi*y &
224 *sigma1) + two_a_pi*half_a*sigma4_5)
225 ELSE
226 faddeyeva_accurate = v_old + aux13 + cmplxj*xsign &
227 *(sin_2yx*exp_x_sqr*min(zero, abs(l_old &
228 + (two_a_pi*y*sigma1))) + two_a_pi*half_a &
229 *sigma4_5)
230 END IF mumu
231
232 ELSE IF (x .GE. sqrt_log_rmin .AND. x .LT. 1.0e15_dp) THEN
233
234 exp2 = exp(four_a_sqr*n3 - 2*two_a_x - two_a_sqr)
235 del3_3_tmp = exp(a_sqr + two_a_x - two_a_sqr*n3)
236
237 loop2: DO
238 IF (delta3 .LT. myerr .AND. delta5 .LT. myerr .AND. &
239 n .GT. 50) EXIT loop2
240 n = n + 1
241 IF (n3_3 .GE. 1) THEN
242 exp3_den = exp(-(a*n3 - x)*(a*n3 - x)) &
243 /(a_sqr*n3**2 + y_sqr)
244 del3_3_tmp = del3_3_tmp*exp2
245 exp3_3_den = exp3_den*del3_3_tmp*((a_sqr*n3**2 + y_sqr) &
246 /(a_sqr*n3_3**2 + y_sqr))
247 del5 = n3_3*exp3_3_den + n3*exp3_den
248 del3 = exp3_3_den + exp3_den
249 ELSE
250 del3 = exp(-(a*n3 - x)**2)/(a_sqr*n3**2 + y_sqr)
251 del5 = n3*del3
252 END IF
253
254 delta3 = del3/sigma3
255 delta5 = del5/sigma5
256 sigma3 = sigma3 + del3
257 sigma5 = sigma5 + del5
258 n3 = n3 + 1
259 n3_3 = n3_3 - 1
260
261 END DO loop2
262
263 faddeyeva_accurate = v_old + y*a_pi*sigma3 + cmplxj*xsign*(sin_2yx &
264 *exp_x_sqr*l_old + two_a_pi*half_a*sigma5)
265
266 ELSE
267 faddeyeva_accurate = one_sqrt_pi*((y + cmplxj*xsign*x)/(x_sqr + y_sqr))
268 END IF outer
269
270 IF (ysign .LT. zero) THEN
271 two_exp_x_sqr_ysqr = two*exp(-x_sqr + y_sqr)
272 faddeyeva_accurate = two_exp_x_sqr_ysqr*cos_2yx - real(faddeyeva_accurate) - cmplxj &
273 *(-xsign*two_exp_x_sqr_ysqr*sin_2yx - aimag(faddeyeva_accurate))
274 END IF
275
276 END FUNCTION faddeyeva_accurate
277
278! **************************************************************************************************
279!> \brief Computes the error function of a complex argument using the Zaghloul and Ali algorithm
280!> \param z complex argument
281!> \param err desired accuracy (positive)
282!> \return error function of z
283! **************************************************************************************************
284 elemental COMPLEX(kind=dp) FUNCTION erfz_accurate(z, err)
285
286 ! This is an error function of a complex argument, which uses faddeyeva_accurate(z).
287
288 COMPLEX(kind=dp), INTENT(in) :: z
289 REAL(kind=dp), INTENT(in) :: err
290
291 erfz_accurate = one - faddeyeva_accurate(cmplxj*z, err)*exp(-z**2)
292
293 END FUNCTION erfz_accurate
294
295! **************************************************************************************************
296!> \brief Computes the Faddeyeva function w(z) = exp(-z**2) * erfc(-i*z)
297!> \param z complex argument
298!> \return Faddeyeva function w(z)
299! **************************************************************************************************
300 elemental COMPLEX(kind=dp) FUNCTION faddeyeva_fast(z)
301
302 ! A modified version of algorithm 680, rewritten in Fortran 2008.
303 ! G.P.M. Poppe, C.M.J. Wijers, More efficient computation of
304 ! the complex error-function, ACM Trans. Math. Software 16:38-46, 1990.
305 ! and
306 ! G.P.M. Poppe, C.M.J. Wijers, Algorithm 680, Evaluation of the
307 ! complex error function, ACM Trans. Math. Software 16:47, 1990.
308 !
309 ! Given a complex number z, this function computes
310 ! the value of the Faddeeva-function w(z) = exp(-z**2)*erfc(-i*z),
311 ! where erfc is the complex complementary error-function and i
312 ! means sqrt(-1). The accuracy of the algorithm for z in the 1st
313 ! and 2nd quadrant is 14 significant digits; in the 3rd and 4th
314 ! it is 13 significant digits outside a circular region with radius
315 ! 0.126 around a zero of the function.
316
317 COMPLEX(kind=dp), INTENT(in) :: z
318
319 REAL(kind=dp), PARAMETER :: factor = 1.12837916709551257388_dp
320
321 INTEGER :: i, j, kapn, n, np1, nu
322 LOGICAL :: a, b
323 REAL(kind=dp) :: c, daux, h, h2, qlambda, qrho, rx, ry, &
324 sx, sy, tx, ty, u, u1, u2, v, v1, v2, &
325 w1, x, xabs, xabsq, xaux, xi, xquad, &
326 xsum, y, yabs, yi, yquad, ysum
327
328 ! factor is 2/sqrt(pi)
329
330 ! To avoid the complier uninitialised varning
331 h2 = zero
332
333 xi = real(z)
334 yi = aimag(z)
335 xabs = abs(xi)
336 yabs = abs(yi)
337 x = xabs/6.3_dp
338 y = yabs/4.4_dp
339 qrho = x**2 + y**2
340 xabsq = xabs**2
341 xquad = xabsq - yabs**2
342 yquad = 2*xabs*yabs
343
344 a = qrho .LT. 0.085264_dp
345
346 branch1: IF (a) THEN
347
348 ! If ( qrho .lt. 0.085264 ) then the Faddeeva-function is evaluated
349 ! using a power-series (abramowitz/stegun, equation (7.1.5), p.297)
350 ! n is the minimum number of terms needed to obtain the required
351 ! accuracy
352
353 qrho = (one - 0.85_dp*y)*sqrt(qrho)
354 n = nint(6.0_dp + 72.0_dp*qrho)
355 j = 2*n + 1
356 xsum = one/real(j, kind=dp)
357 ysum = zero
358
359 DO i = n, 1, -1
360 j = j - 2
361 xaux = (xsum*xquad - ysum*yquad)/real(i, kind=dp)
362 ysum = (xsum*yquad + ysum*xquad)/real(i, kind=dp)
363 xsum = xaux + one/real(j, kind=dp)
364 END DO
365
366 u1 = -factor*(xsum*yabs + ysum*xabs) + one
367 v1 = factor*(xsum*xabs - ysum*yabs)
368 daux = exp(-xquad)
369 u2 = daux*cos(yquad)
370 v2 = -daux*sin(yquad)
371 u = u1*u2 - v1*v2
372 v = u1*v2 + v1*u2
373 ELSE
374
375 bran2: IF (qrho .GT. one) THEN
376
377 ! If ( qrho .gt. 1) then w(z) is evaluated using the laplace
378 ! continued fraction. nu is the minimum number of terms needed
379 ! to obtain the required accuracy.
380
381 h = zero
382 kapn = 0
383 qrho = sqrt(qrho)
384 nu = int(3.0_dp + (1442.0_dp/(26.0_dp*qrho &
385 + 77.0_dp)))
386
387 ELSE
388
389 ! If ( qrho .ge. 0.085264 .and. qrho .le. one ) then
390 ! w(z) is evaluated by a truncated Taylor expansion,
391 ! where the Laplace continued fraction is used to calculate
392 ! the derivatives of w(z). KAPN is the minimum number of terms
393 ! in the Taylor expansion needed to obtain the required accuracy.
394 ! NU is the minimum number of terms of the continued fraction
395 ! needed to calculate the derivatives with the required accuracy.
396
397 qrho = (one - y)*sqrt(one - qrho)
398 h = 1.88_dp*qrho
399 h2 = two*h
400 kapn = nint(7.0_dp + 34.0_dp*qrho)
401 nu = nint(16.0_dp + 26.0_dp*qrho)
402
403 END IF bran2
404
405 b = h .GT. zero
406
407 ! To avoid uninitialise compiler warning. qlambda is used
408 ! only if (b), so can define to any value otherwise.
409 qlambda = zero
410 IF (b) qlambda = h2**kapn
411
412 rx = zero
413 ry = zero
414 sx = zero
415 sy = zero
416
417 DO n = nu, 0, -1
418 np1 = n + 1
419 tx = yabs + h + np1*rx
420 ty = xabs - np1*ry
421 c = half/(tx**2 + ty**2)
422 rx = c*tx
423 ry = c*ty
424 IF (b .AND. n .LE. kapn) THEN
425 tx = qlambda + sx
426 sx = rx*tx - ry*sy
427 sy = ry*tx + rx*sy
428 qlambda = qlambda/h2
429 END IF
430 END DO
431
432 IF (h .EQ. zero) THEN
433 u = factor*rx
434 v = factor*ry
435 ELSE
436 u = factor*sx
437 v = factor*sy
438 END IF
439
440 IF (yabs .EQ. zero) u = exp(-xabs**2)
441
442 END IF branch1
443
444 ! Evaluation of w(z) in the other quadrants
445
446 IF (yi .LT. zero) THEN
447
448 IF (a) THEN
449 u2 = two*u2
450 v2 = two*v2
451 ELSE
452 xquad = -xquad
453 w1 = two*exp(xquad)
454 u2 = w1*cos(yquad)
455 v2 = -w1*sin(yquad)
456 END IF
457
458 u = u2 - u
459 v = v2 - v
460 IF (xi .GT. zero) v = -v
461 ELSE
462 IF (xi .LT. zero) v = -v
463 END IF
464
465 faddeyeva_fast = cmplx(u, v, kind=dp)
466
467 END FUNCTION faddeyeva_fast
468
469! **************************************************************************************************
470!> \brief Computes the error function of a complex argument using the Poppe and Wijers algorithm
471!> \param z complex argument
472!> \return error function of z
473! **************************************************************************************************
474 elemental COMPLEX(kind=dp) FUNCTION erfz_fast(z)
475
476 ! This is an error function of a complex argument, which uses faddeyeva_fast(z).
477
478 COMPLEX(kind=dp), INTENT(in) :: z
479
480 erfz_fast = one - faddeyeva_fast(cmplxj*z)*exp(-z**2)
481
482 END FUNCTION erfz_fast
483
484 !*********************************************************************
485END MODULE erf_complex
Module to compute the error function of a complex argument.
Definition erf_complex.F:50
real(kind=dp), parameter half
Definition erf_complex.F:57
complex(kind=dp), parameter cmplx0
Definition erf_complex.F:61
elemental complex(kind=dp) function, public erfz_fast(z)
Computes the error function of a complex argument using the Poppe and Wijers algorithm.
elemental complex(kind=dp) function, public faddeyeva_accurate(z, err)
Computes the Faddeyeva function w(z) = exp(-z**2) * erfc(-i*z)
Definition erf_complex.F:77
real(kind=dp), parameter eps0
Definition erf_complex.F:57
real(kind=dp), parameter sqrt_log_rmin
Definition erf_complex.F:57
real(kind=dp), parameter two
Definition erf_complex.F:57
real(kind=dp), parameter one
Definition erf_complex.F:57
real(kind=dp), parameter rmin
Definition erf_complex.F:57
elemental complex(kind=dp) function, public erfz_accurate(z, err)
Computes the error function of a complex argument using the Zaghloul and Ali algorithm.
real(kind=dp), parameter zero
Definition erf_complex.F:57
real(kind=dp), parameter pi2
Definition erf_complex.F:57
real(kind=dp), parameter one_sqrt_pi
Definition erf_complex.F:57
complex(kind=dp), parameter cmplxj
Definition erf_complex.F:61
elemental complex(kind=dp) function, public faddeyeva_fast(z)
Computes the Faddeyeva function w(z) = exp(-z**2) * erfc(-i*z)
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Definition of mathematical constants and functions.
real(kind=dp), parameter, public pi