(git:9ccafda)
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-2026 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: half,&
54 one,&
55 oorootpi,&
56 pi,&
57 two,&
58 zero
59
60 IMPLICIT NONE
61
62 REAL(kind=dp), PARAMETER :: rmin = tiny(one), eps0 = epsilon(one), &
63 sqrt_log_rmin = sqrt(-log(rmin)), &
64 pi2 = pi*pi
65 COMPLEX(kind=dp), PARAMETER :: &
66 cmplxj = cmplx(zero, one, kind=dp), &
67 cmplx0 = cmplx(zero, zero, kind=dp)
68
69 PRIVATE
71
72CONTAINS
73
74! **************************************************************************************************
75!> \brief Computes the Faddeyeva function w(z) = exp(-z**2) * erfc(-i*z)
76!> \param z complex argument
77!> \param err desired accuracy (positive)
78!> \return Faddeyeva function w(z)
79! **************************************************************************************************
80 elemental COMPLEX(kind=dp) FUNCTION faddeyeva_accurate(z, err)
81
82 ! This is the Faddeyeva or the plasma dispersion function,
83 ! w(z) = exp(-z**2) * erfc(-i*z). erfc(z) is the complex complementary
84 ! error function of z. z is a complex number.
85 !
86 ! Adapted from the Matlab code implementing TOMS
87 ! Algorithm 916: http://www.netlib.org/toms/
88 !
89 ! file: 916.zip
90 ! ref: TOMS 38,2 (Dec 2011) Article: 15
91 ! for: Computing the Faddeyeva and Voigt Functions
92 ! by: Mofreh R. Zaghloul and Ahmed N. Ali
93 !
94 ! Most of the code is calculation of equations (13)-(19) of the
95 ! above paper.
96 !
97 ! Inputs:
98 ! z - the argument of the function
99 ! err - The desired accuracy (positive). err must be (err .le. 1.0e-4)
100 ! and (err .ge. 0.06447*epsilon). For efficiency,
101 ! no checks are made!
102 ! The lowest accuracy and the fastest calculation are obtained
103 ! with err .eq. 1.0e-4. For higher accuracy use smaller err.
104
105 COMPLEX(kind=dp), INTENT(in) :: z
106 REAL(kind=dp), INTENT(in) :: err
107
108 INTEGER :: n, n3, n3_3
109 REAL(kind=dp) :: a, a_pi, a_sqr, aux13, cos_2yx, del2_tmp, del3, del3_3_tmp, del3_tmp, del5, &
110 delta3, delta5, den1, erfcsy, exp1, exp2, exp3, exp3_3_den, exp3_den, exp_del1, &
111 exp_x_sqr, four_a_sqr, half_a, l_old, myerr, sigma1, sigma2, sigma3, sigma4, sigma4_5, &
112 sigma5, sin_2yx, two_a, two_a_pi, two_a_sqr, two_a_x, two_exp_x_sqr_ysqr, two_yx, v_old, &
113 x, x_sqr, xsign, y, y_sqr, ysign
114
115 x = real(z)
116 y = aimag(z)
117
118 ! For purely imaginary z, use intrinsic scaled complement of
119 ! the error function, erfc_scaled (F2008 and beyond).
120 ! Return immediately.
121 IF (abs(x) == zero) THEN
122 faddeyeva_accurate = erfc_scaled(y)
123 RETURN
124 END IF
125
126 myerr = max(err, eps0)
127 a = sqrt(-pi2/log(err/2.0_dp))
128 half_a = half*a
129 a_sqr = a**2
130 two_a = 2*a
131 two_a_sqr = 2*a_sqr
132 four_a_sqr = 4*a_sqr
133 a_pi = a/pi
134 two_a_pi = 2*a_pi
135 erfcsy = erfc_scaled(abs(y))
136 xsign = sign(one, x)
137 ysign = sign(one, y)
138 x = abs(x)
139 y = max(rmin, abs(y))
140 x_sqr = x**2
141 y_sqr = y**2
142 two_yx = 2*y*x
143 two_a_x = two_a*x
144 exp_x_sqr = exp(-x_sqr)
145 cos_2yx = cos(two_yx)
146 sin_2yx = sin(two_yx)
147 v_old = exp_x_sqr* &
148 (erfcsy*cos_2yx + two_a_pi*sin(two_yx/2)**2/y)
149 l_old = -erfcsy + a_pi/y
150 sigma3 = rmin
151 sigma5 = rmin
152 sigma4_5 = zero
153 delta3 = one
154 delta5 = one
155 n = 0
156 n3 = ceiling(x/a)
157 n3_3 = n3 - 1
158
159 outer: IF ((sqrt_log_rmin - x) > 0) THEN
160 sigma1 = rmin
161 sigma2 = rmin
162 sigma4 = rmin
163 exp1 = exp(-two_a_x)
164 exp2 = exp(four_a_sqr*n3 - 2*two_a_x - two_a_sqr)
165 exp3 = exp(-(two_a_sqr*n3 - two_a_x - two_a_sqr))
166 del2_tmp = one
167 del3_tmp = exp(-(a_sqr*n3**2 - two_a_x*n3 &
168 - two_a_sqr*n3 + x_sqr + two_a_x + a_sqr))
169 del3_3_tmp = exp(a_sqr - (two_a_sqr*n3 - two_a_x))
170
171 loop1: DO
172 IF (delta3 < myerr .AND. delta5 < myerr .AND. &
173 n > 50) EXIT loop1
174 n = n + 1
175 den1 = a_sqr*n**2 + y_sqr
176 exp_del1 = exp(-(a_sqr*n**2))/den1
177 del2_tmp = del2_tmp*exp1
178
179 minor: IF (n3_3 >= 1) THEN
180 del3_tmp = del3_tmp*exp3
181 exp3_den = del3_tmp*exp_del1* &
182 (den1/(a_sqr*n3**2 + y_sqr))
183 del3_3_tmp = del3_3_tmp*exp2
184 exp3_3_den = exp3_den*del3_3_tmp* &
185 ((a_sqr*n3**2 + y_sqr)/ &
186 (a_sqr*n3_3**2 + y_sqr))
187 del5 = n3_3*exp3_3_den + n3*exp3_den
188 del3 = exp3_3_den + exp3_den
189 ELSE
190 del3_tmp = del3_tmp*exp3
191 del3 = del3_tmp*exp_del1* &
192 (den1/(a_sqr*n3**2 + y_sqr))
193 del5 = n3*del3
194 END IF minor
195
196 delta3 = del3/sigma3
197 delta5 = del5/sigma5
198 sigma1 = sigma1 + exp_del1
199 sigma2 = sigma2 + del2_tmp*exp_x_sqr*exp_del1
200 sigma3 = sigma3 + del3
201 sigma4 = sigma4 + n*del2_tmp*exp_x_sqr*exp_del1
202 sigma5 = sigma5 + del5
203
204 IF (x >= 5.0e-4_dp) THEN
205 sigma4_5 = -sigma4 + sigma5
206 ELSE
207 sigma4_5 = sigma4_5 + 2*n**2*two_a_x*exp_x_sqr &
208 *exp_del1*(one + 1.666666666666667e-1_dp &
209 *(two_a_x*n)**2 + 8.333333333333333e-3_dp &
210 *(two_a_x*n)**4)
211 END IF
212
213 n3 = n3 + 1
214 n3_3 = n3_3 - 1
215
216 END DO loop1
217
218 ! Second line of Eqn (13)
219 aux13 = y*two_a_pi* &
220 (-cos_2yx*exp_x_sqr*sigma1 + half*(sigma2 + sigma3))
221 mumu: IF (y <= 5.0_dp .AND. two_yx > rmin) THEN
222 faddeyeva_accurate = v_old + aux13 + cmplxj*xsign &
223 *(sin_2yx*exp_x_sqr*(l_old + two_a_pi*y*sigma1) &
224 + two_a_pi*half_a*sigma4_5)
225 ELSE IF (y <= 5.0_dp .AND. two_yx <= rmin) THEN
226 faddeyeva_accurate = v_old + aux13 + cmplxj*xsign &
227 *(two*y*exp_x_sqr*(x*l_old + x*two_a_pi*y &
228 *sigma1) + two_a_pi*half_a*sigma4_5)
229 ELSE
230 faddeyeva_accurate = v_old + aux13 + cmplxj*xsign &
231 *(sin_2yx*exp_x_sqr*min(zero, abs(l_old &
232 + (two_a_pi*y*sigma1))) + two_a_pi*half_a &
233 *sigma4_5)
234 END IF mumu
235
236 ELSE IF (x >= sqrt_log_rmin .AND. x < 1.0e15_dp) THEN
237
238 exp2 = exp(four_a_sqr*n3 - 2*two_a_x - two_a_sqr)
239 del3_3_tmp = exp(a_sqr + two_a_x - two_a_sqr*n3)
240
241 loop2: DO
242 IF (delta3 < myerr .AND. delta5 < myerr .AND. &
243 n > 50) EXIT loop2
244 n = n + 1
245 IF (n3_3 >= 1) THEN
246 exp3_den = exp(-(a*n3 - x)*(a*n3 - x)) &
247 /(a_sqr*n3**2 + y_sqr)
248 del3_3_tmp = del3_3_tmp*exp2
249 exp3_3_den = exp3_den*del3_3_tmp*((a_sqr*n3**2 + y_sqr) &
250 /(a_sqr*n3_3**2 + y_sqr))
251 del5 = n3_3*exp3_3_den + n3*exp3_den
252 del3 = exp3_3_den + exp3_den
253 ELSE
254 del3 = exp(-(a*n3 - x)**2)/(a_sqr*n3**2 + y_sqr)
255 del5 = n3*del3
256 END IF
257
258 delta3 = del3/sigma3
259 delta5 = del5/sigma5
260 sigma3 = sigma3 + del3
261 sigma5 = sigma5 + del5
262 n3 = n3 + 1
263 n3_3 = n3_3 - 1
264
265 END DO loop2
266
267 faddeyeva_accurate = v_old + y*a_pi*sigma3 + cmplxj*xsign*(sin_2yx &
268 *exp_x_sqr*l_old + two_a_pi*half_a*sigma5)
269
270 ELSE
271 faddeyeva_accurate = oorootpi*((y + cmplxj*xsign*x)/(x_sqr + y_sqr))
272 END IF outer
273
274 IF (ysign < zero) THEN
275 two_exp_x_sqr_ysqr = two*exp(-x_sqr + y_sqr)
276 faddeyeva_accurate = two_exp_x_sqr_ysqr*cos_2yx - real(faddeyeva_accurate) - cmplxj &
277 *(-xsign*two_exp_x_sqr_ysqr*sin_2yx - aimag(faddeyeva_accurate))
278 END IF
279
280 END FUNCTION faddeyeva_accurate
281
282! **************************************************************************************************
283!> \brief Computes the error function of a complex argument using the Zaghloul and Ali algorithm
284!> \param z complex argument
285!> \param err desired accuracy (positive)
286!> \return error function of z
287! **************************************************************************************************
288 elemental COMPLEX(kind=dp) FUNCTION erfz_accurate(z, err)
289
290 ! This is an error function of a complex argument, which uses faddeyeva_accurate(z).
291
292 COMPLEX(kind=dp), INTENT(in) :: z
293 REAL(kind=dp), INTENT(in) :: err
294
295 erfz_accurate = one - faddeyeva_accurate(cmplxj*z, err)*exp(-z**2)
296
297 END FUNCTION erfz_accurate
298
299! **************************************************************************************************
300!> \brief Computes the Faddeyeva function w(z) = exp(-z**2) * erfc(-i*z)
301!> \param z complex argument
302!> \return Faddeyeva function w(z)
303! **************************************************************************************************
304 elemental COMPLEX(kind=dp) FUNCTION faddeyeva_fast(z)
305
306 ! A modified version of algorithm 680, rewritten in Fortran 2008.
307 ! G.P.M. Poppe, C.M.J. Wijers, More efficient computation of
308 ! the complex error-function, ACM Trans. Math. Software 16:38-46, 1990.
309 ! and
310 ! G.P.M. Poppe, C.M.J. Wijers, Algorithm 680, Evaluation of the
311 ! complex error function, ACM Trans. Math. Software 16:47, 1990.
312 !
313 ! Given a complex number z, this function computes
314 ! the value of the Faddeeva-function w(z) = exp(-z**2)*erfc(-i*z),
315 ! where erfc is the complex complementary error-function and i
316 ! means sqrt(-1). The accuracy of the algorithm for z in the 1st
317 ! and 2nd quadrant is 14 significant digits; in the 3rd and 4th
318 ! it is 13 significant digits outside a circular region with radius
319 ! 0.126 around a zero of the function.
320
321 COMPLEX(kind=dp), INTENT(in) :: z
322
323 REAL(kind=dp), PARAMETER :: factor = 1.12837916709551257388_dp
324
325 INTEGER :: i, j, kapn, n, np1, nu
326 LOGICAL :: a, b
327 REAL(kind=dp) :: c, daux, h, h2, qlambda, qrho, rx, ry, &
328 sx, sy, tx, ty, u, u1, u2, v, v1, v2, &
329 w1, x, xabs, xabsq, xaux, xi, xquad, &
330 xsum, y, yabs, yi, yquad, ysum
331
332 ! factor is 2/sqrt(pi)
333
334 ! To avoid the complier uninitialised varning
335 h2 = zero
336
337 xi = real(z)
338 yi = aimag(z)
339 xabs = abs(xi)
340 yabs = abs(yi)
341 x = xabs/6.3_dp
342 y = yabs/4.4_dp
343 qrho = x**2 + y**2
344 xabsq = xabs**2
345 xquad = xabsq - yabs**2
346 yquad = 2*xabs*yabs
347
348 a = qrho < 0.085264_dp
349
350 branch1: IF (a) THEN
351
352 ! If ( qrho .lt. 0.085264 ) then the Faddeeva-function is evaluated
353 ! using a power-series (abramowitz/stegun, equation (7.1.5), p.297)
354 ! n is the minimum number of terms needed to obtain the required
355 ! accuracy
356
357 qrho = (one - 0.85_dp*y)*sqrt(qrho)
358 n = nint(6.0_dp + 72.0_dp*qrho)
359 j = 2*n + 1
360 xsum = one/real(j, kind=dp)
361 ysum = zero
362
363 DO i = n, 1, -1
364 j = j - 2
365 xaux = (xsum*xquad - ysum*yquad)/real(i, kind=dp)
366 ysum = (xsum*yquad + ysum*xquad)/real(i, kind=dp)
367 xsum = xaux + one/real(j, kind=dp)
368 END DO
369
370 u1 = -factor*(xsum*yabs + ysum*xabs) + one
371 v1 = factor*(xsum*xabs - ysum*yabs)
372 daux = exp(-xquad)
373 u2 = daux*cos(yquad)
374 v2 = -daux*sin(yquad)
375 u = u1*u2 - v1*v2
376 v = u1*v2 + v1*u2
377 ELSE
378
379 bran2: IF (qrho > one) THEN
380
381 ! If ( qrho .gt. 1) then w(z) is evaluated using the laplace
382 ! continued fraction. nu is the minimum number of terms needed
383 ! to obtain the required accuracy.
384
385 h = zero
386 kapn = 0
387 qrho = sqrt(qrho)
388 nu = int(3.0_dp + (1442.0_dp/(26.0_dp*qrho &
389 + 77.0_dp)))
390
391 ELSE
392
393 ! If ( qrho .ge. 0.085264 .and. qrho .le. one ) then
394 ! w(z) is evaluated by a truncated Taylor expansion,
395 ! where the Laplace continued fraction is used to calculate
396 ! the derivatives of w(z). KAPN is the minimum number of terms
397 ! in the Taylor expansion needed to obtain the required accuracy.
398 ! NU is the minimum number of terms of the continued fraction
399 ! needed to calculate the derivatives with the required accuracy.
400
401 qrho = (one - y)*sqrt(one - qrho)
402 h = 1.88_dp*qrho
403 h2 = two*h
404 kapn = nint(7.0_dp + 34.0_dp*qrho)
405 nu = nint(16.0_dp + 26.0_dp*qrho)
406
407 END IF bran2
408
409 b = h > zero
410
411 ! To avoid uninitialise compiler warning. qlambda is used
412 ! only if (b), so can define to any value otherwise.
413 qlambda = zero
414 IF (b) qlambda = h2**kapn
415
416 rx = zero
417 ry = zero
418 sx = zero
419 sy = zero
420
421 DO n = nu, 0, -1
422 np1 = n + 1
423 tx = yabs + h + np1*rx
424 ty = xabs - np1*ry
425 c = half/(tx**2 + ty**2)
426 rx = c*tx
427 ry = c*ty
428 IF (b .AND. n <= kapn) THEN
429 tx = qlambda + sx
430 sx = rx*tx - ry*sy
431 sy = ry*tx + rx*sy
432 qlambda = qlambda/h2
433 END IF
434 END DO
435
436 IF (h == zero) THEN
437 u = factor*rx
438 v = factor*ry
439 ELSE
440 u = factor*sx
441 v = factor*sy
442 END IF
443
444 IF (yabs == zero) u = exp(-xabs**2)
445
446 END IF branch1
447
448 ! Evaluation of w(z) in the other quadrants
449
450 IF (yi < zero) THEN
451
452 IF (a) THEN
453 u2 = two*u2
454 v2 = two*v2
455 ELSE
456 xquad = -xquad
457 w1 = two*exp(xquad)
458 u2 = w1*cos(yquad)
459 v2 = -w1*sin(yquad)
460 END IF
461
462 u = u2 - u
463 v = v2 - v
464 IF (xi > zero) v = -v
465 ELSE
466 IF (xi < zero) v = -v
467 END IF
468
469 faddeyeva_fast = cmplx(u, v, kind=dp)
470
471 END FUNCTION faddeyeva_fast
472
473! **************************************************************************************************
474!> \brief Computes the error function of a complex argument using the Poppe and Wijers algorithm
475!> \param z complex argument
476!> \return error function of z
477! **************************************************************************************************
478 elemental COMPLEX(kind=dp) FUNCTION erfz_fast(z)
479
480 ! This is an error function of a complex argument, which uses faddeyeva_fast(z).
481
482 COMPLEX(kind=dp), INTENT(in) :: z
483
484 erfz_fast = one - faddeyeva_fast(cmplxj*z)*exp(-z**2)
485
486 END FUNCTION erfz_fast
487
488 !*********************************************************************
489END MODULE erf_complex
Module to compute the error function of a complex argument.
Definition erf_complex.F:50
complex(kind=dp), parameter cmplx0
Definition erf_complex.F:65
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:81
real(kind=dp), parameter eps0
Definition erf_complex.F:62
real(kind=dp), parameter sqrt_log_rmin
Definition erf_complex.F:62
real(kind=dp), parameter rmin
Definition erf_complex.F:62
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 pi2
Definition erf_complex.F:62
complex(kind=dp), parameter cmplxj
Definition erf_complex.F:65
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 oorootpi
real(kind=dp), parameter, public pi
real(kind=dp), parameter, public half
real(kind=dp), parameter, public two
real(kind=dp), parameter, public one
real(kind=dp), parameter, public zero