Skip to content

Commit 56822b5

Browse files
committed
- Added preliminary implementation for Legendre polynomials
- Added preliminary implementation for Legendre polynomial first derivatives - Added preliminary implementation for Gauss-Legendre quadrature points/weights - Added preliminary implementation for Gauss-Legendre-Lobatto quadrature points/weights
1 parent f4f6045 commit 56822b5

File tree

6 files changed

+257
-0
lines changed

6 files changed

+257
-0
lines changed

src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ set(SRC
4141
stdlib_kinds.f90
4242
stdlib_logger.f90
4343
stdlib_system.F90
44+
stdlib_functions.f90
45+
stdlib_functions_legendre.f90
46+
stdlib_quadrature_gauss.f90
4447
${outFiles}
4548
)
4649

src/Makefile.manual

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ SRC = f18estop.f90 \
44
stdlib_bitsets_64.f90 \
55
stdlib_bitsets_large.f90 \
66
stdlib_error.f90 \
7+
stdlib_functions.f90 \
8+
stdlib_functions_legendre.f90 \
79
stdlib_io.f90 \
810
stdlib_kinds.f90 \
911
stdlib_linalg.f90 \
@@ -12,6 +14,7 @@ SRC = f18estop.f90 \
1214
stdlib_optval.f90 \
1315
stdlib_quadrature.f90 \
1416
stdlib_quadrature_trapz.f90 \
17+
stdlib_quadrature_gauss.f90 \
1518
stdlib_stats.f90 \
1619
stdlib_stats_mean.f90 \
1720
stdlib_stats_moment.f90 \
@@ -50,6 +53,8 @@ stdlib_bitsets.o: stdlib_kinds.o
5053
stdlib_bitsets_64.o: stdlib_bitsets.o
5154
stdlib_bitsets_large.o: stdlib_bitsets.o
5255
stdlib_error.o: stdlib_optval.o
56+
stdlib_functions.o: stdlib_kinds.o
57+
stdlib_functions_legendre.o: stdlib_kinds.o
5358
stdlib_io.o: \
5459
stdlib_error.o \
5560
stdlib_optval.o \
@@ -58,6 +63,7 @@ stdlib_linalg_diag.o: stdlib_kinds.o
5863
stdlib_logger.o: stdlib_ascii.o stdlib_optval.o
5964
stdlib_optval.o: stdlib_kinds.o
6065
stdlib_quadrature.o: stdlib_kinds.o
66+
stdlib_quadrature_gauss.o: stdlib_kinds.o
6167
stdlib_stats_mean.o: \
6268
stdlib_optval.o \
6369
stdlib_kinds.o \

src/stdlib_functions.f90

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module stdlib_functions
2+
use stdlib_kinds, only: sp, dp, qp
3+
4+
implicit none
5+
6+
private
7+
8+
public :: legendre
9+
public :: dlegendre
10+
11+
12+
interface legendre
13+
!! version: experimental
14+
!!
15+
!! Legendre polynomial
16+
pure elemental module function legendre_fp64(N,x) result(L)
17+
integer, intent(in) :: N
18+
real(dp), intent(in) :: x
19+
real(dp) :: L
20+
end function
21+
end interface
22+
23+
interface dlegendre
24+
!! version: experimental
25+
!!
26+
!! First derivative Legendre polynomial
27+
pure elemental module function dlegendre_fp64(N,x) result(DL)
28+
integer, intent(in) :: N
29+
real(dp), intent(in) :: x
30+
real(dp) :: DL
31+
end function
32+
end interface
33+
34+
end module stdlib_functions

src/stdlib_functions_legendre.f90

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
submodule (stdlib_functions) stdlib_functions_legendre
2+
use stdlib_kinds, only: sp, dp, qp
3+
implicit none
4+
5+
contains
6+
7+
! derivatives of Legendre polynomials
8+
! unspecified behaviour if N is negative
9+
pure elemental module function dlegendre_fp64(N,x) result(DL)
10+
integer, intent(in) :: N
11+
real(dp), intent(in) :: x
12+
real(dp) :: DL
13+
14+
select case(N)
15+
case(0)
16+
DL = 0
17+
case(1)
18+
DL = 1
19+
case default
20+
block
21+
real(dp) :: L_down1, L_down2, L
22+
real(dp) :: DL_down1, DL_down2
23+
integer :: i
24+
25+
L_down1 = x
26+
DL_down1 = 1
27+
28+
L_down2 = 1
29+
DL_down2 = 0
30+
31+
do i = 2, N
32+
L = (2*i-1)*x*L_down1/i - (i-1)*L_down2/i
33+
DL = DL_down2 + (2*i-1)*L_down1
34+
L_down2 = L_down1
35+
L_down1 = L
36+
DL_down2 = DL_down1
37+
DL_down1 = DL
38+
end do
39+
end block
40+
end select
41+
end function
42+
43+
! Legendre polynomials
44+
! unspecified behaviour if N is negative
45+
pure elemental module function legendre_fp64(N,x) result(L)
46+
integer, intent(in) :: N
47+
real(dp), intent(in) :: x
48+
real(dp) :: L
49+
select case(N)
50+
case(0)
51+
L = 1
52+
case(1)
53+
L = x
54+
case default
55+
block
56+
real(dp) :: L_down1, L_down2
57+
integer :: i
58+
59+
L_down1 = x
60+
L_down2 = 1
61+
62+
do i = 2, N
63+
L = (2*i-1)*x*L_down1/i - (i-1)*L_down2/i
64+
L_down2 = L_down1
65+
L_down1 = L
66+
end do
67+
end block
68+
end select
69+
end function
70+
71+
end submodule
72+
73+

src/stdlib_quadrature.fypp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module stdlib_quadrature
1212
public :: trapz_weights
1313
public :: simps
1414
public :: simps_weights
15+
public :: gauss_legendre
16+
public :: gauss_legendre_lobatto
1517

1618

1719
interface trapz
@@ -90,6 +92,28 @@ module stdlib_quadrature
9092
end interface simps_weights
9193

9294

95+
interface gauss_legendre
96+
!! version: experimental
97+
!!
98+
!! Computes Gauss-Legendre quadrature nodes and weights.
99+
pure module subroutine gauss_legendre_fp64 (x, w, interval)
100+
real(dp), intent(out) :: x(:), w(:)
101+
real(dp), intent(in), optional :: interval(2)
102+
end subroutine
103+
end interface gauss_legendre
104+
105+
106+
interface gauss_legendre_lobatto
107+
!! version: experimental
108+
!!
109+
!! Computes Gauss-Legendre-Lobatto quadrature nodes and weights.
110+
pure module subroutine gauss_legendre_lobatto_fp64 (x, w, interval)
111+
real(dp), intent(out) :: x(:), w(:)
112+
real(dp), intent(in), optional :: interval(2)
113+
end subroutine
114+
end interface gauss_legendre_lobatto
115+
116+
93117
! Interface for a simple f(x)-style integrand function.
94118
! Could become fancier as we learn about the performance
95119
! ramifications of different ways to do callbacks.
@@ -103,4 +127,6 @@ module stdlib_quadrature
103127
#:endfor
104128
end interface
105129

130+
131+
106132
end module stdlib_quadrature

src/stdlib_quadrature_gauss.f90

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
submodule (stdlib_quadrature) stdlib_quadrature_gauss
2+
use stdlib_kinds, only: sp, dp, qp
3+
use stdlib_functions, only: legendre, dlegendre
4+
implicit none
5+
6+
real(dp), parameter :: PI = acos(-1._dp)
7+
real(dp), parameter :: tolerance = 4._dp * epsilon(1._dp)
8+
integer, parameter :: newton_iters = 100
9+
10+
contains
11+
12+
pure module subroutine gauss_legendre_fp64 (x, w, interval)
13+
real(dp), intent(out) :: x(:), w(:)
14+
real(dp), intent(in), optional :: interval(2)
15+
16+
associate (N => size(x)-1 )
17+
select case (N)
18+
case (0)
19+
x = 0._dp
20+
w = 2._dp
21+
case (1)
22+
x = [-sqrt(1._dp/3._dp), sqrt(1._dp/3._dp)]
23+
w = [1._dp, 1._dp]
24+
case default
25+
block
26+
integer :: i,j
27+
real(dp) :: leg, dleg, delta
28+
29+
do i = 0, int(floor((N+1)/2._dp)-1)
30+
x(i+1) = -cos((2*i+1)/(2._dp*N+2._dp) * PI)
31+
do j = 0, newton_iters-1
32+
leg = legendre(N+1,x(i+1))
33+
dleg = dlegendre(N+1,x(i+1))
34+
delta = -leg/dleg
35+
x(i+1) = x(i+1) + delta
36+
if ( abs(delta) <= tolerance * abs(x(i+1)) ) exit
37+
end do
38+
x(N-i+1) = -x(i+1)
39+
40+
dleg = dlegendre(N+1,x(i+1))
41+
w(i+1) = 2._dp/((1-x(i+1)**2)*dleg**2)
42+
w(N-i+1) = w(i+1)
43+
end do
44+
45+
if (mod(N,2) == 0) then
46+
x(N/2+1) = 0.0
47+
48+
dleg = dlegendre(N+1, 0.0_dp)
49+
w(N/2+1) = 2._dp/(dleg**2)
50+
end if
51+
end block
52+
end select
53+
end associate
54+
55+
if (present(interval)) then
56+
associate ( a => interval(1) , b => interval(2) )
57+
x = 0.5*(b-a)*x+0.5*(b+a)
58+
w = 0.5*(b-a)*w
59+
end associate
60+
end if
61+
end subroutine
62+
63+
pure module subroutine gauss_legendre_lobatto_fp64 (x, w, interval)
64+
real(dp), intent(out) :: x(:), w(:)
65+
real(dp), intent(in), optional :: interval(2)
66+
67+
associate (N => size(x)-1)
68+
select case (N)
69+
case (1)
70+
x = [-1._dp, 1._dp]
71+
w = [ 1._dp, 1._dp]
72+
case default
73+
block
74+
integer :: i,j
75+
real(dp) :: leg, dleg, delta
76+
77+
x(1) = -1._dp
78+
x(N+1) = 1._dp
79+
w(1) = 2._dp/(N*(N+1._dp))
80+
w(N+1) = 2._dp/(N*(N+1._dp))
81+
82+
do i = 1, int(floor((N+1)/2._dp)-1)
83+
x(i+1) = -cos( (i+0.25_dp)*PI/N - 3/(8*N*PI*(i+0.25_dp)))
84+
do j = 0, newton_iters-1
85+
leg = legendre(N+1,x(i+1)) - legendre(N-1,x(i+1))
86+
dleg = dlegendre(N+1,x(i+1)) - dlegendre(N-1,x(i+1))
87+
delta = -leg/dleg
88+
x(i+1) = x(i+1) + delta
89+
if ( abs(delta) <= tolerance * abs(x(i+1)) ) exit
90+
end do
91+
x(N-i+1) = -x(i+1)
92+
93+
leg = legendre(N, x(i+1))
94+
w(i+1) = 2._dp/(N*(N+1._dp)*leg**2)
95+
w(N-i+1) = w(i+1)
96+
end do
97+
98+
if (mod(N,2) == 0) then
99+
x(N/2+1) = 0.0
100+
101+
leg = legendre(N, 0.0_dp)
102+
w(N/2+1) = 2._dp/(N*(N+1._dp)*leg**2)
103+
end if
104+
end block
105+
end select
106+
end associate
107+
108+
if (present(interval)) then
109+
associate ( a => interval(1) , b => interval(2) )
110+
x = 0.5*(b-a)*x+0.5*(b+a)
111+
w = 0.5*(b-a)*w
112+
end associate
113+
end if
114+
end subroutine
115+
end submodule

0 commit comments

Comments
 (0)