program helmholtz ! !******************************************************************************* ! !! HELMHOLTZ solves a discretized Helmholtz equation. ! ! ! Discussion: ! ! The two dimensional region given is: ! ! -1 <= X <= +1 ! -1 <= Y <= +1 ! ! The region is discretized by a set of M by N nodes: ! ! P(I,J) = ( X(I), Y(J) ) ! ! X(I) = ( - M + 2*I - 1 ) / ( M - 1 ) ! Y(J) = ( - N + 2*J - 1 ) / ( N - 1 ) ! ! The Helmholtz equation for the scalar function U(X,Y) is ! ! - Uxx(X,Y) -Uyy(X,Y) + ALPHA * U(X,Y) = F(X,Y) ! ! where ALPHA is a positive constant. We suppose that Dirichlet ! boundary conditions are specified, that is, that the value of ! U(X,Y) is given for all points along the boundary. ! ! We suppose that the right hand side function F(X,Y) is specified in ! such a way that the exact solution is ! ! U(X,Y) = ( 1 - X**2 ) * ( 1 - Y**2 ) ! ! Using standard finite difference techniques, the second derivatives ! of U can be approximated by linear combinations of the values ! of U at neighboring points. Using this fact, the discretized ! differential equation becomes a set of linear equations of the form: ! ! A * U = F ! ! These linear equations are then solved using a form of the Jacobi ! iterative method with a relaxation factor. ! ! Modified: ! ! 18 March 2002 ! ! Author: ! ! Joseph Robicheaux, ! Sanjiv Shah, ! Kuck and Associates, Inc. (KAI) ! ! Directives are used in this code to achieve parallelism. ! All do loops are parallized with default 'static' scheduling. ! implicit none ! double precision alpha integer it_max integer m integer n double precision omega !$integer omp_get_num_procs double precision tol ! call timestamp ( ) write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'HELMHOLTZ' write ( *, '(a)' ) ' A program which solves the 2D Helmholtz equation.' write ( *, '(a)' ) ' ' write ( *, '(a)' ) ' This program includes Open MP directives, so that' write ( *, '(a)' ) ' it can be run sequentially, or in parallel.' ! !$write ( *, '(a)' ) ' ' !$write ( *, '(a)' ) ' This program is being run in parallel.' !$write ( *, '(a,i6)' ) ' The number of processors is ', omp_get_num_procs ( ) ! ! Set the parameters. ! m = 5 n = 5 alpha = 0.25D+00 omega = 1.0D+00 tol = 0.001D+00 it_max = 10 write ( *, '(a)' ) ' ' write ( *, '(a)' ) ' The region is [-1,1] x [-1,1].' write ( *, '(a,i6)' ) ' The number of nodes in the X direction is M = ', m write ( *, '(a,i6)' ) ' The number of nodes in the Y direction is N = ', n write ( *, '(a,g14.6)' ) & ' The scalar coefficient in the Helmholtz equation is ALPHA = ', alpha write ( *, '(a,g14.6)' ) & ' The relaxation value is OMEGA = ', omega write ( *, '(a,g14.6)' ) & ' The error tolerance is TOL = ', tol write ( *, '(a,i6)' ) & ' The maximum number of Jacobi iterations is IT_MAX = ', it_max ! ! Call the driver routine. ! call driver ( m, n, it_max, alpha, omega, tol ) write ( *, '(a)' ) ' ' write ( *, '(a)' ) 'HELMHOLTZ' write ( *, '(a)' ) ' Normal end of execution.' write ( *, '(a)' ) ' ' call timestamp ( ) stop end subroutine driver ( m, n, it_max, alpha, omega, tol ) ! !******************************************************************************* ! !! DRIVER allocates arrays and solves the problem. ! ! ! Modified: ! ! 18 March 2002 ! ! Author: ! ! Joseph Robicheaux, ! Sanjiv Shah, ! Kuck and Associates, Inc. (KAI) ! ! Parameters: ! ! Input, integer M, N, the number of grid points in the X and Y directions. ! ! Input, integer IT_MAX, the maximum number of Jacobi iterations allowed. ! ! Input, double precision ALPHA, the scalar coefficient in the ! Helmholtz equation. ! ! Input, double precision OMEGA, the relaxation parameter, which ! should be strictly between 0 and 2. For a pure Jacobi method, ! use OMEGA = 1. ! ! Input, double precision TOL, an error tolerance for the linear ! equation solver. ! implicit none ! integer m integer n ! double precision alpha double precision dx double precision dy double precision f(m,n) integer it_max double precision omega double precision tol double precision u(m,n) ! ! Initialize the data. ! call initialize ( m, n, alpha, u, f ) ! ! Solve the Helmholtz equation. ! call jacobi ( m, n, alpha, omega, u, f, tol, it_max ) ! ! Determine the error. ! call error_check ( m, n, alpha, u, f ) return end subroutine initialize ( m, n, alpha, u, f ) ! !******************************************************************************* ! !! INITIALIZE initializes the data. ! ! ! Discussion: ! ! The routine assumes that the exact solution and its second ! derivatives are given by the routine EXACT. ! ! The appropriate Dirichlet boundary conditions are determined ! by getting the value of U returned by EXACT. ! ! The appropriate right hand side function is determined by ! having EXACT return the values of U, UXX and UYY, and setting ! ! F = -UXX - UYY + ALPHA * U ! ! Modified: ! ! 20 March 2002 ! ! Author: ! ! Joseph Robicheaux, ! Sanjiv Shah, ! Kuck and Associates, Inc. (KAI) ! ! Parameters: ! ! Input, integer M, N, the number of grid points in the X and Y directions. ! ! Input, double precision ALPHA, the scalar coefficient in the ! Helmholtz equation. ALPHA should be positive. ! ! Output, double precision U(M,N), (the initial estimate of) the solution ! of the Helmholtz equation at the grid points. ! ! Output, double precision F(M,N), values of the right hand side function ! for the Helmholtz equation at the grid points. ! implicit none ! integer m integer n ! double precision alpha double precision dx double precision dy double precision f(m,n) double precision f_norm integer i integer j double precision u(m,n) double precision u_exact double precision uxx double precision uyy double precision x double precision y ! u(1:m,1:n) = 0.0D+00 f(1:m,1:n) = 0.0D+00 dx = 2.0D+00 / dble ( m - 1 ) dy = 2.0D+00 / dble ( n - 1 ) ! ! Set the boundary conditions. ! j = 1 y = -1.0D+00 + dy * dble(j-1) do i = 1, m x = -1.0D+00 + dx * dble(i-1) call exact ( x, y, u_exact, uxx, uyy ) f(i,j) = u_exact u(i,j) = u_exact end do j = n y = -1.0D+00 + dy * dble(j-1) do i = 1, m x = -1.0D+00 + dx * dble(i-1) call exact ( x, y, u_exact, uxx, uyy ) f(i,j) = u_exact u(i,j) = u_exact end do i = 1 x = -1.0D+00 + dx * dble(i-1) do j = 1, n y = -1.0D+00 + dy * dble(j-1) call exact ( x, y, u_exact, uxx, uyy ) f(i,j) = u_exact u(i,j) = u_exact end do i = m x = -1.0D+00 + dx * dble(i-1) do j = 1, n y = -1.0D+00 + dy * dble(j-1) call exact ( x, y, u_exact, uxx, uyy ) f(i,j) = u_exact u(i,j) = u_exact end do ! ! Set the right hand side F. ! ! !$omp parallel do private ( x, y, u_exact, uxx, uyy ) do j = 2, n-1 do i = 2, m-1 x = -1.0D+00 + dx * dble(i-1) y = -1.0D+00 + dy * dble(j-1) call exact ( x, y, u_exact, uxx, uyy ) f(i,j) = - uxx - uyy + alpha * u_exact end do end do !$omp end parallel do f_norm = sqrt ( sum ( f(1:m,1:n)**2 ) ) write ( *, '(a,g14.6)' ) ' ' write ( *, '(a,g14.6)' ) ' Right hand side RMS = ', f_norm return end subroutine jacobi ( m, n, alpha, omega, u, f, tol, it_max ) ! !******************************************************************************* ! !! JACOBI ... ! ! ! Discussion: ! ! The Jacobi iterative method is used to solve the linear system ! for the Helmholtz equation solution. ! ! Modified: ! ! 17 March 2002 ! ! Author: ! ! Joseph Robicheaux, ! Sanjiv Shah, ! Kuck and Associates, Inc. (KAI) ! ! Parameters: ! ! Input, integer M, N, the number of grid points in the X and Y directions. ! ! Input, double precision ALPHA, the scalar coefficient in the ! Helmholtz equation. ALPHA should be positive. ! ! Input, double precision OMEGA, the relaxation parameter, which ! should be strictly between 0 and 2. For a pure Jacobi method, ! use OMEGA = 1. ! ! Input/output, double precision U(M,N), the solution of the Helmholtz ! equation at the grid points. ! ! Input, double precision F(M,N), values of the right hand side function ! for the Helmholtz equation at the grid points. ! ! Input, double precision TOL, an error tolerance for the linear ! equation solver. ! ! Input, integer IT_MAX, the maximum number of Jacobi iterations allowed. ! implicit none ! integer m integer n ! double precision alpha double precision ax double precision ay double precision b double precision dx double precision dy double precision error double precision f(m,n) integer i integer it integer it_max integer j integer k integer maxit double precision omega double precision resid double precision rsum double precision tol double precision u(m,n) double precision uold(m,n) ! ! Initialize the coefficients ! dx = 2.0D+00 / dble ( m - 1 ) dy = 2.0D+00 / dble ( n - 1 ) ax = -1.0D+00 / dx**2 ay = -1.0D+00 / dy**2 b = +2.0D+00 / dx**2 + 2.0D+00 / dy**2 + alpha do it = 1, it_max error = 0.0D+00 ! ! Copy new solution into old ! !$omp parallel !$omp do do j = 1, n do i = 1, m uold(i,j) = u(i,j) end do end do ! ! Compute stencil, residual, and update. ! !$omp do private(resid) reduction(+:error) do j = 1, n do i = 1, m ! ! Evaluate the residual ! if ( i == 1 .or. i == m .or. j == 1 .or. j == n ) then resid = uold(i,j) - f(i,j) else resid = ( ax * ( uold(i-1,j) + uold(i+1,j) ) & + ay * ( uold(i,j-1) + uold(i,j+1) ) & + b * uold(i,j) - f(i,j) ) / b end if ! ! Update the solution ! u(i,j) = uold(i,j) - omega * resid ! ! Accumulate the residual error ! error = error + resid**2 end do end do !$omp end do nowait !$omp end parallel ! ! Error check ! error = sqrt ( error ) write ( *, '(a,g14.6)' ) 'Jacobi residual RMS ', error if ( error <= tol * dble ( m * n ) ) then exit end if end do write ( *, '(a)' ) ' ' write ( *, '(a,i6)' ) 'Total number of iterations ', it return end subroutine error_check ( m, n, alpha, u, f ) ! !******************************************************************************* ! !! ERROR_CHECK determines the error in the numerical solution. ! ! ! Modified: ! ! 18 March 2002 ! ! Author: ! ! Joseph Robicheaux, ! Sanjiv Shah, ! Kuck and Associates, Inc. (KAI) ! ! Parameters: ! ! Input, integer M, N, the number of grid points in the X and Y directions. ! ! Input, double precision ALPHA, the scalar coefficient in the ! Helmholtz equation. ALPHA should be positive. ! ! Input, double precision U(M,N), the solution of the Helmholtz equation ! at the grid points. ! ! Input, double precision F(M,N), values of the right hand side function ! for the Helmholtz equation at the grid points. ! implicit none ! integer m integer n ! double precision alpha double precision dx double precision dy double precision error double precision f(m,n) integer i integer j double precision u(m,n) double precision u_exact double precision u_exact_norm double precision u_norm double precision uxx double precision uyy double precision x double precision y ! dx = 2.0D+00 / dble ( m - 1 ) dy = 2.0D+00 / dble ( n - 1 ) error = 0.0D+00 u_norm = sqrt ( sum ( u(1:m,1:n)**2 ) ) u_exact_norm = 0.0D+00 !$omp parallel !$omp do private ( x, y, u_exact, uxx, uyy ) reduction(+:error, u_exact_norm) do j = 1, n do i = 1, m x = -1.0D+00 + dx * dble(i-1) y = -1.0D+00 + dy * dble(j-1) call exact ( x, y, u_exact, uxx, uyy ) error = error + ( u(i,j) - u_exact )**2 u_exact_norm = u_exact_norm + u_exact**2 end do end do !$omp end do !$omp end parallel error = sqrt ( error ) u_exact_norm = sqrt ( u_exact_norm ) write ( *, '(a)' ) ' ' write ( *, '(a,g14.6)' ) ' Computed U RMS norm : ', u_norm write ( *, '(a,g14.6)' ) ' Computed U_EXACT RMS norm : ', u_exact_norm write ( *, '(a,g14.6)' ) ' RMS error : ', error return end subroutine exact ( x, y, u, uxx, uyy ) ! !******************************************************************************* ! !! EXACT returns the exact value of the solution and derivatives. ! ! ! Discussion: ! ! EXACT can be used to compute the error, and to formulate the ! appropriate boundary conditions and right hand side for any ! desired solution. ! ! Modified: ! ! 20 March 2002 ! ! Author: ! ! John Burkardt ! ! Parameters: ! ! Input, double precision X, Y, the point at which the values are needed. ! ! Output, double precision U, UXX, UYY, the values of the function, ! and its second derivatives with respect to X and Y. ! implicit none ! double precision u double precision uxx double precision uyy double precision x double precision y ! u = ( 1.0D+00 - x**2 ) * ( 1.0D+00 - y**2 ) uxx = -2.0D+00 * ( 1.0D+00 + y ) * ( 1.0D+00 - y ) uyy = -2.0D+00 * ( 1.0D+00 + x ) * ( 1.0D+00 - 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