MOM6
pqm_functions Module Reference

Functions/Subroutines

subroutine, public pqm_reconstruction (N, h, u, ppoly_E, ppoly_S, ppoly_coefficients)
 
subroutine pqm_limiter (N, h, u, ppoly_E, ppoly_S)
 
subroutine, public pqm_boundary_extrapolation (N, h, u, ppoly_E, ppoly_coefficients)
 
subroutine, public pqm_boundary_extrapolation_v1 (N, h, u, ppoly_E, ppoly_S, ppoly_coefficients)
 

Variables

real, parameter h_neglect = 1.E-30
 

Function/Subroutine Documentation

◆ pqm_boundary_extrapolation()

subroutine, public pqm_functions::pqm_boundary_extrapolation ( integer, intent(in)  N,
real, dimension(:), intent(in)  h,
real, dimension(:), intent(in)  u,
real, dimension(:,:), intent(inout)  ppoly_E,
real, dimension(:,:), intent(inout)  ppoly_coefficients 
)

Definition at line 364 of file PQM_functions.F90.

364 !------------------------------------------------------------------------------
365 ! Reconstruction by parabolas within boundary cells.
366 !
367 ! The following explanations apply to the left boundary cell. The same
368 ! reasoning holds for the right boundary cell.
369 !
370 ! A parabola needs to be built in the cell and requires three degrees of
371 ! freedom, which are the right edge value and slope and the cell average.
372 ! The right edge values and slopes are taken to be that of the neighboring
373 ! cell (i.e., the left edge value and slope of the neighboring cell).
374 ! The resulting parabola is not necessarily monotonic and the traditional
375 ! PPM limiter is used to modify one of the edge values in order to yield
376 ! a monotonic parabola.
377 !
378 ! grid: one-dimensional grid (properly initialized)
379 ! ppoly: piecewise linear polynomial to be reconstructed (properly initialized)
380 ! u: cell averages
381 !
382 ! It is assumed that the size of the array 'u' is equal to the number of cells
383 ! defining 'grid' and 'ppoly'. No consistency check is performed here.
384 !------------------------------------------------------------------------------
385 
386  ! Arguments
387  integer, intent(in) :: n ! Number of cells
388  real, dimension(:), intent(in) :: h ! cell widths (size N)
389  real, dimension(:), intent(in) :: u ! cell averages (size N)
390  real, dimension(:,:), intent(inout) :: ppoly_e !Edge value of polynomial
391  real, dimension(:,:), intent(inout) :: ppoly_coefficients !Coefficients of polynomial
392 
393  ! Local variables
394  integer :: i0, i1
395  real :: u0, u1
396  real :: h0, h1
397  real :: a, b, c, d, e
398  real :: u0_l, u0_r
399  real :: u1_l, u1_r
400  real :: slope
401  real :: exp1, exp2
402 
403  ! ----- Left boundary -----
404  i0 = 1
405  i1 = 2
406  h0 = h(i0)
407  h1 = h(i1)
408  u0 = u(i0)
409  u1 = u(i1)
410 
411  ! Compute the left edge slope in neighboring cell and express it in
412  ! the global coordinate system
413  b = ppoly_coefficients(i1,2)
414  u1_r = b *(h0/h1) ! derivative evaluated at xi = 0.0,
415  ! expressed w.r.t. xi (local coord. system)
416 
417  ! Limit the right slope by the PLM limited slope
418  slope = 2.0 * ( u1 - u0 )
419  if ( abs(u1_r) .GT. abs(slope) ) then
420  u1_r = slope
421  end if
422 
423  ! The right edge value in the boundary cell is taken to be the left
424  ! edge value in the neighboring cell
425  u0_r = ppoly_e(i1,1)
426 
427  ! Given the right edge value and slope, we determine the left
428  ! edge value and slope by computing the parabola as determined by
429  ! the right edge value and slope and the boundary cell average
430  u0_l = 3.0 * u0 + 0.5 * u1_r - 2.0 * u0_r
431 
432  ! Apply the traditional PPM limiter
433  exp1 = (u0_r - u0_l) * (u0 - 0.5*(u0_l+u0_r))
434  exp2 = (u0_r - u0_l) * (u0_r - u0_l) / 6.0
435 
436  if ( exp1 .GT. exp2 ) then
437  u0_l = 3.0 * u0 - 2.0 * u0_r
438  end if
439 
440  if ( exp1 .LT. -exp2 ) then
441  u0_r = 3.0 * u0 - 2.0 * u0_l
442  end if
443 
444  ppoly_e(i0,1) = u0_l
445  ppoly_e(i0,2) = u0_r
446 
447  a = u0_l
448  b = 6.0 * u0 - 4.0 * u0_l - 2.0 * u0_r
449  c = 3.0 * ( u0_r + u0_l - 2.0 * u0 )
450 
451  ! The quartic is reduced to a parabola in the boundary cell
452  ppoly_coefficients(i0,1) = a
453  ppoly_coefficients(i0,2) = b
454  ppoly_coefficients(i0,3) = c
455  ppoly_coefficients(i0,4) = 0.0
456  ppoly_coefficients(i0,5) = 0.0
457 
458  ! ----- Right boundary -----
459  i0 = n-1
460  i1 = n
461  h0 = h(i0)
462  h1 = h(i1)
463  u0 = u(i0)
464  u1 = u(i1)
465 
466  ! Compute the right edge slope in neighboring cell and express it in
467  ! the global coordinate system
468  b = ppoly_coefficients(i0,2)
469  c = ppoly_coefficients(i0,3)
470  d = ppoly_coefficients(i0,4)
471  e = ppoly_coefficients(i0,5)
472  u1_l = (b + 2*c + 3*d + 4*e) ! derivative evaluated at xi = 1.0
473  u1_l = u1_l * (h1/h0)
474 
475  ! Limit the left slope by the PLM limited slope
476  slope = 2.0 * ( u1 - u0 )
477  if ( abs(u1_l) .GT. abs(slope) ) then
478  u1_l = slope
479  end if
480 
481  ! The left edge value in the boundary cell is taken to be the right
482  ! edge value in the neighboring cell
483  u0_l = ppoly_e(i0,2)
484 
485  ! Given the left edge value and slope, we determine the right
486  ! edge value and slope by computing the parabola as determined by
487  ! the left edge value and slope and the boundary cell average
488  u0_r = 3.0 * u1 - 0.5 * u1_l - 2.0 * u0_l
489 
490  ! Apply the traditional PPM limiter
491  exp1 = (u0_r - u0_l) * (u1 - 0.5*(u0_l+u0_r))
492  exp2 = (u0_r - u0_l) * (u0_r - u0_l) / 6.0
493 
494  if ( exp1 .GT. exp2 ) then
495  u0_l = 3.0 * u1 - 2.0 * u0_r
496  end if
497 
498  if ( exp1 .LT. -exp2 ) then
499  u0_r = 3.0 * u1 - 2.0 * u0_l
500  end if
501 
502  ppoly_e(i1,1) = u0_l
503  ppoly_e(i1,2) = u0_r
504 
505  a = u0_l
506  b = 6.0 * u1 - 4.0 * u0_l - 2.0 * u0_r
507  c = 3.0 * ( u0_r + u0_l - 2.0 * u1 )
508 
509  ! The quartic is reduced to a parabola in the boundary cell
510  ppoly_coefficients(i1,1) = a
511  ppoly_coefficients(i1,2) = b
512  ppoly_coefficients(i1,3) = c
513  ppoly_coefficients(i1,4) = 0.0
514  ppoly_coefficients(i1,5) = 0.0
515 

◆ pqm_boundary_extrapolation_v1()

subroutine, public pqm_functions::pqm_boundary_extrapolation_v1 ( integer, intent(in)  N,
real, dimension(:), intent(in)  h,
real, dimension(:), intent(in)  u,
real, dimension(:,:), intent(inout)  ppoly_E,
real, dimension(:,:), intent(inout)  ppoly_S,
real, dimension(:,:), intent(inout)  ppoly_coefficients 
)

Definition at line 523 of file PQM_functions.F90.

References h_neglect.

Referenced by mom_remapping::build_reconstructions_1d(), and regrid_interp::regridding_set_ppolys().

523 !------------------------------------------------------------------------------
524 ! Reconstruction by parabolas within boundary cells.
525 !
526 ! The following explanations apply to the left boundary cell. The same
527 ! reasoning holds for the right boundary cell.
528 !
529 ! A parabola needs to be built in the cell and requires three degrees of
530 ! freedom, which are the right edge value and slope and the cell average.
531 ! The right edge values and slopes are taken to be that of the neighboring
532 ! cell (i.e., the left edge value and slope of the neighboring cell).
533 ! The resulting parabola is not necessarily monotonic and the traditional
534 ! PPM limiter is used to modify one of the edge values in order to yield
535 ! a monotonic parabola.
536 !
537 ! grid: one-dimensional grid (properly initialized)
538 ! ppoly: piecewise linear polynomial to be reconstructed (properly initialized)
539 ! u: cell averages
540 !
541 ! It is assumed that the size of the array 'u' is equal to the number of cells
542 ! defining 'grid' and 'ppoly'. No consistency check is performed here.
543 !------------------------------------------------------------------------------
544 
545  ! Arguments
546  integer, intent(in) :: n ! Number of cells
547  real, dimension(:), intent(in) :: h ! cell widths (size N)
548  real, dimension(:), intent(in) :: u ! cell averages (size N)
549  real, dimension(:,:), intent(inout) :: ppoly_e !Edge value of polynomial
550  real, dimension(:,:), intent(inout) :: ppoly_s !Edge slope of polynomial
551  real, dimension(:,:), intent(inout) :: ppoly_coefficients !Coefficients of polynomial
552 
553  ! Local variables
554  integer :: i0, i1
555  integer :: inflexion_l
556  integer :: inflexion_r
557  real :: u0, u1, um
558  real :: h0, h1
559  real :: a, b, c, d, e
560  real :: ar, br, beta
561  real :: u0_l, u0_r
562  real :: u1_l, u1_r
563  real :: u_plm
564  real :: slope
565  real :: alpha1, alpha2, alpha3
566  real :: rho, sqrt_rho
567  real :: gradient1, gradient2
568  real :: x1, x2
569 
570  ! ----- Left boundary (TOP) -----
571  i0 = 1
572  i1 = 2
573  h0 = h(i0)
574  h1 = h(i1)
575  u0 = u(i0)
576  u1 = u(i1)
577  um = u0
578 
579  ! Compute real slope and express it w.r.t. local coordinate system
580  ! within boundary cell
581  slope = 2.0 * ( u1 - u0 ) / ( ( h0 + h1 ) + h_neglect )
582  slope = slope * h0
583 
584  ! The right edge value and slope of the boundary cell are taken to be the
585  ! left edge value and slope of the adjacent cell
586  a = ppoly_coefficients(i1,1)
587  b = ppoly_coefficients(i1,2)
588 
589  u0_r = a ! edge value
590  u1_r = b / (h1 + h_neglect) ! edge slope (w.r.t. global coord.)
591 
592  ! Compute coefficient for rational function based on mean and right
593  ! edge value and slope
594  if (u1_r.ne.0.) then ! HACK by AJA
595  beta = 2.0 * ( u0_r - um ) / ( (h0 + h_neglect)*u1_r) - 1.0
596  else
597  beta = 0.
598  endif ! HACK by AJA
599  br = u0_r + beta*u0_r - um
600  ar = um + beta*um - br
601 
602  ! Left edge value estimate based on rational function
603  u0_l = ar
604 
605  ! Edge value estimate based on PLM
606  u_plm = um - 0.5 * slope
607 
608  ! Check whether the left edge value is bounded by the mean and
609  ! the PLM edge value. If so, keep it and compute left edge slope
610  ! based on the rational function. If not, keep the PLM edge value and
611  ! compute corresponding slope.
612  if ( abs(um-u0_l) .lt. abs(um-u_plm) ) then
613  u1_l = 2.0 * ( br - ar*beta)
614  u1_l = u1_l / (h0 + h_neglect)
615  else
616  u0_l = u_plm
617  u1_l = slope / (h0 + h_neglect)
618  end if
619 
620  ! Monotonize quartic
621  inflexion_l = 0
622 
623  a = u0_l
624  b = h0 * u1_l
625  c = 30.0 * um - 12.0*u0_r - 18.0*u0_l + 1.5*h0*(u1_r - 3.0*u1_l)
626  d = -60.0 * um + h0 *(6.0*u1_l - 4.0*u1_r) + 28.0*u0_r + 32.0*u0_l
627  e = 30.0 * um + 2.5*h0*(u1_r - u1_l) - 15.0*(u0_l + u0_r)
628 
629  alpha1 = 6*e
630  alpha2 = 3*d
631  alpha3 = c
632 
633  rho = alpha2 * alpha2 - 4.0 * alpha1 * alpha3
634 
635  ! Check whether inflexion points exist. If so, transform the quartic
636  ! so that both inflexion points coalesce on the left edge.
637  if (( alpha1 .ne. 0.0 ) .and. ( rho .ge. 0.0 )) then
638 
639  sqrt_rho = sqrt( rho )
640 
641  x1 = 0.5 * ( - alpha2 - sqrt_rho ) / alpha1
642  if ( (x1 .gt. 0.0) .and. (x1 .lt. 1.0) ) then
643  gradient1 = 4.0 * e * (x1**3) + 3.0 * d * (x1**2) + 2.0 * c * x1 + b
644  if ( gradient1 * slope .lt. 0.0 ) then
645  inflexion_l = 1
646  end if
647  end if
648 
649  x2 = 0.5 * ( - alpha2 + sqrt_rho ) / alpha1
650  if ( (x2 .gt. 0.0) .and. (x2 .lt. 1.0) ) then
651  gradient2 = 4.0 * e * (x2**3) + 3.0 * d * (x2**2) + 2.0 * c * x2 + b
652  if ( gradient2 * slope .lt. 0.0 ) then
653  inflexion_l = 1
654  end if
655  end if
656 
657  end if
658 
659  if (( alpha1 .eq. 0.0 ) .and. ( alpha2 .ne. 0.0 )) then
660 
661  x1 = - alpha3 / alpha2
662  if ( (x1 .ge. 0.0) .and. (x1 .le. 1.0) ) then
663  gradient1 = 3.0 * d * (x1**2) + 2.0 * c * x1 + b
664  if ( gradient1 * slope .lt. 0.0 ) then
665  inflexion_l = 1
666  end if
667  end if
668 
669  end if
670 
671  if ( inflexion_l .eq. 1 ) then
672 
673  ! We modify the edge slopes so that both inflexion points
674  ! collapse onto the left edge
675  u1_l = ( 10.0 * um - 2.0 * u0_r - 8.0 * u0_l ) / (3.0*h0 + h_neglect)
676  u1_r = ( -10.0 * um + 6.0 * u0_r + 4.0 * u0_l ) / (h0 + h_neglect)
677 
678  ! One of the modified slopes might be inconsistent. When that happens,
679  ! the inconsistent slope is set equal to zero and the opposite edge value
680  ! and edge slope are modified in compliance with the fact that both
681  ! inflexion points must still be located on the left edge
682  if ( u1_l * slope .LT. 0.0 ) then
683 
684  u1_l = 0.0
685  u0_r = 5.0 * um - 4.0 * u0_l
686  u1_r = 20.0 * (um - u0_l) / ( h0 + h_neglect )
687 
688  else if ( u1_r * slope .LT. 0.0 ) then
689 
690  u1_r = 0.0
691  u0_l = (5.0*um - 3.0*u0_r) / 2.0
692  u1_l = 10.0 * (-um + u0_r) / (3.0 * h0 + h_neglect )
693 
694  end if
695 
696  end if
697 
698  ! Store edge values, edge slopes and coefficients
699  ppoly_e(i0,1) = u0_l
700  ppoly_e(i0,2) = u0_r
701  ppoly_s(i0,1) = u1_l
702  ppoly_s(i0,2) = u1_r
703 
704  a = u0_l
705  b = h0 * u1_l
706  c = 30.0 * um - 12.0*u0_r - 18.0*u0_l + 1.5*h0*(u1_r - 3.0*u1_l)
707  d = -60.0 * um + h0 *(6.0*u1_l - 4.0*u1_r) + 28.0*u0_r + 32.0*u0_l
708  e = 30.0 * um + 2.5*h0*(u1_r - u1_l) - 15.0*(u0_l + u0_r)
709 
710  ! Store coefficients
711  ppoly_coefficients(i0,1) = a
712  ppoly_coefficients(i0,2) = b
713  ppoly_coefficients(i0,3) = c
714  ppoly_coefficients(i0,4) = d
715  ppoly_coefficients(i0,5) = e
716 
717  ! ----- Right boundary (BOTTOM) -----
718  i0 = n-1
719  i1 = n
720  h0 = h(i0)
721  h1 = h(i1)
722  u0 = u(i0)
723  u1 = u(i1)
724  um = u1
725 
726  ! Compute real slope and express it w.r.t. local coordinate system
727  ! within boundary cell
728  slope = 2.0 * ( u1 - u0 ) / ( h0 + h1 )
729  slope = slope * h1
730 
731  ! The left edge value and slope of the boundary cell are taken to be the
732  ! right edge value and slope of the adjacent cell
733  a = ppoly_coefficients(i0,1)
734  b = ppoly_coefficients(i0,2)
735  c = ppoly_coefficients(i0,3)
736  d = ppoly_coefficients(i0,4)
737  e = ppoly_coefficients(i0,5)
738  u0_l = a + b + c + d + e ! edge value
739  u1_l = (b + 2*c + 3*d + 4*e) / h0 ! edge slope (w.r.t. global coord.)
740 
741  ! Compute coefficient for rational function based on mean and left
742  ! edge value and slope
743  if (um-u0_l.ne.0.) then ! HACK by AJA
744  beta = 0.5*h1*u1_l / (um-u0_l) - 1.0
745  else
746  beta = 0.
747  endif ! HACK by AJA
748  br = beta*um + um - u0_l
749  ar = u0_l
750 
751  ! Right edge value estimate based on rational function
752  if (1+beta.ne.0.) then ! HACK by AJA
753  u0_r = (ar + 2*br + beta*br ) / ((1+beta)*(1+beta))
754  else
755  u0_r = um + 0.5 * slope ! PLM
756  endif ! HACK by AJA
757 
758  ! Right edge value estimate based on PLM
759  u_plm = um + 0.5 * slope
760 
761  ! Check whether the right edge value is bounded by the mean and
762  ! the PLM edge value. If so, keep it and compute right edge slope
763  ! based on the rational function. If not, keep the PLM edge value and
764  ! compute corresponding slope.
765  if ( abs(um-u0_r) .lt. abs(um-u_plm) ) then
766  u1_r = 2.0 * ( br - ar*beta ) / ( (1+beta)*(1+beta)*(1+beta) )
767  u1_r = u1_r / h1
768  else
769  u0_r = u_plm
770  u1_r = slope / h1
771  end if
772 
773  ! Monotonize quartic
774  inflexion_r = 0
775 
776  a = u0_l
777  b = h1 * u1_l
778  c = 30.0 * um - 12.0*u0_r - 18.0*u0_l + 1.5*h1*(u1_r - 3.0*u1_l)
779  d = -60.0 * um + h1*(6.0*u1_l - 4.0*u1_r) + 28.0*u0_r + 32.0*u0_l
780  e = 30.0 * um + 2.5*h1*(u1_r - u1_l) - 15.0*(u0_l + u0_r)
781 
782  alpha1 = 6*e
783  alpha2 = 3*d
784  alpha3 = c
785 
786  rho = alpha2 * alpha2 - 4.0 * alpha1 * alpha3
787 
788  ! Check whether inflexion points exist. If so, transform the quartic
789  ! so that both inflexion points coalesce on the right edge.
790  if (( alpha1 .ne. 0.0 ) .and. ( rho .ge. 0.0 )) then
791 
792  sqrt_rho = sqrt( rho )
793 
794  x1 = 0.5 * ( - alpha2 - sqrt_rho ) / alpha1
795  if ( (x1 .gt. 0.0) .and. (x1 .lt. 1.0) ) then
796  gradient1 = 4.0 * e * (x1**3) + 3.0 * d * (x1**2) + 2.0 * c * x1 + b
797  if ( gradient1 * slope .lt. 0.0 ) then
798  inflexion_r = 1
799  end if
800  end if
801 
802  x2 = 0.5 * ( - alpha2 + sqrt_rho ) / alpha1
803  if ( (x2 .gt. 0.0) .and. (x2 .lt. 1.0) ) then
804  gradient2 = 4.0 * e * (x2**3) + 3.0 * d * (x2**2) + 2.0 * c * x2 + b
805  if ( gradient2 * slope .lt. 0.0 ) then
806  inflexion_r = 1
807  end if
808  end if
809 
810  end if
811 
812  if (( alpha1 .eq. 0.0 ) .and. ( alpha2 .ne. 0.0 )) then
813 
814  x1 = - alpha3 / alpha2
815  if ( (x1 .ge. 0.0) .and. (x1 .le. 1.0) ) then
816  gradient1 = 3.0 * d * (x1**2) + 2.0 * c * x1 + b
817  if ( gradient1 * slope .lt. 0.0 ) then
818  inflexion_r = 1
819  end if
820  end if
821 
822  end if
823 
824  if ( inflexion_r .eq. 1 ) then
825 
826  ! We modify the edge slopes so that both inflexion points
827  ! collapse onto the right edge
828  u1_r = ( -10.0 * um + 8.0 * u0_r + 2.0 * u0_l ) / (3.0 * h1)
829  u1_l = ( 10.0 * um - 4.0 * u0_r - 6.0 * u0_l ) / h1
830 
831  ! One of the modified slopes might be inconsistent. When that happens,
832  ! the inconsistent slope is set equal to zero and the opposite edge value
833  ! and edge slope are modified in compliance with the fact that both
834  ! inflexion points must still be located on the right edge
835  if ( u1_l * slope .lt. 0.0 ) then
836 
837  u1_l = 0.0
838  u0_r = ( 5.0 * um - 3.0 * u0_l ) / 2.0
839  u1_r = 10.0 * (um - u0_l) / (3.0 * h1)
840 
841  else if ( u1_r * slope .lt. 0.0 ) then
842 
843  u1_r = 0.0
844  u0_l = 5.0 * um - 4.0 * u0_r
845  u1_l = 20.0 * ( -um + u0_r ) / h1
846 
847  end if
848 
849  end if
850 
851  ! Store edge values, edge slopes and coefficients
852  ppoly_e(i1,1) = u0_l
853  ppoly_e(i1,2) = u0_r
854  ppoly_s(i1,1) = u1_l
855  ppoly_s(i1,2) = u1_r
856 
857  a = u0_l
858  b = h1 * u1_l
859  c = 30.0 * um - 12.0*u0_r - 18.0*u0_l + 1.5*h1*(u1_r - 3.0*u1_l)
860  d = -60.0 * um + h1 *(6.0*u1_l - 4.0*u1_r) + 28.0*u0_r + 32.0*u0_l
861  e = 30.0 * um + 2.5*h1*(u1_r - u1_l) - 15.0*(u0_l + u0_r)
862 
863  ppoly_coefficients(i1,1) = a
864  ppoly_coefficients(i1,2) = b
865  ppoly_coefficients(i1,3) = c
866  ppoly_coefficients(i1,4) = d
867  ppoly_coefficients(i1,5) = e
868 
Here is the caller graph for this function:

◆ pqm_limiter()

subroutine pqm_functions::pqm_limiter ( integer, intent(in)  N,
real, dimension(:), intent(in)  h,
real, dimension(:), intent(in)  u,
real, dimension(:,:), intent(inout)  ppoly_E,
real, dimension(:,:), intent(inout)  ppoly_S 
)
private

Definition at line 89 of file PQM_functions.F90.

References regrid_edge_values::bound_edge_values(), regrid_edge_values::check_discontinuous_edge_values(), and h_neglect.

Referenced by pqm_reconstruction().

89 !------------------------------------------------------------------------------
90 ! Standard PQM limiter (White & Adcroft, JCP 2008).
91 !
92 ! grid: one-dimensional grid (see grid.F90)
93 ! ppoly: piecewise quadratic polynomial to be reconstructed (see ppoly.F90)
94 ! u: cell averages
95 !
96 ! It is assumed that the dimension of 'u' is equal to the number of cells
97 ! defining 'grid' and 'ppoly'. No consistency check is performed.
98 !------------------------------------------------------------------------------
99 
100  ! Arguments
101  integer, intent(in) :: n ! Number of cells
102  real, dimension(:), intent(in) :: h ! cell widths (size N)
103  real, dimension(:), intent(in) :: u ! cell averages (size N)
104  real, dimension(:,:), intent(inout) :: ppoly_e !Edge value of polynomial
105  real, dimension(:,:), intent(inout) :: ppoly_s !Edge slope of polynomial
106 
107  ! Local variables
108  integer :: k ! loop index
109  integer :: inflexion_l
110  integer :: inflexion_r
111  real :: u0_l, u0_r ! edge values
112  real :: u1_l, u1_r ! edge slopes
113  real :: u_l, u_c, u_r ! left, center and right cell averages
114  real :: h_l, h_c, h_r ! left, center and right cell widths
115  real :: sigma_l, sigma_c, sigma_r ! left, center and right
116  ! van Leer slopes
117  real :: slope ! retained PLM slope
118  real :: a, b, c, d, e
119  real :: alpha1, alpha2, alpha3
120  real :: rho, sqrt_rho
121  real :: gradient1, gradient2
122  real :: x1, x2
123 
124  ! Bound edge values
125  call bound_edge_values( n, h, u, ppoly_e )
126 
127  ! Make discontinuous edge values monotonic (thru averaging)
128  call check_discontinuous_edge_values( n, u, ppoly_e )
129 
130  ! Loop on interior cells to apply the PQM limiter
131  do k = 2,n-1
132 
133  !if ( h(k) .lt. 1.0 ) cycle
134 
135  inflexion_l = 0
136  inflexion_r = 0
137 
138  ! Get edge values, edge slopes and cell width
139  u0_l = ppoly_e(k,1)
140  u0_r = ppoly_e(k,2)
141  u1_l = ppoly_s(k,1)
142  u1_r = ppoly_s(k,2)
143 
144  ! Get cell widths and cell averages (boundary cells are assumed to
145  ! be local extrema for the sake of slopes)
146  h_l = h(k-1)
147  h_c = h(k)
148  h_r = h(k+1)
149  u_l = u(k-1)
150  u_c = u(k)
151  u_r = u(k+1)
152 
153  ! Compute limited slope
154  sigma_l = 2.0 * ( u_c - u_l ) / ( h_c + h_neglect )
155  sigma_c = 2.0 * ( u_r - u_l ) / ( h_l + 2.0*h_c + h_r + h_neglect )
156  sigma_r = 2.0 * ( u_r - u_c ) / ( h_c + h_neglect )
157 
158  if ( (sigma_l * sigma_r) .GT. 0.0 ) then
159  slope = sign( min(abs(sigma_l),abs(sigma_c),abs(sigma_r)), sigma_c )
160  else
161  slope = 0.0
162  end if
163 
164  ! If one of the slopes has the wrong sign compared with the
165  ! limited PLM slope, it is set equal to the limited PLM slope
166  if ( u1_l*slope .le. 0.0 ) u1_l = slope
167  if ( u1_r*slope .le. 0.0 ) u1_r = slope
168 
169  ! Local extremum --> flatten
170  if ( (u0_r - u_c) * (u_c - u0_l) .le. 0.0) then
171  u0_l = u_c
172  u0_r = u_c
173  u1_l = 0.0
174  u1_r = 0.0
175  inflexion_l = -1
176  inflexion_r = -1
177  end if
178 
179  ! Edge values are bounded and averaged when discontinuous and not
180  ! monotonic, edge slopes are consistent and the cell is not an extremum.
181  ! We now need to check and encorce the monotonicity of the quartic within
182  ! the cell
183  if ( (inflexion_l .EQ. 0) .AND. (inflexion_r .EQ. 0) ) then
184 
185  a = u0_l
186  b = h_c * u1_l
187  c = 30.0 * u(k) - 12.0*u0_r - 18.0*u0_l + 1.5*h_c*(u1_r - 3.0*u1_l)
188  d = -60.0 * u(k) + h_c *(6.0*u1_l - 4.0*u1_r) + 28.0*u0_r + 32.0*u0_l
189  e = 30.0 * u(k) + 2.5*h_c*(u1_r - u1_l) - 15.0*(u0_l + u0_r)
190 
191  ! Determine the coefficients of the second derivative
192  ! alpha1 xi^2 + alpha2 xi + alpha3
193  alpha1 = 6*e
194  alpha2 = 3*d
195  alpha3 = c
196 
197  rho = alpha2 * alpha2 - 4.0 * alpha1 * alpha3
198 
199  ! Check whether inflexion points exist
200  if (( alpha1 .ne. 0.0 ) .and. ( rho .ge. 0.0 )) then
201 
202  sqrt_rho = sqrt( rho )
203 
204  x1 = 0.5 * ( - alpha2 - sqrt_rho ) / alpha1
205  x2 = 0.5 * ( - alpha2 + sqrt_rho ) / alpha1
206 
207  ! Check whether both inflexion points lie in [0,1]
208  if ( (x1 .GE. 0.0) .AND. (x1 .LE. 1.0) .AND. &
209  (x2 .GE. 0.0) .AND. (x2 .LE. 1.0) ) then
210 
211  gradient1 = 4.0 * e * (x1**3) + 3.0 * d * (x1**2) + 2.0 * c * x1 + b
212  gradient2 = 4.0 * e * (x2**3) + 3.0 * d * (x2**2) + 2.0 * c * x2 + b
213 
214  ! Check whether one of the gradients is inconsistent
215  if ( (gradient1 * slope .LT. 0.0) .OR. &
216  (gradient2 * slope .LT. 0.0) ) then
217  ! Decide where to collapse inflexion points
218  ! (depends on one-sided slopes)
219  if ( abs(sigma_l) .LT. abs(sigma_r) ) then
220  inflexion_l = 1
221  else
222  inflexion_r = 1
223  end if
224  end if
225 
226  ! If both x1 and x2 do not lie in [0,1], check whether
227  ! only x1 lies in [0,1]
228  else if ( (x1 .GE. 0.0) .AND. (x1 .LE. 1.0) ) then
229 
230  gradient1 = 4.0 * e * (x1**3) + 3.0 * d * (x1**2) + 2.0 * c * x1 + b
231 
232  ! Check whether the gradient is inconsistent
233  if ( gradient1 * slope .LT. 0.0 ) then
234  ! Decide where to collapse inflexion points
235  ! (depends on one-sided slopes)
236  if ( abs(sigma_l) .LT. abs(sigma_r) ) then
237  inflexion_l = 1
238  else
239  inflexion_r = 1
240  end if
241  end if
242 
243  ! If x1 does not lie in [0,1], check whether x2 lies in [0,1]
244  else if ( (x2 .GE. 0.0) .AND. (x2 .LE. 1.0) ) then
245 
246  gradient2 = 4.0 * e * (x2**3) + 3.0 * d * (x2**2) + 2.0 * c * x2 + b
247 
248  ! Check whether the gradient is inconsistent
249  if ( gradient2 * slope .LT. 0.0 ) then
250  ! Decide where to collapse inflexion points
251  ! (depends on one-sided slopes)
252  if ( abs(sigma_l) .LT. abs(sigma_r) ) then
253  inflexion_l = 1
254  else
255  inflexion_r = 1
256  end if
257  end if
258 
259  end if ! end checking where the inflexion points lie
260 
261  end if ! end checking if alpha1 != 0 AND rho >= 0
262 
263  ! If alpha1 is zero, the second derivative of the quartic reduces
264  ! to a straight line
265  if (( alpha1 .eq. 0.0 ) .and. ( alpha2 .ne. 0.0 )) then
266 
267  x1 = - alpha3 / alpha2
268  if ( (x1 .ge. 0.0) .AND. (x1 .le. 1.0) ) then
269 
270  gradient1 = 4.0 * e * (x1**3) + 3.0 * d * (x1**2) + 2.0 * c * x1 + b
271 
272  ! Check whether the gradient is inconsistent
273  if ( gradient1 * slope .LT. 0.0 ) then
274  ! Decide where to collapse inflexion points
275  ! (depends on one-sided slopes)
276  if ( abs(sigma_l) .LT. abs(sigma_r) ) then
277  inflexion_l = 1
278  else
279  inflexion_r = 1
280  end if
281  end if ! check slope consistency
282 
283  end if
284 
285  end if ! end check whether we can find the root of the straight line
286 
287  end if ! end checking whether to shift inflexion points
288 
289  ! At this point, we know onto which edge to shift inflexion points
290  if ( inflexion_l .EQ. 1 ) then
291 
292  ! We modify the edge slopes so that both inflexion points
293  ! collapse onto the left edge
294  u1_l = ( 10.0 * u_c - 2.0 * u0_r - 8.0 * u0_l ) / (3.0*h_c + h_neglect )
295  u1_r = ( -10.0 * u_c + 6.0 * u0_r + 4.0 * u0_l ) / ( h_c + h_neglect )
296 
297  ! One of the modified slopes might be inconsistent. When that happens,
298  ! the inconsistent slope is set equal to zero and the opposite edge value
299  ! and edge slope are modified in compliance with the fact that both
300  ! inflexion points must still be located on the left edge
301  if ( u1_l * slope .LT. 0.0 ) then
302 
303  u1_l = 0.0
304  u0_r = 5.0 * u_c - 4.0 * u0_l
305  u1_r = 20.0 * (u_c - u0_l) / ( h_c + h_neglect )
306 
307  else if ( u1_r * slope .LT. 0.0 ) then
308 
309  u1_r = 0.0
310  u0_l = (5.0*u_c - 3.0*u0_r) / 2.0
311  u1_l = 10.0 * (-u_c + u0_r) / (3.0 * h_c + h_neglect)
312 
313  end if
314 
315  else if ( inflexion_r .EQ. 1 ) then
316 
317  ! We modify the edge slopes so that both inflexion points
318  ! collapse onto the right edge
319  u1_r = ( -10.0 * u_c + 8.0 * u0_r + 2.0 * u0_l ) / (3.0 * h_c + h_neglect)
320  u1_l = ( 10.0 * u_c - 4.0 * u0_r - 6.0 * u0_l ) / (h_c + h_neglect)
321 
322  ! One of the modified slopes might be inconsistent. When that happens,
323  ! the inconsistent slope is set equal to zero and the opposite edge value
324  ! and edge slope are modified in compliance with the fact that both
325  ! inflexion points must still be located on the right edge
326  if ( u1_l * slope .LT. 0.0 ) then
327 
328  u1_l = 0.0
329  u0_r = ( 5.0 * u_c - 3.0 * u0_l ) / 2.0
330  u1_r = 10.0 * (u_c - u0_l) / (3.0 * h_c + h_neglect)
331 
332  else if ( u1_r * slope .LT. 0.0 ) then
333 
334  u1_r = 0.0
335  u0_l = 5.0 * u_c - 4.0 * u0_r
336  u1_l = 20.0 * ( -u_c + u0_r ) / (h_c + h_neglect)
337 
338  end if
339 
340  end if ! clause to check where to collapse inflexion points
341 
342  ! Save edge values and edge slopes for reconstruction
343  ppoly_e(k,1) = u0_l
344  ppoly_e(k,2) = u0_r
345  ppoly_s(k,1) = u1_l
346  ppoly_s(k,2) = u1_r
347 
348  end do ! end loop on interior cells
349 
350  ! Constant reconstruction within boundary cells
351  ppoly_e(1,:) = u(1)
352  ppoly_s(1,:) = 0.0
353 
354  ppoly_e(n,:) = u(n)
355  ppoly_s(n,:) = 0.0
356 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ pqm_reconstruction()

subroutine, public pqm_functions::pqm_reconstruction ( integer, intent(in)  N,
real, dimension(:), intent(in)  h,
real, dimension(:), intent(in)  u,
real, dimension(:,:), intent(inout)  ppoly_E,
real, dimension(:,:), intent(inout)  ppoly_S,
real, dimension(:,:), intent(inout)  ppoly_coefficients 
)

Definition at line 27 of file PQM_functions.F90.

References pqm_limiter().

Referenced by mom_remapping::build_reconstructions_1d(), and regrid_interp::regridding_set_ppolys().

27 !------------------------------------------------------------------------------
28 ! Reconstruction by quartic polynomials within each cell.
29 !
30 ! grid: one-dimensional grid (see grid.F90)
31 ! ppoly: piecewise quartic polynomial to be reconstructed (see ppoly.F90)
32 ! u: cell averages
33 !
34 ! It is assumed that the dimension of 'u' is equal to the number of cells
35 ! defining 'grid' and 'ppoly'. No consistency check is performed.
36 !------------------------------------------------------------------------------
37 
38  ! Arguments
39  integer, intent(in) :: n ! Number of cells
40  real, dimension(:), intent(in) :: h ! cell widths (size N)
41  real, dimension(:), intent(in) :: u ! cell averages (size N)
42  real, dimension(:,:), intent(inout) :: ppoly_e !Edge value of polynomial
43  real, dimension(:,:), intent(inout) :: ppoly_s !Edge slope of polynomial
44  real, dimension(:,:), intent(inout) :: ppoly_coefficients !Coefficients of polynomial
45 
46  ! Local variables
47  integer :: k ! loop index
48  real :: h_c ! cell width
49  real :: u0_l, u0_r ! edge values (left and right)
50  real :: u1_l, u1_r ! edge slopes (left and right)
51  real :: a, b, c, d, e ! parabola coefficients
52 
53  ! PQM limiter
54  call pqm_limiter( n, h, u, ppoly_e, ppoly_s )
55 
56  ! Loop on cells to construct the cubic within each cell
57  do k = 1,n
58 
59  u0_l = ppoly_e(k,1)
60  u0_r = ppoly_e(k,2)
61 
62  u1_l = ppoly_s(k,1)
63  u1_r = ppoly_s(k,2)
64 
65  h_c = h(k)
66 
67  a = u0_l
68  b = h_c * u1_l
69  c = 30.0 * u(k) - 12.0*u0_r - 18.0*u0_l + 1.5*h_c*(u1_r - 3.0*u1_l)
70  d = -60.0 * u(k) + h_c *(6.0*u1_l - 4.0*u1_r) + 28.0*u0_r + 32.0*u0_l
71  e = 30.0 * u(k) + 2.5*h_c*(u1_r - u1_l) - 15.0*(u0_l + u0_r)
72 
73  ! Store coefficients
74  ppoly_coefficients(k,1) = a
75  ppoly_coefficients(k,2) = b
76  ppoly_coefficients(k,3) = c
77  ppoly_coefficients(k,4) = d
78  ppoly_coefficients(k,5) = e
79 
80  end do ! end loop on cells
81 
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ h_neglect

real, parameter pqm_functions::h_neglect = 1.E-30
private

Definition at line 19 of file PQM_functions.F90.

Referenced by pqm_boundary_extrapolation_v1(), and pqm_limiter().

19 real, parameter :: h_neglect = 1.e-30