DBSGQ8, a modification of gaus8,
integrates the product of fun(x)
by the id
-th derivative of a spline
dbvalu between limits a
and b
using an adaptive 8-point Legendre-Gauss
algorithm.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
procedure(b1fqad_func) | :: | fun |
name of external function of one argument which multiplies dbvalu. |
|||
real(kind=wp), | intent(in), | dimension(:) | :: | xt |
knot array for dbvalu |
|
real(kind=wp), | intent(in), | dimension(n) | :: | bc |
b-coefficient array for dbvalu |
|
integer(kind=ip), | intent(in) | :: | n |
number of b-coefficients for dbvalu |
||
integer(kind=ip), | intent(in) | :: | kk |
order of the spline, |
||
integer(kind=ip), | intent(in) | :: | id |
Order of the spline derivative, |
||
real(kind=wp), | intent(in) | :: | a |
lower limit of integral |
||
real(kind=wp), | intent(in) | :: | b |
upper limit of integral (may be less than |
||
integer(kind=ip), | intent(inout) | :: | inbv |
initialization parameter for dbvalu |
||
real(kind=wp), | intent(inout) | :: | err |
IN: is a requested pseudorelative error
tolerance. normally pick a value of
OUT: will be an estimate of the absolute
error in ans if the input value of |
||
real(kind=wp), | intent(out) | :: | ans |
computed value of integral |
||
integer(kind=ip), | intent(out) | :: | iflag |
a status code:
|
||
real(kind=wp), | intent(inout), | dimension(:) | :: | work |
work vector of length |
subroutine dbsgq8(fun,xt,bc,n,kk,id,a,b,inbv,err,ans,iflag,work) implicit none procedure(b1fqad_func) :: fun !! name of external function of one !! argument which multiplies [[dbvalu]]. integer(ip),intent(in) :: n !! number of b-coefficients for [[dbvalu]] integer(ip),intent(in) :: kk !! order of the spline, `kk>=1` real(wp),dimension(:),intent(in) :: xt !! knot array for [[dbvalu]] real(wp),dimension(n),intent(in) :: bc !! b-coefficient array for [[dbvalu]] integer(ip),intent(in) :: id !! Order of the spline derivative, `0<=id<=kk-1` real(wp),intent(in) :: a !! lower limit of integral real(wp),intent(in) :: b !! upper limit of integral (may be less than `a`) integer(ip),intent(inout) :: inbv !! initialization parameter for [[dbvalu]] real(wp),intent(inout) :: err !! **IN:** is a requested pseudorelative error !! tolerance. normally pick a value of !! `abs(err)<1e-3`. `ans` will normally !! have no more error than `abs(err)` times !! the integral of the absolute value of !! `fun(x)*[[dbvalu]]()`. !! !! **OUT:** will be an estimate of the absolute !! error in ans if the input value of `err` !! was negative. (`err` is unchanged if !! the input value of `err` was nonnegative.) !! the estimated error is solely for information !! to the user and should not be used as a !! correction to the computed integral. real(wp),intent(out) :: ans !! computed value of integral integer(ip),intent(out) :: iflag !! a status code: !! !! * 0: `ans` most likely meets requested !! error tolerance, or `a=b`. !! * 1101: `a` and `b` are too nearly equal !! to allow normal integration. !! `ans` is set to zero. !! * 1102: `ans` probably does not meet !! requested error tolerance. real(wp),dimension(:),intent(inout) :: work !! work vector of length `3*k` for [[dbvalu]] integer(ip) :: k,l,lmn,lmx,mxl,nbits,nib,nlmx real(wp) :: ae,anib,area,c,ce,ee,ef,eps,est,gl,glr,tol,vr,x integer(ip),dimension(60) :: lr real(wp),dimension(60) :: aa,hh,vl,gr integer(ip),parameter :: i1mach14 = digits(1.0_wp) !! i1mach(14) real(wp),parameter :: d1mach5 = log10(real(radix(x),wp)) !! d1mach(5) real(wp),parameter :: ln2 = log(2.0_wp) !! 0.69314718d0 real(wp),parameter :: sq2 = sqrt(2.0_wp) integer(ip),parameter :: nlmn = 1 integer(ip),parameter :: kmx = 5000 integer(ip),parameter :: kml = 6 ! initialize inbv = 1_ip iflag = 0_ip k = i1mach14 anib = d1mach5*k/0.30102000_wp nbits = int(anib,ip) nlmx = min((nbits*5_ip)/8_ip,60_ip) ans = 0.0_wp ce = 0.0_wp if ( a==b ) then if ( err<0.0_wp ) err = ce else lmx = nlmx lmn = nlmn if ( b/=0.0_wp ) then if ( sign(1.0_wp,b)*a>0.0_wp ) then c = abs(1.0_wp-a/b) if ( c<=0.1_wp ) then if ( c<=0.0_wp ) then if ( err<0.0_wp ) err = ce return else anib = 0.5_wp - log(c)/ln2 nib = int(anib,ip) lmx = min(nlmx,nbits-nib-7_ip) if ( lmx<1_ip ) then ! a and b are too nearly equal ! to allow normal integration iflag = 1101_ip if ( err<0.0_wp ) err = ce return else lmn = min(lmn,lmx) end if end if end if end if end if tol = max(abs(err),2.0_wp**(5-nbits))/2.0_wp if ( err==0.0_wp ) tol = sqrt(epsilon(1.0_wp)) eps = tol hh(1_ip) = (b-a)/4.0_wp aa(1_ip) = a lr(1_ip) = 1_ip l = 1_ip call g8(aa(l)+2.0_wp*hh(l),2.0_wp*hh(l),est,iflag) if (iflag/=0_ip) return k = 8_ip area = abs(est) ef = 0.5_wp mxl = 0_ip end if do ! compute refined estimates, estimate the error, etc. call g8(aa(l)+hh(l),hh(l),gl,iflag) if (iflag/=0_ip) return call g8(aa(l)+3.0_wp*hh(l),hh(l),gr(l),iflag) if (iflag/=0_ip) return k = k + 16_ip area = area + (abs(gl)+abs(gr(l))-abs(est)) glr = gl + gr(l) ee = abs(est-glr)*ef ae = max(eps*area,tol*abs(glr)) if ( ee>ae ) then ! consider the left half of this level if ( k>kmx ) lmx = kml if ( l>=lmx ) then mxl = 1_ip else l = l + 1_ip eps = eps*0.5_wp ef = ef/sq2 hh(l) = hh(l-1)*0.5_wp lr(l) = -1_ip aa(l) = aa(l-1_ip) est = gl cycle end if end if ce = ce + (est-glr) if ( lr(l)<=0_ip ) then ! proceed to right half at this level vl(l) = glr else ! return one level vr = glr do if ( l<=1_ip ) then ! exit ans = vr if ( (mxl/=0_ip) .and. (abs(ce)>2.0_wp*tol*area) ) then iflag = 1102_ip end if if ( err<0.0_wp ) err = ce return else l = l - 1_ip eps = eps*2.0_wp ef = ef*sq2 if ( lr(l)<=0 ) then vl(l) = vl(l+1_ip) + vr exit else vr = vl(l+1_ip) + vr end if end if end do end if est = gr(l-1_ip) lr(l) = 1_ip aa(l) = aa(l) + 4.0_wp*hh(l) end do contains subroutine g8(x,h,res,iflag) !! 8-point formula. !! !!@note Replaced the original double precision abscissa and weight !! coefficients with the higher precision versions from here: !! http://pomax.github.io/bezierinfo/legendre-gauss.html !! So, if `wp` is changed to say, `real128`, more precision !! can be obtained. These coefficients have about 300 digits. implicit none real(wp),intent(in) :: x real(wp),intent(in) :: h real(wp),intent(out) :: res integer(ip),intent(out) :: iflag real(wp),dimension(8) :: f real(wp),dimension(8) :: v ! abscissa and weight coefficients: real(wp),parameter :: x1 = & &0.1834346424956498049394761423601839806667578129129737823171884736992044& &742215421141160682237111233537452676587642867666089196012523876865683788& &569995160663568104475551617138501966385810764205532370882654749492812314& &961247764619363562770645716456613159405134052985058171969174306064445289& &638150514997832_wp real(wp),parameter :: x2 = & &0.5255324099163289858177390491892463490419642431203928577508570992724548& &207685612725239614001936319820619096829248252608507108793766638779939805& &395303668253631119018273032402360060717470006127901479587576756241288895& &336619643528330825624263470540184224603688817537938539658502113876953598& &879150514997832_wp real(wp),parameter :: x3 = & &0.7966664774136267395915539364758304368371717316159648320701702950392173& &056764730921471519272957259390191974534530973092653656494917010859602772& &562074621689676153935016290342325645582634205301545856060095727342603557& &415761265140428851957341933710803722783136113628137267630651413319993338& &002150514997832_wp real(wp),parameter :: x4 = & &0.9602898564975362316835608685694729904282352343014520382716397773724248& &977434192844394389592633122683104243928172941762102389581552171285479373& &642204909699700433982618326637346808781263553346927867359663480870597542& &547603929318533866568132868842613474896289232087639988952409772489387324& &25615051499783203_wp real(wp),parameter :: w1 = & &0.3626837833783619829651504492771956121941460398943305405248230675666867& &347239066773243660420848285095502587699262967065529258215569895173844995& &576007862076842778350382862546305771007553373269714714894268328780431822& &779077846722965535548199601402487767505928976560993309027632737537826127& &502150514997832_wp real(wp),parameter :: w2 = & &0.3137066458778872873379622019866013132603289990027349376902639450749562& &719421734969616980762339285560494275746410778086162472468322655616056890& &624276469758994622503118776562559463287222021520431626467794721603822601& &295276898652509723185157998353156062419751736972560423953923732838789657& &919150514997832_wp real(wp),parameter :: w3 = & &0.2223810344533744705443559944262408844301308700512495647259092892936168& &145704490408536531423771979278421592661012122181231114375798525722419381& &826674532090577908613289536840402789398648876004385697202157482063253247& &195590228631570651319965589733545440605952819880671616779621183704306688& &233150514997832_wp real(wp),parameter :: w4 = & &0.1012285362903762591525313543099621901153940910516849570590036980647401& &787634707848602827393040450065581543893314132667077154940308923487678731& &973041136073584690533208824050731976306575729205467961435779467552492328& &730055025992954089946676810510810729468366466585774650346143712142008566& &866150514997832_wp res = 0.0_wp v(1_ip) = x-x1*h v(2_ip) = x+x1*h v(3_ip) = x-x2*h v(4_ip) = x+x2*h v(5_ip) = x-x3*h v(6_ip) = x+x3*h v(7_ip) = x-x4*h v(8_ip) = x+x4*h call dbvalu(xt,bc,n,kk,id,v(1_ip),inbv,work,iflag,f(1_ip)); if (iflag/=0_ip) return call dbvalu(xt,bc,n,kk,id,v(2_ip),inbv,work,iflag,f(2_ip)); if (iflag/=0_ip) return call dbvalu(xt,bc,n,kk,id,v(3_ip),inbv,work,iflag,f(3_ip)); if (iflag/=0_ip) return call dbvalu(xt,bc,n,kk,id,v(4_ip),inbv,work,iflag,f(4_ip)); if (iflag/=0_ip) return call dbvalu(xt,bc,n,kk,id,v(5_ip),inbv,work,iflag,f(5_ip)); if (iflag/=0_ip) return call dbvalu(xt,bc,n,kk,id,v(6_ip),inbv,work,iflag,f(6_ip)); if (iflag/=0_ip) return call dbvalu(xt,bc,n,kk,id,v(7_ip),inbv,work,iflag,f(7_ip)); if (iflag/=0_ip) return call dbvalu(xt,bc,n,kk,id,v(8_ip),inbv,work,iflag,f(8_ip)); if (iflag/=0_ip) return res = h*((w1*(fun(v(1_ip))*f(1_ip) + fun(v(2_ip))*f(2_ip)) + & w2*(fun(v(3_ip))*f(3_ip) + fun(v(4_ip))*f(4_ip))) + & (w3*(fun(v(5_ip))*f(5_ip) + fun(v(6_ip))*f(6_ip)) + & w4*(fun(v(7_ip))*f(7_ip) + fun(v(8_ip))*f(8_ip)))) end subroutine g8 end subroutine dbsgq8