! ! num_quad - numerical quadrature ! module num_quad implicit none ! --- in/out ------------------- private public :: IntervalQuad_Lin public :: IntervalQuad_Cos_Lin public :: IntervalQuad_Const public :: IntervalSum ! --- const --------------------------------------- character(len=*), parameter :: mname = 'num_quad' contains !------------------------------------------------------------------- ! ! Syntax: ! ! call IntervalQuad_Lin( x, y, a, b, c, ilast ) ! ! Compute an approximation to the integral: ! ! b ! int y(x) dx ! x=a ! ! Input is formed by two vectors x and y: y contains the function ! values to the abscissa values contained in x. ! The result, stored in c is computed using the trapezoid formula ! throughout the domain of integration. ! If ax(n) then the function is extended to a, resp. b, ! assuming a constant value of y(1), resp. y(n). ! The index ilast should be set to 1 in the first call. If subsequent ! integrals on the same vector are to be computed, it should be kept ! unaltered between calls. This will speed up the lookup of values ! if the various integrals are ascending in x. ! !-------------------------------------------------------------------- subroutine IntervalQuad_Lin( x,y, a,b, c, ilast, status ) use GO , only : gol, goErr use num_tools, only : interval ! --- in/out --------------------- real, intent(in) :: x(:), y(:) real, intent(in) :: a, b real, intent(out) :: c integer, intent(inout) :: ilast integer, intent(out) :: status ! --- const --------------------- character(len=*), parameter :: rname = mname//'/IntervalQuad_Lin' ! --- local ------------------- integer :: n real :: ya, yb integer :: i, i1, i2, iflag ! --- begin --------------------- n = size(x) if ( size(y) /= n ) then write (gol,'("x and y should have same lengths:")'); call goErr write (gol,'(" size(x) : ",i6)') size(x); call goErr write (gol,'(" size(y) : ",i6)') size(y); call goErr write (gol,'("in ",a)') rname; call goErr; status=1; return end if i1=ilast call interval(x,a,i1,iflag) if (iflag == -1) then ! a < x(1) case c=y(1)*(x(1)-a) else if (iflag == 1) then ! a > x(n) case c=-y(n)*(a-x(n)) else ya=y(i1)+(a-x(i1))*(y(i1+1)-y(i1))/(x(i1+1)-x(i1)) c=-0.5*(a-x(i1))*(y(i1)+ya) end if i2=i1 call interval(x,b,i2,iflag) if (iflag == -1) then ! b < x(1) case c=c-y(1)*(x(1)-b) else if (iflag == 1) then ! b > x(n) case c=c+y(n)*(b-x(n)) else yb=y(i2)+(b-x(i2))*(y(i2+1)-y(i2))/(x(i2+1)-x(i2)) c=c+0.5*(b-x(i2))*(y(i2)+yb) end if if (i1 < i2) then do i=i1,i2-1 c=c+0.5*(x(i+1)-x(i))*(y(i+1)+y(i)) end do else do i=i2,i1-1 c=c-0.5*(x(i+1)-x(i))*(y(i+1)+y(i)) end do end if ilast=i2 ! ok status = 0 end subroutine IntervalQuad_Lin ! === !------------------------------------------------------------------- ! ! Syntax: ! ! call lquad_cos( x, y, a, b, c, ilast ) ! ! Compute an approximation to the integral: ! ! b ! int y(x) cos(x) dx ! x=a ! ! Input is formed by two vectors x and y: y contains the function ! values to the abscissa values contained in x. ! The result, stored in c is computed using the trapezoid formula ! throughout the domain of integration. ! If ax(n) then the function is extended to a, resp. b, ! assuming a constant value of y(1), resp. y(n). ! The index ilast should be set to 1 in the first call. If subsequent ! integrals on the same vector are to be computed, it should be kept ! unaltered between calls. This will speed up the lookup of values ! if the various integrals are ascending in x. ! ! Assuming y(x) = c + dx in the gridbox : ! ! b b ! int (c +dx) cos(x) dx = [ sin(x) + d( x sin(x) + cos(x) ) ] ! x=a x=a ! !-------------------------------------------------------------------- subroutine IntervalQuad_Cos_Lin( x, y, a, b, c, ilast, status ) use GO , only : gol, goErr use num_tools, only : interval ! --- in/out -------------------------------- real, intent(in) :: x(:), y(:) real, intent(in) :: a, b real, intent(out) :: c integer, intent(inout) :: ilast integer, intent(out) :: status ! --- const --------------------- character(len=*), parameter :: rname = mname//'/IntervalQuad_Cos_Lin' ! --- local ---------------------------------- integer :: n real :: s, co integer :: i, i1, i2 integer :: iflag ! --- begin ---------------------------------- n = size(x) if ( size(y) /= n ) then write (gol,'("x and y should have same lengths:")'); call goErr write (gol,'(" size(x) : ",i6)') size(x); call goErr write (gol,'(" size(y) : ",i6)') size(y); call goErr write (gol,'("in ",a)') rname; call goErr; status=1; return end if i1=ilast call interval(x,a,i1,iflag) if (iflag == -1) then ! a < x(1) case c = y(1)*(sin(x(1))-sin(a)) else if (iflag == 1) then ! a > x(n) case c = -y(n)*(sin(a)-sin(x(n))) else ! int (co + s*x) cos(x) dx = INt (co cos(x) dx + Int s*x cos(x) dx ! co sin(x) | + s x sin(x) | + s cosx | s = (y(i1+1)-y(i1))/(x(i1+1)-x(i1)) co = y(i1) - x(i1)*s c = (co + s*a) *sin(a) - & (co + s*x(i1))*sin(x(i1)) + & s*(cos(a)-cos(x(i1))) c = -c ! negative because outside a-b end if i2=i1 call interval(x,b,i2,iflag) if (iflag == -1) then ! b < x(1) case c = c - y(1)*(sin(x(1))-sin(b)) else if (iflag == 1) then ! b > x(n) case c = c + y(n)*(sin(b)-sin(x(n))) else s = (y(i2+1)-y(i2))/(x(i2+1)-x(i2)) co = y(i2) - x(i2)*s c = c + (co + s*b)*sin(b) - & (co + s*x(i2)) *sin(x(i2)) + & s*(cos(b)-cos(x(i2))) end if if (i1 < i2) then do i=i1,i2-1 s = (y(i+1)-y(i))/(x(i+1)-x(i)) co = y(i) - x(i)*s c = c + (co + s*x(i+1))*sin(x(i+1)) - & (co + s*x(i)) *sin(x(i)) + & s*(cos(x(i+1))-cos(x(i))) end do else do i=i2,i1-1 s = (y(i+1)-y(i))/(x(i+1)-x(i)) co = y(i) - x(i)*s c = c - (co + s*x(i+1))*sin(x(i+1)) + & (co + s*x(i)) *sin(x(i)) - & s*(cos(x(i+1))-cos(x(i))) end do end if ilast=i2 ! ok status = 0 end subroutine IntervalQuad_Cos_Lin !------------------------------------------------------------------- ! ! NAME ! Integral_const ! ! INTERFACE ! subroutine IntervalQuad_Const( x, y, a, b, c, ilast ) ! real, intent(in) :: x(:), y(:) ! real, intent(in) :: a, b ! real, intent(out) :: c ! integer, intent(inout) :: ilast ! ! DESCRIPTION ! Compute integral over [a,b] for function specified by: ! o interval boundaries x(1),..,x(n),x(n+1) ! o function values y(1),..,y(n) ; constant in interval ! ! y(1) y(2) y(3) ! +-----o-----+ ! |/ / / / / /+-----o----+ ! +----o----+ / / / / / / / / / /| ! | / / / / / / / / / / / | ! |/ / / / / / / / / / / /| ! ----+------|--+-----------+--------|-+--- ! x(1) x(2) x(3) x(4) ! a b ! ! The result is stored in c. ! If ax(n+1) then the function is extended to a, resp. b, ! assuming a constant value of y(1), resp. y(n). ! The index ilast should be set to 1 in the first call. ! If subsequent integrals on the same vector are to be computed, ! it should be kept unaltered between calls. This will speed up the ! lookup of values if the various integrals are ascending in x. ! !-------------------------------------------------------------------- subroutine IntervalQuad_Const( x, y, a,b, c, ilast, status ) use GO , only : gol, goErr use num_tools, only : interval ! --- in/out --------------------- real, intent(in) :: x(:), y(:) real, intent(in) :: a, b real, intent(out) :: c integer, intent(inout) :: ilast integer, intent(out) :: status ! --- const --------------------- character(len=*), parameter :: rname = mname//'/IntervalQuad_Const' ! --- local ------------------- integer :: n real :: ya, yb integer :: i, i_a, i_b, iflag ! --- begin --------------------- ! check array sizes: n = size(y) if ( size(x) /= n+1 ) then write (gol,'("x should have one element more than y:")'); call goErr write (gol,'(" size(x) : ",i6)') size(x); call goErr write (gol,'(" size(y) : ",i6)') size(y); call goErr write (gol,'("in ",a)') rname; call goErr; status=1; return end if ! check interval: if ( b < a ) then write (gol,'("found strange interval [a,b] :")'); call goErr write (gol,'(" a : ",es12.4)') a; call goErr write (gol,'(" b : ",es12.4)') b; call goErr write (gol,'("in ",a)') rname; call goErr; status=1; return end if ! fill c with contribution of interval including a: i_a = ilast call Interval( x, a, i_a, iflag ) select case ( iflag ) case ( -1 ) ! a < x(1) ; extend y to the left: c = y(1) * ( x(1) - a ) case ( 0 ) ! x(i_a) < a < x(i_a+1) c = y(i_a) * ( x(i_a+1) - a ) case ( 1 ) ! a > x(n+1) ; extend y to the right ! negative contribution to integral: c = - y(n) * ( a - x(n+1) ) case default write (gol,'("unsupported iflag from call to Interval : ",i6)') iflag write (gol,'("in ",a)') rname; call goErr; status=1; return end select ! add contributions of interval including b: i_b = i_a call Interval( x, b, i_b, iflag ) select case ( iflag ) case ( -1 ) ! b < x(1) ; negative contribution c = c - y(1) * ( x(1) - b ) case ( 0 ) ! x(i_b) < b < x(i_b+1) c = c + y(i_b) * ( b - x(i_b) ) case ( 1 ) ! b > x(n+1) c = c + y(n) * ( b - x(n+1) ) case default write (gol,'("unsupported iflag from call to Interval : ",i6)') iflag write (gol,'("in ",a)') rname; call goErr; status=1; return end select ! add contributions of intermediate intervals do i = i_a+1, i_b-1 c = c + y(i) * (x(i+1)-x(i)) end do ! set index of last interval (including b): ilast = i_b ! ok status = 0 end subroutine IntervalQuad_Const !------------------------------------------------------------------- ! ! NAME ! IntervalSum - add contributions of small intervals ! ! INTERFACE ! subroutine IntervalSum( x, y, a, b, c, ilast, fac ) ! real, intent(in) :: x(:), y(:) ! real, intent(in) :: a, b ! real, intent(out) :: c ! integer, intent(inout) :: ilast ! real, intent(out), optional :: fac(:) ! ! DESCRIPTION ! Compute sum of values y over invterval [a,b], ! for function y specified by: ! o interval boundaries x(1),..,x(n),x(n+1) ! o function values y(1),..,y(n) ; constant in interval ! ! y(1) y(2) y(3) ! ! 100% ! +-----o-----+ 60% ! 30% | ^ +-----o----+ ! +----o----+ | ^ ! ^ | | ! | | | ! | | | ! ----+------|--+-----------+--------|-+--- ! x(1) x(2) x(3) x(4) ! a b ! ! Each y(i) contributes to the sum for the fraction of [x(i),x(i+1)] ! covered by [a,b]. ! The result is stored in c. ! If ax(n+1) an error is issued. ! ! The index ilast should be set to 1 in the first call. ! If subsequent integrals on the same vector are to be computed, ! it should be kept unaltered between calls. This will speed up the ! lookup of values if the various integrals are ascending in x. ! ! If b < a, the result is negative. ! ! If present, the array 'fac' is filt with the factors applied to y(:). ! !-------------------------------------------------------------------- subroutine IntervalSum( x, y, a_in, b_in, c, ilast, status, fac ) use GO , only : gol, goErr use num_tools, only : interval ! --- in/out --------------------- real, intent(in) :: x(:), y(:) real, intent(in) :: a_in, b_in real, intent(out) :: c integer, intent(inout) :: ilast integer, intent(inout) :: status real, intent(out), optional :: fac(:) ! --- const -------------------------- character(len=*), parameter :: rname = mname//'/IntervalSum' ! --- local ------------------- integer :: n real :: ya, yb real :: f integer :: i, i_a, i_b, iflag real :: a, b, plusmin ! --- begin --------------------- ! check array sizes: n = size(y) if ( size(x) /= n+1 ) then write (gol,'("x should have one element more than y:")'); call goErr write (gol,'(" size(x) : ",i6)') size(x); call goErr write (gol,'(" size(y) : ",i6)') size(y); call goErr write (gol,'("in ",a)') rname; call goErr; status=1; return end if ! check size of factor array if present: if ( present(fac) ) then if ( size(fac) /= n ) then write (gol,'("ERROR in ",a)') rname; status=1; return write (gol,'("fac should have same size as y:")'); call goErr write (gol,'(" size(y) : ",i6)') size(y); call goErr write (gol,'(" size(fac) : ",i6)') size(fac); call goErr write (gol,'("in ",a)') rname; call goErr; status=1; return end if end if ! increasing or decreasing interval ? if ( a_in <= b_in ) then a = a_in b = b_in plusmin = 1.0 else a = b_in b = a_in plusmin = -1.0 end if ! init output factors: if ( present(fac) ) fac = 0.0 ! fill c with contribution of interval including a: i_a = ilast call interval(x,a,i_a,iflag) select case ( iflag ) case ( -1 ) ! a < x(1) write (gol,'("interval is partly less than x :")') write (gol,'(" a : ",es12.4)') a; call goErr write (gol,'(" x(1) : ",es12.4)') x(1); call goErr write (gol,'("ERROR in ",a)') rname; status=1; return case ( 0 ) ! x(i_a) < a < x(i_a+1) f = ( x(i_a+1) - a ) / ( x(i_a+1) - x(i_a) ) c = y(i_a) * f if ( present(fac) ) fac(i_a) = fac(i_a) + f case ( 1 ) ! a > x(n+1) write (gol,'("interval partly exceeds x :")') write (gol,'(" a : ",es12.4)') a; call goErr write (gol,'(" x(n+1) : ",es12.4)') x(n+1); call goErr write (gol,'("ERROR in ",a)') rname; status=1; return case default write (gol,'("unsupported iflag from call to Interval : ",i6)') iflag write (gol,'("in ",a)') rname; call goErr; status=1; return end select ! add contributions of interval including b: i_b = i_a call interval( x, b, i_b, iflag ) select case ( iflag ) case ( -1 ) ! b < x(1) write (gol,'("interval is outside x :")') write (gol,'(" b : ",es12.4)') a; call goErr write (gol,'(" x(1) : ",es12.4)') x(1); call goErr write (gol,'("ERROR in ",a)') rname; status=1; return case ( 0 ) ! x(i_b) < b < x(i_b+1) if ( i_b > i_a ) then ! b in other interval; add contrib [x(i_b),b] f = ( b - x(i_b) ) / ( x(i_b+1) - x(i_b) ) else ! a and b in same interval; substract contrib [b,x(i_b+1)] f = - ( x(i_b+1) - b ) / ( x(i_b+1) - x(i_b) ) end if c = c + y(i_b) * f if ( present(fac) ) fac(i_b) = fac(i_b) + f case ( 1 ) ! b > x(n+1) write (gol,'("interval exceeds x :")') write (gol,'(" b : ",es12.4)') b; call goErr write (gol,'(" x(n+1) : ",es12.4)') x(n+1); call goErr write (gol,'("ERROR in ",a)') rname; status=1; return case default write (gol,'("unsupported iflag from call to Interval : ",i6)') iflag write (gol,'("in ",a)') rname; call goErr; status=1; return end select ! add contributions of intermediate intervals if ( present(fac) ) then do i = i_a+1, i_b-1 c = c + y(i) fac(i) = fac(i) + 1.0 end do else do i = i_a+1, i_b-1 c = c + y(i) end do end if ! set index of last interval (including b): ilast = i_b ! apply factor for b