33# integration with the order-n Kronrod rule and weights of type Tw,
44# with absolute tolerance atol and relative tolerance rtol,
55# with maxevals an approximate maximum number of f evaluations.
6- function do_quadgk (f:: F , s:: NTuple{N,T} , n, atol, rtol, maxevals, nrm) where {T,N,F}
6+ function do_quadgk (f:: F , s:: NTuple{N,T} , n, atol, rtol, maxevals, nrm, segbuf ) where {T,N,F}
77 x,w,gw = cachedrule (T,n)
88
99 @assert N ≥ 2
@@ -32,11 +32,13 @@ function do_quadgk(f::F, s::NTuple{N,T}, n, atol, rtol, maxevals, nrm) where {T,
3232 return (I, E) # fast return when no subdivisions required
3333 end
3434
35- return adapt (f, heapify! (collect (segs), Reverse), I, E, numevals, x,w,gw,n, atol_, rtol_, maxevals, nrm)
35+ segheap = segbuf === nothing ? collect (segs) : (resize! (segbuf, N- 1 ) .= segs)
36+ heapify! (segheap, Reverse)
37+ return adapt (f, segheap, I, E, numevals, x,w,gw,n, atol_, rtol_, maxevals, nrm)
3638end
3739
3840# internal routine to perform the h-adaptive refinement of the integration segments (segs)
39- function adapt (f, segs:: Vector{T} , I, E, numevals, x,w,gw,n, atol, rtol, maxevals, nrm) where {T}
41+ function adapt (f:: F , segs:: Vector{T} , I, E, numevals, x,w,gw,n, atol, rtol, maxevals, nrm) where {F, T}
4042 # Pop the biggest-error segment and subdivide (h-adaptation)
4143 # until convergence is achieved or maxevals is exceeded.
4244 while E > atol && E > rtol * nrm (I) && numevals < maxevals
116118# Gauss-Kronrod quadrature of f from a to b to c...
117119
118120"""
119- quadgk(f, a,b,c...; rtol=sqrt(eps), atol=0, maxevals=10^7, order=7, norm=norm)
121+ quadgk(f, a,b,c...; rtol=sqrt(eps), atol=0, maxevals=10^7, order=7, norm=norm, segbuf=nothing )
120122
121123Numerically integrate the function `f(x)` from `a` to `b`, and optionally over additional
122124intervals `b` to `c` and so on. Keyword options include a relative error tolerance `rtol`
@@ -169,15 +171,35 @@ or `1/sqrt(x)` singularity).
169171
170172For real-valued endpoints, the starting and/or ending points may be infinite. (A coordinate
171173transformation is performed internally to map the infinite interval to a finite one.)
174+
175+ In normal usage, `quadgk(...)` will allocate a buffer for segments. You can
176+ instead pass a preallocated buffer allocated using `alloc_segbuf(...)` as the
177+ `segbuf` argument. This buffer can be used across multiple calls to avoid
178+ repeated allocation.
172179"""
173- quadgk (f, a, b, c ... ; kws... ) =
174- quadgk (f, promote (a, b, c ... )... ; kws... )
180+ quadgk (f, segs ... ; kws... ) =
181+ quadgk (f, promote (segs ... )... ; kws... )
175182
176- quadgk (f, a :: T ,b :: T ,c :: T... ;
177- atol= nothing , rtol= nothing , maxevals= 10 ^ 7 , order= 7 , norm= norm) where {T} =
178- handle_infinities (f, (a, b, c ... ) ) do f, s, _
179- do_quadgk (f, s, order, atol, rtol, maxevals, norm)
183+ function quadgk (f, segs :: T... ;
184+ atol= nothing , rtol= nothing , maxevals= 10 ^ 7 , order= 7 , norm= norm, segbuf = nothing ) where {T}
185+ handle_infinities (f, segs ) do f, s, _
186+ do_quadgk (f, s, order, atol, rtol, maxevals, norm, segbuf )
180187 end
188+ end
189+
190+ """
191+ function alloc_segbuf(domain_type=Float64, range_type=Float64, error_type=Float64; size=1)
192+
193+ This helper will allocate a segment buffer for segments to a `quadgk(...)` call
194+ with the given `domain_type`, which is the same as the type of the integration
195+ limits, `range_type` i.e. the range of the function being integrated and
196+ `error_type`, the type returned by the `norm` given to `quadgk(...)` and
197+ starting with the given `size`. The buffer can then be reused across multiple
198+ compatible calls to `quadgk(...)` to avoid repeated allocation.
199+ """
200+ function alloc_segbuf (domain_type= Float64, range_type= Float64, error_type= Float64; size= 1 )
201+ Vector {Segment{domain_type, range_type, error_type}} (undef, size)
202+ end
181203
182204"""
183205 quadgk!(f!, result, a,b,c...; rtol=sqrt(eps), atol=0, maxevals=10^7, order=7, norm=norm)
@@ -199,8 +221,8 @@ For integrands whose values are *small* arrays whose length is known at compile-
199221it is usually more efficient to use `quadgk` and modify your integrand to return
200222an `SVector` from the [StaticArrays.jl package](https://github.com/JuliaArrays/StaticArrays.jl).
201223"""
202- function quadgk! (f!, result, a:: T ,b:: T ,c:: T... ; atol= nothing , rtol= nothing , maxevals= 10 ^ 7 , order= 7 , norm= norm) where {T}
224+ function quadgk! (f!, result, a:: T ,b:: T ,c:: T... ; atol= nothing , rtol= nothing , maxevals= 10 ^ 7 , order= 7 , norm= norm, segbuf = nothing ) where {T}
203225 fx = result / oneunit (T) # pre-allocate array of correct type for integrand evaluations
204226 f = InplaceIntegrand (f!, result, fx)
205- return quadgk (f, a, b, c... ; atol= atol, rtol= rtol, maxevals= maxevals, order= order, norm= norm)
206- end
227+ return quadgk (f, a, b, c... ; atol= atol, rtol= rtol, maxevals= maxevals, order= order, norm= norm, segbuf = segbuf )
228+ end
0 commit comments