program pmethod ! !******************************************************************************* ! !! PMETHOD implements the P-version of the finite element method. ! ! ! Programmed by Max Gunzburger and Teresa Hodge. ! ! ! Program to solve the one dimensional problem: ! ! - d/dX (P dU/dX) + Q U = F ! ! by the finite-element method using a sequence of polynomials ! which satisfy the boundary conditions and are orthogonal ! with respect to the inner product: ! ! (U,V) = Integral (-1 to 1) P U' V' + Q U V dx ! ! Here U is an unknown scalar function of X defined on the ! interval [-1,1], and P, Q and F are given functions of X. ! ! The boundary values are U(-1) = U(1)=0. ! ! ! Two sample problems: ! ! U = 1-x**4, P=1, Q=1, F=1.0+12.0*x**2-x**4 ! ! U = cos(0.5*pi*x), P=1, Q=0, F=0.25*pi*pi*cos(0.5*pi*x) ! ! The program should be able to get the exact solution for ! the first problem, using NP = 2. ! ! Parameters: ! ! REAL A(0:NP). ! A(I) contains the square of the norm of the I-th basis ! function. ! ! REAL ALPHA(NP). ! ALPHA(I) contains one of the coefficients of a recurrence ! relationship that defines the basis functions. ! ! REAL B(0:NP,0:NP). ! B(I,J) contains the dot product of the I-th and J-th ! basis functions. The basis functions should be orthogonal, ! so B(I,J) should be 0 unless I equals J. ! ! REAL BETA(NP). ! BETA(I) contains one of the coefficients of a recurrence ! relationship that defines the basis functions. ! ! REAL F(0:NP). ! F contains the basis function coefficients that form the ! representation of the solution U. That is, ! ! U(X) = SUM (I=0 to NP) F(I) * BASIS(I)(X) ! ! where "BASIS(I)(X)" means the I-th basis function ! evaluated at the point X. ! ! INTEGER NP. ! The highest degree polynomial to use. ! ! INTEGER NPRINT. ! The number of points at which the computed solution ! should be printed out at the end of the computation. ! ! INTEGER QUAD_NUM is the number of points used in the quadrature rule. ! ! REAL QUAD_W(QUAD_NUM) are the quadrature weights. ! ! REAL QUAD_X(QUAD_NUM) are the quadrature abscissas. ! implicit none ! integer, parameter :: np = 2 integer, parameter :: nprint = 10 integer, parameter :: quad_num = 10 ! real a(0:np) real alpha(np) real b(0:np,0:np) real beta(np) real f(0:np) real quad_w(quad_num) real quad_x(quad_num) ! call timestamp ( ) print *, ' ' print *, 'PMETHOD' print *, ' ' print *, 'Solve the two-point boundary value problem' print *, ' ' print *, ' - d/dX (P dU/dX) + Q U = F' print *, ' ' print *, 'on the interval [-1,1], with' print *, 'U(-1) = U(1)=0.' print *, ' ' print *, 'The P method is used, which represents U as' print *, 'a weighted sum of orthogonal polynomials.' print *, ' ' print *, ' ' print *, 'Highest degree polynomial to use is ', np print *, 'Number of points to be used for output = ', nprint ! ! Get quadrature abscissas and weights for interval [-1,1]. ! call quad ( quad_num, quad_w, quad_x ) ! ! Compute the constants for the recurrence relationship ! that defines the basis functions. ! call alpbet ( a, alpha, beta, np, quad_num, quad_w, quad_x ) ! ! Test the orthogonality of the basis functions. ! call ortho ( a, alpha, b, beta, np, quad_num, quad_w, quad_x ) ! ! Solve for the solution of the problem, in terms of coefficients ! of the basis functions. ! call sol ( a, alpha, beta, f, np, quad_num, quad_w, quad_x ) ! ! Print out the solution, evaluated at each of the NPRINT points. ! call out ( alpha, beta, f, np, nprint ) ! ! Compare the computed and exact solutions. ! call exact ( alpha, beta, f, np, nprint, quad_num, quad_w, quad_x ) write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'PMETHOD' write ( *, '(a)' ) ' Normal end of execution.' stop end subroutine alpbet ( a, alpha, beta, np, quad_num, quad_w, quad_x ) ! !******************************************************************************* ! !! ALPBET calculates the constants in the recurrence relationship. ! ! ! Discussion: ! ! ALPHA and BETA are the constants in the three ! term recurrence relation for the orthogonal basis functions ! on [-1,1]. ! ! The routine also calculates A, the square of the norm of each basis ! function. ! implicit none ! integer np integer quad_num ! real a(0:np) real alpha(np) real beta(np) integer i integer iq integer k real pp real q real qm1 real qm1x real qm2 real qm2x real qq real quad_w(quad_num) real quad_x(quad_num) real qx real s real ss real su real sv real t real u real v real x ! ss = 0.0E+00 su = 0.0E+00 do iq = 1, quad_num x = quad_x(iq) s = 4.0*pp(x)*x**2 + qq(x)*(1.0-x*x)**2 u = 2.0*pp(x)*x*(3.0*x**2-1.0) + x*qq(x)*(1.0-x*x)**2 ss = ss + s*quad_w(iq) su = su + u*quad_w(iq) end do beta(1) = 0.0E+00 a(0) = ss alpha(1) = su/ss do i = 2, np+1 ss = 0.0E+00 su = 0.0E+00 sv = 0.0E+00 do iq = 1, quad_num x = quad_x(iq) q = 1.0 qm1 = 0.0 qx = 0.0 qm1x = 0.0 do k = 1,i-1 qm2 = qm1 qm1 = q qm2x = qm1x qm1x = qx q = (x-alpha(k))*qm1-beta(k)*qm2 qx = qm1+(x-alpha(k))*qm1x-beta(k)*qm2x end do t = 1.0-x**2 s = pp(x)*(t*qx-2.0*x*q)**2+qq(x)*(t*q)**2 u = pp(x)*(x*t*qx+(1.-3.0*x**2)*q)*(t*qx-2.0*x*q)+x*qq(x)*(t*q)**2 v = pp(x)*(x*t*qx+(1.0-3.0*x**2)*q)*(t*qm1x-2.0*x*qm1)+x*qq(x)*t**2*q*qm1 ss = ss+s*quad_w(iq) su = su+u*quad_w(iq) sv = sv+v*quad_w(iq) end do a(i-1) = ss if ( i <= np ) then alpha(i) = su/ss beta(i) = sv/a(i-2) end if end do return end subroutine exact ( alpha, beta, f, np, nprint, quad_num, quad_w, quad_x ) ! !******************************************************************************* ! !! EXACT compares the computed and exact solutions. ! implicit none ! integer np integer quad_num ! real alpha(np) real beta(np) real big_l2 real error real f(0:np) integer i integer ip integer j integer k integer nprint integer, parameter :: nsub = 10 real phii real phiix real quad_w(quad_num) real quad_x(quad_num) real ue real uex real up real x real xl real xr ! write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'Comparison of computed and exact solutions:' write ( *, '(a)' ) ' ' write ( *, '(a)' ) ' X U computed U exact Difference' write ( *, '(a)' ) ' ' do i = 0, nprint x = real(2*i-nprint)/real(nprint) ue = uex(x) up = 0.0E+00 do j = 0, np call phi(alpha,beta,j,np,phii,phiix,x) up = up+phii*f(j) end do write(*,'(f8.4,3g14.6)') x,up,ue, ue - up end do ! ! Compute the big L2 error. ! big_l2 = 0.0E+00 do i = 1, nsub xl = real ( 2 * i - nsub - 1 ) / real ( nsub ) xr = real ( 2 * i - nsub ) / real ( nsub ) do j = 1, quad_num x = ( xl * ( 1.0E+00 - quad_x(j) ) & + xr * ( 1.0E+00 + quad_x(j) ) ) / 2.0E+00 up = 0.0E+00 do k = 0, np call phi ( alpha, beta, k, np, phii, phiix, x ) up = up + phii * f(k) end do big_l2 = big_l2 + ( up - uex(x) )**2 * quad_w(j) * ( xr - xl ) / 2.0E+00 end do end do big_l2 = sqrt ( big_l2 ) write ( *, '(a)' ) ' ' write ( *, '(a,g14.6)' ) 'Big L2 error = ', big_l2 return end function ff ( x ) ! !******************************************************************************* ! !! FF evaluates the right hand side function F(X) at any point X. ! implicit none ! real ff real, parameter :: pi = 3.14159265E+00 real x ! ! Test problem 1 ! ! ff = 1.0+12.0*x**2-x**4 ! ! Test problem 2 ! ff = 0.25 * pi**2 * cos ( 0.5 * pi * x ) return end subroutine ortho ( a, alpha, b, beta, np, quad_num, quad_w, quad_x ) ! !******************************************************************************* ! !! ORTHO tests the basis functions for orthogonality. ! implicit none ! integer np integer quad_num ! real a(0:np) real alpha(np) real b(0:np,0:np) real beta(np) real bij integer i integer iq integer j real phii real phiix real phij real phijx real pp real qq real quad_w(quad_num) real x real quad_x(quad_num) ! ! Zero out the B array, so we can start summing up the dot products. ! b(0:np,0:np) = 0.0E+00 ! ! Approximate the integral of the product of basis function ! I and basis function J over the interval [-1,1]. ! ! We expect to get zero, except when I and J are equal, ! when we should get A(I). ! do iq = 1,quad_num x = quad_x(iq) do i = 0,np call phi(alpha,beta,i,np,phii,phiix,x) do j = 0,np call phi(alpha,beta,j,np,phij,phijx,x) bij = pp(x)*phiix*phijx+qq(x)*phii*phij b(i,j) = b(i,j)+bij*quad_w(iq) end do end do end do ! ! Print out the results of the test. ! print *, ' ' print *, 'Basis function orthogonality test:' print *, ' ' print *, ' i j b(i,j)/a(i)' print *, ' ' do i = 0, np print *, ' ' do j = 0, np write(*,'(2i4,g14.6)') i,j,b(i,j)/a(i) end do end do return end subroutine out ( alpha, beta, f, np, nprint ) ! !******************************************************************************* ! !! OUT prints out the computed solution. ! implicit none ! integer np ! real alpha(np) real beta(np) real f(0:np) integer i integer ip integer nprint real phii real phiix real up real x ! print *, ' ' print *, 'Representation of solution:' print *, ' ' print *, 'Basis function coefficients:' print *, ' ' do i = 0, np write(*,'(i4,g14.6)')i,f(i) end do print *, ' ' print *, ' ' print *, ' X Approximate Solution' print *, ' ' do ip = 0, nprint x = real(2*ip-nprint)/real(nprint) up = 0.0E+00 do i = 0, np call phi(alpha,beta,i,np,phii,phiix,x) up = up+phii*f(i) end do write(*,'(2g14.6)')x,up end do print *, ' ' return end subroutine phi ( alpha, beta, i, np, phii, phiix, x ) ! !******************************************************************************* ! !! PHI evaluates the I-th basis function at the point X. ! ! I can be between 0 and NP. ! implicit none ! integer np ! real alpha(np) real beta(np) integer i integer j real phii real phiix real q real qm1 real qm1x real qm2 real qm2x real qx real t real x ! qm1 = 0.0E+00 q = 1.0E+00 qm1x = 0.0E+00 qx = 0.0E+00 do j = 1, i qm2 = qm1 qm1 = q qm2x = qm1x qm1x = qx t = x-alpha(j) q = t*qm1-beta(j)*qm2 qx = qm1+t*qm1x-beta(j)*qm2x end do t = 1.0-x**2 phii = t*q phiix = t*qx-2.0*x*q return end function pp ( x ) ! !******************************************************************************* ! !! PP returns the value of the coefficient function P(X). ! implicit none ! real pp real x ! ! Test problem 1 ! ! pp = 1.0E+00 ! ! Test problem 2 ! pp = 1.0E+00 return end subroutine quad ( quad_num, quad_w, quad_x ) ! !******************************************************************************* ! !! QUAD returns the abscissas and weights for gaussian quadrature on [-1,1]. ! implicit none ! integer quad_num ! real quad_w(quad_num) real quad_x(quad_num) ! ! Quadrature points on [-1,1] ! quad_x(1) = -0.973906528517172 quad_x(2) = -0.865063366688985 quad_x(3) = -0.679409568299024 quad_x(4) = -0.433395394129247 quad_x(5) = -0.148874338981631 quad_x(6) = 0.148874338981631 quad_x(7) = 0.433395394129247 quad_x(8) = 0.679409568299024 quad_x(9) = 0.865063366688985 quad_x(10) = 0.973906528517172 ! ! Weight factors ! quad_w(1) = 0.066671344308688 quad_w(2) = 0.149451349150581 quad_w(3) = 0.219086362515982 quad_w(4) = 0.269266719309996 quad_w(5) = 0.295524224714753 quad_w(6) = 0.295524224714753 quad_w(7) = 0.269266719309996 quad_w(8) = 0.219086362515982 quad_w(9) = 0.149451349150581 quad_w(10) = 0.066671344308688 return end function qq ( x ) ! !******************************************************************************* ! !! QQ returns the value of the coefficient function Q(X). ! implicit none ! real qq real x ! ! Test problem 1 ! ! qq = 1.0 ! ! Test problem 2 ! qq = 0.0 return end subroutine sol ( a, alpha, beta, f, np, quad_num, quad_w, quad_x ) ! !******************************************************************************* ! !! SOL solves a linear system for the finite element coefficients. ! implicit none ! integer np integer quad_num ! real a(0:np) real alpha(np) real beta(np) real f(0:np) real ff integer i integer iq real phii real phiix real t real quad_w(quad_num) real x real quad_x(quad_num) ! f(0:np) = 0.0E+00 do iq = 1, quad_num x = quad_x(iq) t = ff(x) * quad_w(iq) do i = 0, np call phi(alpha,beta,i,np,phii,phiix,x) f(i) = f(i) + phii * t end do end do f(0:np) = f(0:np) / a(0:np) return end function uex ( x ) ! !******************************************************************************* ! !! UEX returns the value of the exact solution at a point X. ! implicit none ! real, parameter :: pi = 3.14159265E+00 real uex real x ! ! Test problem 1 ! ! uex = 1.0-x**4 ! ! Test problem 2 ! uex = cos ( 0.5 * pi * x ) return end subroutine timestamp ( ) ! !******************************************************************************* ! !! TIMESTAMP prints the current YMDHMS date as a time stamp. ! ! ! Example: ! ! May 31 2001 9:45:54.872 AM ! ! Modified: ! ! 31 May 2001 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! None ! implicit none ! character ( len = 8 ) ampm integer d character ( len = 8 ) date integer h integer m integer mm character ( len = 9 ), parameter, dimension(12) :: month = (/ & 'January ', 'February ', 'March ', 'April ', & 'May ', 'June ', 'July ', 'August ', & 'September', 'October ', 'November ', 'December ' /) integer n integer s character ( len = 10 ) time integer values(8) integer y character ( len = 5 ) zone ! call date_and_time ( date, time, zone, values ) y = values(1) m = values(2) d = values(3) h = values(5) n = values(6) s = values(7) mm = values(8) if ( h < 12 ) then ampm = 'AM' else if ( h == 12 ) then if ( n == 0 .and. s == 0 ) then ampm = 'Noon' else ampm = 'PM' end if else h = h - 12 if ( h < 12 ) then ampm = 'PM' else if ( h == 12 ) then if ( n == 0 .and. s == 0 ) then ampm = 'Midnight' else ampm = 'AM' end if end if end if write ( *, '(a,1x,i2,1x,i4,2x,i2,a1,i2.2,a1,i2.2,a1,i3.3,1x,a)' ) & trim ( month(m) ), d, y, h, ':', n, ':', s, '.', mm, trim ( ampm ) return end