! source file: /Users/oschlies/UVIC/master/source/mom/hyper3.F subroutine hyper3 (npt, variable, bc_symm &, guess, dpsi, forc, res &, cf &, sor, mxscan, mscan, crit &, imask, iperm, jperm, iofs, nisle, nippts &, map &, converged &, estimated_error & ) !======================================================================= ! MOM 2 "hypergrid" relax using symmetric coefficients ! It does not normalize symmetric coefficients ala MOM 1. ! Hyper3 does checkerboard updating as 4 loops of constant stride !======================================================================= ! H Y P E R G R I D ! solve: ! A * dpsi = forc ! for "dpsi" with dirichlet boundary conditions (dpsi=const on ! each component of the boundary) by a "hypergrid" version of ! Gauss-Seidel iteration. In this version, the grid is ! decomposed into 4 sets, each with the same values of ! (i mod 2, j mod 2). All calculations within a set may be ! done in parallel. ! inputs: ! npt = 5 or 9 (active coefficients) ! variable = character string identifying solution variable ! bc_symm = equatorial symmetry type (used only when the ! symmetry option is on. otherwise ignore it) ! guess = initial approximation to solution ! A = linear operator (assumed symmetric) ! typically A is grad{(1/h)*grad(dpsi)} - ! 2dt*acor*{grad(f/h) x grad(dpsi)} ! using 5 or 9 pt discretizations ! cf = imt x jmt x 3 x 3 array of coefficients of A ! sor = over-relaxation multiplier ! forc = the sum of all terms evaluated at times tau ! or tau-1 ! epsilon = convergence criterion ! max_iterations = maximum number of iterations ! imask = shows which land masses have perimeter equations ! iperm = i coordinate of island perimeter points ! jperm = j coordinate of island perimeter points ! iofs = offset in iperm, jperm for start of perimeter ! of land_mass(isle) ! nisle = actual number of land_masses ! nippts = number of perimeter ocean points for a land_mass ! output: ! dpsi = answer ! iterations = actual number of iterations performed ! converged = logical value ! estimated_error = estimated maximum error in solution ! based on step sizes and convergence rate !======================================================================= ! more specifically, the equations to be solved are ! sum (A(ij,i'j') * dpsi(i'j')) = forc(ij) ! where the subscripts ij and i'j' range over all "free ocean" ! T cells ij=(i,j) that are not adjacent to land T cells, ! and one ij=isle for each boundary component of the ocean. ! with this choice of variables, in the absence of coriolis terms ! (acor=0), the operator A is symmetric, i.e., ! A(ij,i'j') = A(i'j',ij) ! based on code by: M. Cox, B. Semtner, R. C. Pacanowski and ! C. H. Goldberg !======================================================================= include "size.h" include "task_on.h" logical imask(-mnisle:mnisle) dimension dpsi(imt,jmt), forc(imt,jmt), res(imt,jmt) dimension cf(imt,jmt,-1:1,-1:1) dimension relmsk(imt,jmt), guess(imt,jmt) dimension nippts(mnisle) dimension iofs(mnisle), iperm(maxipp), jperm(maxipp) dimension map(imt,jmt) dimension rcfdiag(imt,jmt) dimension diagsum(mnisle) dimension resmi(jmt) logical converged character(16) :: variable character(*) :: bc_symm !----------------------------------------------------------------------- ! set locally needed constants !----------------------------------------------------------------------- c0 = 0.0 c1 = 1.0 !----------------------------------------------------------------------- ! calculate "normalized" coefficients used in MOM1 ! relmsk is now a locally computed array ! it is 1 on mid-ocean points, and 0 elsewhere !----------------------------------------------------------------------- do j=1,jmt do i=1,imt if (map(i,j) .eq. 0) then relmsk(i,j) = c1 else relmsk(i,j) = c0 endif enddo enddo do isle=1,nisle diagsum(isle) = c0 enddo do j=2,jmt-1 do i=2,imt-1 if (cf(i,j,0,0) .eq. 0.0) then rcfdiag(i,j) = c0 elseif (map(i,j) .eq. 0) then rcfdiag(i,j) = c1/cf(i,j,0,0) else rcfdiag(i,j) = c0 endif ! sum diagonal coefficients on island boundary isle = -map(i,j) if (isle .gt. 0 .and. imask(isle)) then diagsum(isle) = diagsum(isle)+cf(i,j,0,0) endif enddo enddo do isle=1,nisle if (imask(isle)) then do n=1,nippts(isle) i = iperm(iofs(isle)+n) j = jperm(iofs(isle)+n) rcfdiag(i,j) = c1/diagsum(isle) enddo endif enddo !----------------------------------------------------------------------- ! impose boundary conditions on guess ! dpsi(0) = guess !----------------------------------------------------------------------- call border(guess, bc_symm) !----------------------------------------------------------------------- ! set residuals to zero and initialize dpsi !----------------------------------------------------------------------- do j=1,jmt do i=1,imt res(i,j) = c0 dpsi(i,j) = guess(i,j) enddo enddo !----------------------------------------------------------------------- ! begin iteration loop !----------------------------------------------------------------------- do mscan=1,mxscan do j=2,jmt-1 resmi(j) = c0 enddo !----------------------------------------------------------------------- ! consider the arrays as being defined on the squares of an ! "imt by jmt" checkerboard. take four passes: first solve the ! equation on the black squares in even columns, then on the red ! squares in even columns, then red squares in odd columns, and ! finally on black squares in odd columns.. !----------------------------------------------------------------------- if (npt .eq. 5) then ! 5 point calculation do i1=0,1 do j1=0,1 do j=2+j1,jmt-1,2 do i=2+i1,imt-1,2 res(i,j) = relmsk(i,j) * & ((forc(i,j) & -cf(i,j, 0, 1)*dpsi(i,j+1) & -cf(i,j, 0,-1)*dpsi(i,j-1) & -cf(i,j, 1, 0)*dpsi(i+1,j) & -cf(i,j,-1, 0)*dpsi(i-1,j) & )*rcfdiag(i,j) - dpsi(i,j) ) enddo call border(res, bc_symm) ! make a correction to dpsi based on the residuals do i=2+i1,imt,2 dpsi(i,j) = dpsi(i,j) + sor * res(i,j) enddo ! find the maximum absolute residual to determine convergence do i=2+i1,imt,2 resmi(j) = max(abs(res(i,j)),resmi(j)) enddo enddo enddo call border(res, bc_symm) enddo else ! 9 point calculation do i1=0,1 do j1=0,1 do j=2+j1,jmt-1,2 do i=2+i1,imt-1,2 res(i,j) = relmsk(i,j) * & ((forc(i,j) & -cf(i,j, 0, 1)*dpsi(i,j+1) & -cf(i,j, 0,-1)*dpsi(i,j-1) & -cf(i,j, 1, 0)*dpsi(i+1,j) & -cf(i,j,-1, 0)*dpsi(i-1,j) & -cf(i,j, 1, 1)*dpsi(i+1,j+1) & -cf(i,j,-1, 1)*dpsi(i-1,j+1) & -cf(i,j, 1,-1)*dpsi(i+1,j-1) & -cf(i,j,-1,-1)*dpsi(i-1,j-1) & )*rcfdiag(i,j) - dpsi(i,j) ) enddo call border(res, bc_symm) ! make a correction to dpsi based on the residuals do i=2+i1,imt,2 dpsi(i,j) = dpsi(i,j) + sor * res(i,j) enddo ! find the maximum absolute residual to determine convergence do i=2+i1,imt,2 resmi(j) = max(abs(res(i,j)),resmi(j)) enddo enddo enddo call border(res, bc_symm) enddo endif !----------------------------------------------------------------------- ! find maximum residual !----------------------------------------------------------------------- resmax = c0 do j=2,jmt-1 resmax = max(resmi(j),resmax) enddo !----------------------------------------------------------------------- ! do integration around each island !----------------------------------------------------------------------- do isle=1,nisle if (imask(isle)) then resis = c0 do n=1,nippts(isle) i = iperm(iofs(isle)+n) j = jperm(iofs(isle)+n) resis = resis + forc(i,j) & - cf(i,j, 0, 1)*dpsi(i,j+1) & - cf(i,j, 0,-1)*dpsi(i,j-1) & - cf(i,j, 1, 0)*dpsi(i+1,j) & - cf(i,j,-1, 0)*dpsi(i-1,j) & - cf(i,j, 1, 1)*dpsi(i+1,j+1) & - cf(i,j,-1, 1)*dpsi(i-1,j+1) & - cf(i,j, 1,-1)*dpsi(i+1,j-1) & - cf(i,j,-1,-1)*dpsi(i-1,j-1) enddo resis = resis / diagsum(isle) - dpsi(i,j) resmax = max(abs(resis),resmax) do n=1,nippts(isle) i = iperm(iofs(isle)+n) j = jperm(iofs(isle)+n) dpsi(i,j) = dpsi(i,j) + sor * resis enddo endif enddo call border(dpsi, bc_symm) !----------------------------------------------------------------------- ! test for convergence of the relaxation. ! the solver is deemed to have converged when the estimated ! maximum sum of all future corrections does not exceed ! crit at any point. !----------------------------------------------------------------------- step = sor * resmax if (mscan .eq. 1) then step1 = step estimated_error = step if (step .lt. crit) goto 1001 elseif (step .lt. crit) then cfactor = log(step/step1) convergence_rate = exp(cfactor/(mscan-1)) estimated_error = step*convergence_rate/(1.0-convergence_rate) if (estimated_error .lt. crit) goto 1001 endif enddo 1001 continue if (mscan .lt. mxscan) then converged = .true. else converged = .false. endif !--------------------------------------------------------------------- ! return the last increment to dpsi in the argument res !----------------------------------------------------------------------- do i=1,imt do j=1,jmt res(i,j) = sor * res(i,j) enddo enddo return end