C $Header: /u/gcmpack/MITgcm/pkg/generic_advdiff/gad_pqm_fun.F,v 1.1 2016/03/13 01:44:03 jmc Exp $ C $Name: $ # include "GAD_OPTIONS.h" C-- File gad_pqm_fun.F: Routines to form PQM grid-cell polynomial. C-- Contents C-- o QUADROOT C-- o GAD_PPM_FUN_NULL C-- o GAD_PPM_FUN_MONO C---+----1----+----2----+----3----+----4----+----5----+----6----+----7-|--+----| LOGICAL FUNCTION QUADROOT(aa,bb,cc,xx) C |================================================================| C | QUADROOT: find roots of quadratic ax**2 + bx + c = 0. | C |================================================================| implicit none C ====================================================== arguments _RL aa,bb,cc _RL xx(1:2) C ====================================================== variables _RL sq,a0,b0 a0 = abs(aa) b0 = abs(bb) sq = bb * bb - 4. _d 0 * aa * cc if (a0 .gt. 0. _d 0) then if (sq .ge. 0. _d 0) then QUADROOT = .TRUE. sq = sqrt(sq) xx(1) = - bb + sq xx(2) = - bb - sq aa = 0.5 _d 0 / aa xx(1) = xx(1) * aa xx(2) = xx(2) * aa else QUADROOT = .FALSE. end if else if (b0 .gt. 0. _d 0) then QUADROOT = .TRUE. xx(1) = - cc / bb xx(2) = - cc / bb else QUADROOT = .FALSE. end if end if return c end function QUADROOT end C---+----1----+----2----+----3----+----4----+----5----+----6----+----7-|--+----| SUBROUTINE GAD_PQM_FUN_NULL( I ff00, I fell,ferr, I dell,derr, O fhat,mono) C |================================================================| C | PQM_FUN_NULL: form PQM grid-cell polynomial. | C | Piecewise Quartic Method (PQM) - unlimited variant. | C |================================================================| implicit none C ====================================================== arguments _RL ff00 _RL fell,ferr _RL dell,derr _RL fhat(+1:+5) integer mono mono = +0 C ============================================== unlimited profile fhat(1) = & + (30. _d 0 / 16. _d 0) * ff00 & - ( 7. _d 0 / 16. _d 0) *(ferr+fell) & + ( 1. _d 0 / 16. _d 0) *(derr-dell) fhat(2) = & + ( 3. _d 0 / 4. _d 0) *(ferr-fell) & - ( 1. _d 0 / 4. _d 0) *(derr+dell) fhat(3) = & - (30. _d 0 / 8. _d 0) * ff00 & + (15. _d 0 / 8. _d 0) *(ferr+fell) & - ( 3. _d 0 / 8. _d 0) *(derr-dell) fhat(4) = & - ( 1. _d 0 / 4. _d 0) *(ferr-fell-derr-dell) fhat(5) = & + (30. _d 0 / 16. _d 0) * ff00 & - (15. _d 0 / 16. _d 0) *(ferr+fell) & + ( 5. _d 0 / 16. _d 0) *(derr-dell) return c end subroutine GAD_PQM_FUN_NULL end C---+----1----+----2----+----3----+----4----+----5----+----6----+----7-|--+----| SUBROUTINE GAD_PQM_FUN_MONO( I ff00, I ffll,ffrr, I fell,ferr, I dell,derr, I dfds, O fhat,mono) C |================================================================| C | PQM_FUN_MONO: form PQM grid-cell polynomial. | C | Piecewise Quartic Method (PQM) - monotonic variant. | C |================================================================| implicit none C ====================================================== arguments _RL ff00,ffll,ffrr _RL fell,ferr _RL dell,derr _RL dfds(-1:+1) _RL fhat(+1:+5) integer mono C ====================================================== functions logical QUADROOT C ====================================================== variables integer bind _RL aval,bval,cval _RL iflx(+1:+2) _RL dflx(+1:+2) mono = +0 C ============================================== "flatten" extrema if((ffrr-ff00) * & (ff00-ffll) .le. 0. _d 0) then mono = +1 fhat(1) = ff00 fhat(2) = 0. _d 0 fhat(3) = 0. _d 0 fhat(4) = 0. _d 0 fhat(5) = 0. _d 0 return end if C ============================================== limit edge values if((ffll - fell) * & (fell - ff00) .le. 0. _d 0) then mono = +1 fell = ff00 - dfds(0) end if if((ffrr - ferr) * & (ferr - ff00) .le. 0. _d 0) then mono = +1 ferr = ff00 + dfds(0) end if C ============================================== limit edge slopes if((dell * dfds(-1)) .lt. 0. _d 0) then mono = +1 dell = dfds(-1) end if if((derr * dfds(+1)) .lt. 0. _d 0) then mono = +1 derr = dfds(+1) end if C ============================================== limit cell values fhat(1) = & + (30. _d 0 / 16. _d 0) * ff00 & - ( 7. _d 0 / 16. _d 0) *(ferr+fell) & + ( 1. _d 0 / 16. _d 0) *(derr-dell) fhat(2) = & + ( 3. _d 0 / 4. _d 0) *(ferr-fell) & - ( 1. _d 0 / 4. _d 0) *(derr+dell) fhat(3) = & - (30. _d 0 / 8. _d 0) * ff00 & + (15. _d 0 / 8. _d 0) *(ferr+fell) & - ( 3. _d 0 / 8. _d 0) *(derr-dell) fhat(4) = & - ( 1. _d 0 / 4. _d 0) *(ferr-fell-derr-dell) fhat(5) = & + (30. _d 0 / 16. _d 0) * ff00 & - (15. _d 0 / 16. _d 0) *(ferr+fell) & + ( 5. _d 0 / 16. _d 0) *(derr-dell) C ============================= calc. inflexion via 2nd-derivative aval = 12. _d 0 * fhat(5) bval = 6. _d 0 * fhat(4) cval = 2. _d 0 * fhat(3) if ( QUADROOT(aval,bval,cval,iflx) ) then bind = +0 if ( ( iflx(1) .gt. -1. _d 0 ) & .and. ( iflx(1) .lt. +1. _d 0 ) ) then C ============================= check for non-monotonic inflection dflx(1) = fhat(2) & + iflx(1) * fhat(3) * 2. _d 0 & +(iflx(1) ** 2) * fhat(4) * 3. _d 0 & +(iflx(1) ** 3) * fhat(5) * 4. _d 0 if (dflx(1)*dfds(+0) .lt. 0. _d 0) then if (abs(dell) & .lt. abs(derr) ) then bind = -1 else bind = +1 end if end if end if if ( ( iflx(2) .gt. -1. _d 0 ) & .and. ( iflx(2) .lt. +1. _d 0 ) ) then C ============================= check for non-monotonic inflection dflx(2) = fhat(2) & + iflx(2) * fhat(3) * 2. _d 0 & +(iflx(2) ** 2) * fhat(4) * 3. _d 0 & +(iflx(2) ** 3) * fhat(5) * 4. _d 0 if (dflx(2)*dfds(+0) .lt. 0. _d 0) then if (abs(dell) & .lt. abs(derr) ) then bind = -1 else bind = +1 end if end if end if C ============================= pop non-monotone inflexion to edge if (bind .eq. -1) then C ============================= pop inflection points onto -1 edge mono = +2 derr = & -( 5. _d 0 / 1. _d 0) * ff00 & +( 3. _d 0 / 1. _d 0) * ferr & +( 2. _d 0 / 1. _d 0) * fell dell = & +( 5. _d 0 / 3. _d 0) * ff00 & -( 1. _d 0 / 3. _d 0) * ferr & -( 4. _d 0 / 3. _d 0) * fell if (dell*dfds(-1) .lt. 0. _d 0) then dell = 0. _d 0 ferr = & +( 5. _d 0 / 1. _d 0) * ff00 & -( 4. _d 0 / 1. _d 0) * fell derr = & +(10. _d 0 / 1. _d 0) * ff00 & -(10. _d 0 / 1. _d 0) * fell end if if (derr*dfds(+1) .lt. 0. _d 0) then derr = 0. _d 0 fell = & +( 5. _d 0 / 2. _d 0) * ff00 & -( 3. _d 0 / 2. _d 0) * ferr dell = & -( 5. _d 0 / 3. _d 0) * ff00 & +( 5. _d 0 / 3. _d 0) * ferr end if end if if (bind .eq. +1) then C ============================= pop inflection points onto +1 edge mono = +2 derr = & -( 5. _d 0 / 3. _d 0) * ff00 & +( 4. _d 0 / 3. _d 0) * ferr & +( 1. _d 0 / 3. _d 0) * fell dell = & +( 5. _d 0 / 1. _d 0) * ff00 & -( 2. _d 0 / 1. _d 0) * ferr & -( 3. _d 0 / 1. _d 0) * fell if (dell*dfds(-1) .lt. 0. _d 0) then dell = 0. _d 0 ferr = & +( 5. _d 0 / 2. _d 0) * ff00 & -( 3. _d 0 / 2. _d 0) * fell derr = & +( 5. _d 0 / 3. _d 0) * ff00 & -( 5. _d 0 / 3. _d 0) * fell end if if (derr*dfds(+1) .lt. 0. _d 0) then derr = 0. _d 0 fell = & +( 5. _d 0 / 1. _d 0) * ff00 & -( 4. _d 0 / 1. _d 0) * ferr dell = & -(10. _d 0 / 1. _d 0) * ff00 & +(10. _d 0 / 1. _d 0) * ferr end if end if end if C ============================= re-assemble coefficients on demand if (mono .eq. +2) then fhat(1) = & + (30. _d 0 / 16. _d 0) * ff00 & - ( 7. _d 0 / 16. _d 0) *(ferr+fell) & + ( 1. _d 0 / 16. _d 0) *(derr-dell) fhat(2) = & + ( 3. _d 0 / 4. _d 0) *(ferr-fell) & - ( 1. _d 0 / 4. _d 0) *(derr+dell) fhat(3) = & - (30. _d 0 / 8. _d 0) * ff00 & + (15. _d 0 / 8. _d 0) *(ferr+fell) & - ( 3. _d 0 / 8. _d 0) *(derr-dell) fhat(4) = & - ( 1. _d 0 / 4. _d 0) *(ferr-fell-derr-dell) fhat(5) = & + (30. _d 0 / 16. _d 0) * ff00 & - (15. _d 0 / 16. _d 0) *(ferr+fell) & + ( 5. _d 0 / 16. _d 0) *(derr-dell) end if return c end subroutine GAD_PQM_FUN_MONO end