!//当theta<0.1°时,Pn2(cos(theta)) 计算不准! !//当theta=90°时,Pn, Pn2(cos(theta)) 计算不准! !// 一般情况可以使用. !//当theta<0.1°时,Pn2(cos(theta)) 计算不准! !//当theta=90°时,Pn, Pn2(cos(theta)) 计算不准! !// 一般情况可以使用. module LegendreP012 !// 计算连带勒让德(不含"Condon-Shortley phase") implicit none contains !// 函数LgendrePn1,LgendrePn2需要调用LegndrePn. function LegendrePn(theta,degree) result(Pn) !// n阶勒让德函数 !// theat in Unit of Degree, that is degree measure !// degree is the degree of Legendre Functions implicit none real (kind=8),intent(in) :: theta integer(kind=8),intent(in) :: degree real (kind=8) :: Pn !// temp variable integer(kind=8) :: l !// for degree loop real(kind=8) :: pfirst,psecond,pthird !// p_(k-1),p_k,p_(k+1) real(kind=8) :: ct,pi !// theta in unit of radian,pi=3.14 real(kind=8) :: x !// cos(ct) pi=3.14159265358979d0 ct=theta/180.0d0*pi x=cos(ct) l=0 select case (degree) case(0) Pn=1.0d0 case(1) Pn=x case(2:) pfirst=1.0d0 psecond=x !// 递推计算 A,B->C l=2 do pthird=(2-1.0d0/l)*x*psecond-(1-1.0d0/l)*pfirst if (l==degree) then Pn=pthird exit end if pfirst=psecond psecond=pthird l=l+1 end do case default write(*,*) 'Error in "LegendrePn", Degree of Legendre Function should not be less than 0!' end select end function LegendrePn function LegendrePn1(theta,degree) result(Pn1) !// n阶1次勒让德函数 !// Pn1, the degree is n and the order is 1 !// theat in Unit of Degree, that is degree measure !// degree is the degree of Legendre Functions implicit none real (kind=8),intent(in) :: theta integer(kind=8),intent(in) :: degree real (kind=8) :: Pn1 !// temp variable real(kind=8) :: pfirst,psecond real(kind=8) :: ct,pi !// theta in unit of radian,pi=3.14 real(kind=8) :: x,y !// cos(ct), sin(ct) pi=3.14159265358979d0 ct=theta/180.0d0*pi x=cos(ct) y=sin(ct) select case (degree) case(0) Pn1=0.0d0 case(1:) pfirst=LegendrePn(theta,degree-1) psecond=LegendrePn(theta,degree) Pn1=degree/y*(pfirst-x*psecond) case default write(*,*) 'Error in "LegendrePn1", Degree of Legendre Function should not be less than 0!' end select end function LegendrePn1 function LegendrePn2(theta,degree) result(Pn2) !// n阶2次勒让德函数 !// Pn2, the degree is n and the order is 1 !// theat in Unit of Degree, that is degree measure !// degree is the degree of Legendre Functions !// When theta is very small, LegendrePn2 will give wrong result! implicit none real (kind=8),intent(in) :: theta integer(kind=8),intent(in) :: degree real (kind=8) :: Pn2 !// temp variable real(kind=8) :: pfirst,psecond real(kind=8) :: ct,pi !// theta in unit of radian,pi=3.14 real(kind=8) :: x,y,x2,factor1,factor2 !// cos(ct), sin(ct), cos(2*ct) pi=3.14159265358979d0 ct=theta/180.0d0*pi x=cos(ct) y=sin(ct) x2=cos(2.0d0*ct) select case (degree) case(0,1) Pn2=0.0d0 case(2:) !// use recursion formula to calculate pfirst=LegendrePn(theta,degree-1) psecond=LegendrePn(theta,degree) factor1=2*x factor2=-((degree+1)*y*y+2*x*x) Pn2=degree/(y*y)*(factor1*pfirst+factor2*psecond) !// 或者 ! Pn2=2*degree/(1-x2)*(factor1*pfirst+factor2*psecond) ! ! !// 或者 ! factor1=2*x/(y*y) ! factor2=1.0d0*(1-degree)-2.0d0/(y*y) ! Pn2=degree*factor1*pfirst+degree*factor2*psecond case default write(*,*) 'Error in "LegendrePn2", Degree of Legendre Function should not be less than 0!' end select end function LegendrePn2 !// 子例行程序CLegendrePn,CLegendrePn1,CLegendrePn2是独立的,可以单独使用. subroutine CLegendrePn(choose,ntheta,thetalist,degree,LegendrePn,LegendrePn_sine,LegendrePnd,LegendrePndd) !// 使用时: 需要3步1),2),3) !// 1) call CLegendrePn ('prepare', 2_8,list_angle(1:2),loop_degree,LegendrePnd=pnd) !// 2) call CLegendrePn ('keep', 2_8,list_angle(1:2),loop_degree,LegendrePnd=pnd) loop_degree=0/1/2 开始连续调用即可. !// 3) call CLegendrePn ('deallocate',2_8,list_angle(1:2),loop_degree,LegendrePnd=pnd) !// implicit none character(len=*),intent(in) :: choose !// prepare,keep,deallocate integer (kind=8),intent(in) :: ntheta !// length of theta list real (kind=8),intent(in) :: thetalist(ntheta) !// theats in unit of degree, that is degree measure integer (kind=8),intent(in) :: degree !// Legendre的阶次 real (kind=8),intent(out),optional :: LegendrePn(ntheta),LegendrePn_sine(ntheta),LegendrePnd(ntheta),LegendrePndd(ntheta) !// 勒让德函数以及导数: Pn, Pn/sine(theta), dPn(cos(theta))/d(theta), d^2Pn(theta)/d(theta^2) 二阶导数 !// temp variable integer(kind=8) :: theta_loop real (kind=8) :: theta real (kind=8) :: Pn(ntheta),Pn_sine(ntheta),Pnd(ntheta),Pndd(ntheta) !// 勒让德函数以及导数: Pn, Pn/sine(theta), dPn(cos(theta))/d(theta) integer(kind=8) :: l !// for degree loop real (kind=8),allocatable :: pfirst(:),psecond(:) !// p_(k-1),p_k integer(kind=8) :: allstatus1,allstatus2 real (kind=8) :: pthird(ntheta) !// p_(k+1) real (kind=8) :: ct,pi !// theta in unit of radian,pi=3.14 real (kind=8) :: xct,yct !// cos(ct),sin(ct) real (kind=8) :: xctlist(ntheta),yctlist(ntheta) !// all cos(ct), sin(ct) save :: pfirst,psecond !// 常数初值 pi=3.14159265358979d0 if (trim(choose)=='prepare' .or. trim(choose)=='PREPARE') then !// Legendre递推初值 if (.not.allocated(pfirst)) allocate(pfirst(ntheta)) if (.not.allocated(psecond)) allocate(psecond(ntheta)) pfirst=1.0d0 do theta_loop=1,ntheta theta=thetalist(theta_loop) ct=theta/180.0d0*pi xct=cos(ct) psecond(theta_loop)=xct end do Pn=0.0d0 Pn_sine=0.0d0 Pnd=0.0d0 Pndd=0.0d0 else if (trim(choose)=='keep' .or. trim(choose)=='KEEP') then !// get xct and yct do theta_loop=1,ntheta theta=thetalist(theta_loop) ct=theta/180.0d0*pi xctlist(theta_loop)=cos(ct) yctlist(theta_loop)=sin(ct) end do l=0 !// get result for different degree select case (degree) case(0) Pn=1.0d0 Pnd=0.0d0 Pndd=0.0d0 do theta_loop=1,ntheta yct=yctlist(theta_loop) Pn_sine(theta_loop)=1.0d0/yct end do case(1) do theta_loop=1,ntheta xct=xctlist(theta_loop) yct=yctlist(theta_loop) Pn(theta_loop)=xct Pn_sine(theta_loop)=xct/yct Pnd(theta_loop)=-yct Pndd(theta_loop)=-xct end do case(2:) !// 递推计算 A,B->C l=degree do theta_loop=1,ntheta xct=xctlist(theta_loop) yct=yctlist(theta_loop) pthird(theta_loop)=(2-1.0d0/l)*xct*psecond(theta_loop)-(1-1.0d0/l)*pfirst(theta_loop) Pn(theta_loop)=pthird(theta_loop) Pn_sine(theta_loop)=pthird(theta_loop)/yct Pnd(theta_loop)=l/yct*(xct*pthird(theta_loop)-psecond(theta_loop)) Pndd(theta_loop)=l/(yct**2)*(-(1+l*yct**2)*pthird(theta_loop)+xct*psecond(theta_loop)) pfirst(theta_loop)=psecond(theta_loop) psecond(theta_loop)=pthird(theta_loop) end do case default write(*,*) 'Error in "CLegendrePn", Degree of Legendre Function should not be less than 0!' end select else if (trim(choose)=='deallocate' .or. trim(choose)=='DEALLOCATE') then deallocate(pfirst, stat=allstatus1) deallocate(psecond,stat=allstatus2) if (allstatus1/=0 .or. allstatus2/=0) then write(*,*) 'Error! When deallocate arrays in "CLegendrePn"!' end if else write(*,*) 'Error! The first argument of "CLegendrePn" must be "prepare", or "keep", or "deallocate"!' end if !// 返回值 if(present(LegendrePn)) LegendrePn=Pn if(present(LegendrePn_sine)) LegendrePn_sine=Pn_sine if(present(LegendrePnd)) LegendrePnd=Pnd if(present(LegendrePndd)) LegendrePndd=Pndd end subroutine CLegendrePn subroutine CLegendrePn1(choose,ntheta,thetalist,degree,LegendrePn1,LegendrePn1_sine,LegendrePn1d,LegendrePn1dd) !Attation: we define Pn1 = -d(Pn)/d(theta), as it used in Dislocation's thory, and it DO NOT have Condon–Shortley phase, not Same as Mathematica! !// 使用方法和前面一样. !// 再递推,可以从0/1/2开始向后连续(Continue)调用 implicit none character(len=*),intent(in) :: choose !// prepare,keep,deallocate integer (kind=8),intent(in) :: ntheta !// length of theta list real (kind=8),intent(in) :: thetalist(ntheta) !// theat in unit of degree, that is degree measure integer (kind=8),intent(in) :: degree !// Legendre的阶次 real (kind=8),intent(out),optional :: LegendrePn1(ntheta),LegendrePn1_sine(ntheta),& LegendrePn1d(ntheta),LegendrePn1dd(ntheta) !// 勒让德函数以及导数: Pn1, Pn1/sine(theta), dPn1(cos(theta))/d(theta), d^2Pn1(cos(theta))/d(theta^2) !// temp variable real (kind=8) :: theta integer(kind=8) :: theta_loop real (kind=8) :: Pn1(ntheta),Pn1_sine(ntheta),Pn1d(ntheta),Pn1dd(ntheta) integer(kind=8) :: l !// for degree loop real (kind=8),allocatable:: pfirst(:),psecond(:) !// p_(k-1),p_k integer(kind=8) :: allstatus1,allstatus2 real (kind=8) :: pthird(ntheta) !// p_(k+1) real (kind=8) :: ct,pi !// theta in unit of radian,pi=3.14 real (kind=8) :: xct,yct !// cos(ct),sin(ct) real (kind=8) :: xctlist(ntheta),yctlist(ntheta) !// all cos(ct), sin(ct) save :: pfirst,psecond !// 常数初值 pi=3.14159265358979d0 if (trim(choose)=='prepare' .or. trim(choose)=='PREPARE') then !// Legendre递推初值 if (.not.allocated(pfirst)) allocate(pfirst(ntheta)) if (.not.allocated(psecond)) allocate(psecond(ntheta)) pfirst=1.0d0 do theta_loop=1,ntheta theta=thetalist(theta_loop) ct=theta/180.0d0*pi xct=cos(ct) psecond(theta_loop)=xct end do Pn1=0.0d0 Pn1_sine=0.0d0 Pn1d=0.0d0 Pn1dd=0.0d0 else if (trim(choose)=='keep' .or. trim(choose)=='KEEP') then !// get xct and yct do theta_loop=1,ntheta theta=thetalist(theta_loop) ct=theta/180.0d0*pi xctlist(theta_loop)=cos(ct) yctlist(theta_loop)=sin(ct) end do l=0 !// get result for different degree select case (degree) case(0) Pn1=0.0d0 Pn1_sine=0.0d0 Pn1d=0.0d0 Pn1dd=0.0d0 case(1) Pn1_sine=1.0d0 do theta_loop=1,ntheta xct=xctlist(theta_loop) yct=yctlist(theta_loop) Pn1(theta_loop)=(1-xct**2)/yct Pn1d(theta_loop)=xct Pn1dd(theta_loop)=-yct end do case(2:) !// 递推计算 A,B->C l=degree do theta_loop=1,ntheta xct=xctlist(theta_loop) yct=yctlist(theta_loop) pthird(theta_loop)=(2-1.0d0/l)*xct*psecond(theta_loop)-(1-1.0d0/l)*pfirst(theta_loop) Pn1(theta_loop)=-l/yct*(-psecond(theta_loop)+xct*pthird(theta_loop)) Pn1_sine(theta_loop)=Pn1(theta_loop)/yct Pn1d(theta_loop)=l/(yct**2)*(-xct*psecond(theta_loop)+(1+l*yct**2)*pthird(theta_loop)) Pn1dd(theta_loop)=l/(yct**3)*( (1-l-l**2 + (1+l+l**2)*(xct**2) )*psecond(theta_loop) + & (-2+(l**2)*(yct**2))*xct*pthird(theta_loop) ) pfirst(theta_loop)=psecond(theta_loop) psecond(theta_loop)=pthird(theta_loop) end do case default write(*,*) 'Error in "CLegendrePn1", Degree of Legendre Function should not be less than 0!' end select else if (trim(choose)=='deallocate' .or. trim(choose)=='DEALLOCATE') then deallocate(pfirst, stat=allstatus1) deallocate(psecond,stat=allstatus2) if (allstatus1/=0 .or. allstatus2/=0) then write(*,*) 'Error! When deallocate arrays in "CLegendrePn1"!' end if else write(*,*) 'Error! The first argument of "CLegendrePn1" must be "prepare", or "keep", or "deallocate"!' end if if(present(LegendrePn1)) LegendrePn1=Pn1 if(present(LegendrePn1_sine)) LegendrePn1_sine=Pn1_sine if(present(LegendrePn1d)) LegendrePn1d=Pn1d if(present(LegendrePn1dd)) LegendrePn1dd=Pn1dd end subroutine CLegendrePn1 subroutine CLegendrePn2(choose,ntheta,thetalist,degree,LegendrePn2,LegendrePn2_sine,LegendrePn2d,LegendrePn2dd) !// 使用时,必须先调用一次CLegendrePn2(theta,0_8,.true.,Pn2)! !// 再递推,可以从0/1/2开始向后连续(Continue)调用 implicit none character(len=*),intent(in) :: choose !// prepare,keep,deallocate integer (kind=8),intent(in) :: ntheta !// length of theta list real (kind=8),intent(in) :: thetalist(ntheta) !// theat in unit of degree, that is degree measure integer (kind=8),intent(in) :: degree !// Legendre的阶次 real (kind=8),intent(out),optional :: LegendrePn2(ntheta),LegendrePn2_sine(ntheta),& LegendrePn2d(ntheta),LegendrePn2dd(ntheta) !// 勒让德函数以及导数: Pn2, Pn2/sine(theta), dPn2(cos(theta))/d(theta) !// temp variable real (kind=8) :: theta integer(kind=8) :: theta_loop real (kind=8) :: Pn2(ntheta),Pn2_sine(ntheta),Pn2d(ntheta),Pn2dd(ntheta) integer(kind=8) :: l !// for degree loop real (kind=8),allocatable:: pfirst(:),psecond(:) !// p_(k-1),p_k integer(kind=8) :: allstatus1,allstatus2 real (kind=8) :: pthird(ntheta) !// p_(k+1) real (kind=8) :: ct,pi !// theta in unit of radian,pi=3.14 real (kind=8) :: xct,yct !// cos(ct),sin(ct) real (kind=8) :: xctlist(ntheta),yctlist(ntheta) !// all cos(ct), sin(ct) real (kind=8) :: factor1,factor2 save :: pfirst,psecond !// 常数初值 pi=3.14159265358979d0 if (trim(choose)=='prepare' .or. trim(choose)=='PREPARE') then !// Legendre递推初值 if (.not.allocated(pfirst)) allocate(pfirst(ntheta)) if (.not.allocated(psecond)) allocate(psecond(ntheta)) pfirst=1.0d0 do theta_loop=1,ntheta theta=thetalist(theta_loop) ct=theta/180.0d0*pi xct=cos(ct) psecond(theta_loop)=xct end do Pn2=0.0d0 Pn2_sine=0.0d0 Pn2d=0.0d0 Pn2dd=0.0d0 else if (trim(choose)=='keep' .or. trim(choose)=='KEEP') then !// get xct and yct do theta_loop=1,ntheta theta=thetalist(theta_loop) ct=theta/180.0d0*pi xctlist(theta_loop)=cos(ct) yctlist(theta_loop)=sin(ct) end do l=0 !// get result for different degree select case (degree) case(0,1) Pn2=0.0d0 Pn2_sine=0.0d0 Pn2d=0.0d0 Pn2dd=0.0d0 case(2:) !// 递推计算 A,B->C l=degree do theta_loop=1,ntheta xct=xctlist(theta_loop) yct=yctlist(theta_loop) factor1=2*xct factor2=-((l+1)*yct**2+2*xct**2) pthird(theta_loop)=(2-1.0d0/l)*xct*psecond(theta_loop)-(1-1.0d0/l)*pfirst(theta_loop) Pn2(theta_loop)=l/(yct**2)*(factor1*psecond(theta_loop)+factor2*pthird(theta_loop)) Pn2_sine(theta_loop)=Pn2(theta_loop)/yct factor1=(l**2+l+2)*yct**2-4 factor2=((l-l**2)*yct**2+4)*xct Pn2d(theta_loop)=l/(yct**3)*(factor1*psecond(theta_loop)+factor2*pthird(theta_loop)) factor1=2*(1+3*l-l**2) + (8-9*l-l**2)*(yct**2) + 2*(l**2-3*l+5)*(xct**2) factor2=-4*(l+1) + (l*(l+1)**2)*(yct**2) + 4*(l-2)*xct**2 - (l**2)*(l-1)*(xct**2)*(yct**2) Pn2dd(theta_loop)=l/(yct**4)*(factor1*xct*psecond(theta_loop)+factor2*pthird(theta_loop)) pfirst(theta_loop)=psecond(theta_loop) psecond(theta_loop)=pthird(theta_loop) end do case default write(*,*) 'Error in "CLegendrePn2", Degree of Legendre Function should not be less than 0!' end select else if (trim(choose)=='deallocate' .or. trim(choose)=='DEALLOCATE') then deallocate(pfirst, stat=allstatus1) deallocate(psecond,stat=allstatus2) if (allstatus1/=0 .or. allstatus2/=0) then write(*,*) 'Error! When deallocate arrays in "CLegendrePn2"!' end if end if if(present(LegendrePn2)) LegendrePn2=Pn2 if(present(LegendrePn2_sine)) LegendrePn2_sine=Pn2_sine if(present(LegendrePn2d)) LegendrePn2d=Pn2d if(present(LegendrePn2dd)) LegendrePn2dd=Pn2dd end subroutine CLegendrePn2 end module LegendreP012