MOM6
PPM_functions.F90
1 !> Provides functions used with the Piecewise-Parabolic-Method in the vertical ALE algorithm.
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 ! First version was created by Laurent White, June 2008.
7 ! Substantially re-factored January 2016.
8 
9 !! @todo Re-factor PPM_boundary_extrapolation to give round-off safe and
10 !! optimization independent results.
11 
12 use regrid_edge_values, only : bound_edge_values, check_discontinuous_edge_values
13 
14 implicit none ; private
15 
16 public ppm_reconstruction, ppm_boundary_extrapolation
17 
18 !> A tiny width that is so small that adding it to cell widths does not
19 !! change the value due to a computational representation. It is used
20 !! to avoid division by zero.
21 !! @note This is a dimensional parameter and should really include a unit
22 !! conversion.
23 real, parameter :: hneglect_dflt = 1.e-30
24 
25 contains
26 
27 !> Builds quadratic polynomials coefficients from cell mean and edge values.
28 subroutine ppm_reconstruction( N, h, u, edge_values, ppoly_coef, h_neglect, answers_2018)
29  integer, intent(in) :: n !< Number of cells
30  real, dimension(N), intent(in) :: h !< Cell widths [H]
31  real, dimension(N), intent(in) :: u !< Cell averages [A]
32  real, dimension(N,2), intent(inout) :: edge_values !< Edge values [A]
33  real, dimension(N,3), intent(inout) :: ppoly_coef !< Polynomial coefficients, mainly [A]
34  real, optional, intent(in) :: h_neglect !< A negligibly small width [H]
35  logical, optional, intent(in) :: answers_2018 !< If true use older, less acccurate expressions.
36 
37  ! Local variables
38  integer :: k ! Loop index
39  real :: edge_l, edge_r ! Edge values (left and right)
40 
41  ! PPM limiter
42  call ppm_limiter_standard( n, h, u, edge_values, h_neglect, answers_2018 )
43 
44  ! Loop over all cells
45  do k = 1,n
46 
47  edge_l = edge_values(k,1)
48  edge_r = edge_values(k,2)
49 
50  ! Store polynomial coefficients
51  ppoly_coef(k,1) = edge_l
52  ppoly_coef(k,2) = 4.0 * ( u(k) - edge_l ) + 2.0 * ( u(k) - edge_r )
53  ppoly_coef(k,3) = 3.0 * ( ( edge_r - u(k) ) + ( edge_l - u(k) ) )
54 
55  enddo
56 
57 end subroutine ppm_reconstruction
58 
59 !> Adjusts edge values using the standard PPM limiter (Colella & Woodward, JCP 1984)
60 !! after first checking that the edge values are bounded by neighbors cell averages
61 !! and that the edge values are monotonic between cell averages.
62 subroutine ppm_limiter_standard( N, h, u, edge_values, h_neglect, answers_2018 )
63  integer, intent(in) :: N !< Number of cells
64  real, dimension(:), intent(in) :: h !< cell widths (size N) [H]
65  real, dimension(:), intent(in) :: u !< cell average properties (size N) [A]
66  real, dimension(:,:), intent(inout) :: edge_values !< Potentially modified edge values [A]
67  real, optional, intent(in) :: h_neglect !< A negligibly small width [H]
68  logical, optional, intent(in) :: answers_2018 !< If true use older, less acccurate expressions.
69 
70  ! Local variables
71  integer :: k ! Loop index
72  real :: u_l, u_c, u_r ! Cell averages (left, center and right)
73  real :: edge_l, edge_r ! Edge values (left and right)
74  real :: expr1, expr2
75 
76  ! Bound edge values
77  call bound_edge_values( n, h, u, edge_values, h_neglect, answers_2018 )
78 
79  ! Make discontinuous edge values monotonic
80  call check_discontinuous_edge_values( n, u, edge_values )
81 
82  ! Loop on interior cells to apply the standard
83  ! PPM limiter (Colella & Woodward, JCP 84)
84  do k = 2,n-1
85 
86  ! Get cell averages
87  u_l = u(k-1)
88  u_c = u(k)
89  u_r = u(k+1)
90 
91  edge_l = edge_values(k,1)
92  edge_r = edge_values(k,2)
93 
94  if ( (u_r - u_c)*(u_c - u_l) <= 0.0) then
95  ! Flatten extremum
96  edge_l = u_c
97  edge_r = u_c
98  else
99  expr1 = 3.0 * (edge_r - edge_l) * ( (u_c - edge_l) + (u_c - edge_r))
100  expr2 = (edge_r - edge_l) * (edge_r - edge_l)
101  if ( expr1 > expr2 ) then
102  ! Place extremum at right edge of cell by adjusting left edge value
103  edge_l = u_c + 2.0 * ( u_c - edge_r )
104  edge_l = max( min( edge_l, max(u_l, u_c) ), min(u_l, u_c) ) ! In case of round off
105  elseif ( expr1 < -expr2 ) then
106  ! Place extremum at left edge of cell by adjusting right edge value
107  edge_r = u_c + 2.0 * ( u_c - edge_l )
108  edge_r = max( min( edge_r, max(u_r, u_c) ), min(u_r, u_c) ) ! In case of round off
109  endif
110  endif
111  ! This checks that the difference in edge values is representable
112  ! and avoids overshoot problems due to round off.
113  !### The 1.e-60 needs to have units of [A], so this dimensionally inconsisent.
114  if ( abs( edge_r - edge_l )<max(1.e-60,epsilon(u_c)*abs(u_c)) ) then
115  edge_l = u_c
116  edge_r = u_c
117  endif
118 
119  edge_values(k,1) = edge_l
120  edge_values(k,2) = edge_r
121 
122  enddo ! end loop on interior cells
123 
124  ! PCM within boundary cells
125  edge_values(1,:) = u(1)
126  edge_values(n,:) = u(n)
127 
128 end subroutine ppm_limiter_standard
129 
130 
131 !------------------------------------------------------------------------------
132 !> Reconstruction by parabolas within boundary cells
133 subroutine ppm_boundary_extrapolation( N, h, u, edge_values, ppoly_coef, h_neglect)
134 !------------------------------------------------------------------------------
135 ! Reconstruction by parabolas within boundary cells.
136 !
137 ! The following explanations apply to the left boundary cell. The same
138 ! reasoning holds for the right boundary cell.
139 !
140 ! A parabola needs to be built in the cell and requires three degrees of
141 ! freedom, which are the right edge value and slope and the cell average.
142 ! The right edge values and slopes are taken to be that of the neighboring
143 ! cell (i.e., the left edge value and slope of the neighboring cell).
144 ! The resulting parabola is not necessarily monotonic and the traditional
145 ! PPM limiter is used to modify one of the edge values in order to yield
146 ! a monotonic parabola.
147 !
148 ! N: number of cells in grid
149 ! h: thicknesses of grid cells
150 ! u: cell averages to use in constructing piecewise polynomials
151 ! edge_values : edge values of piecewise polynomials
152 ! ppoly_coef : coefficients of piecewise polynomials
153 !
154 ! It is assumed that the size of the array 'u' is equal to the number of cells
155 ! defining 'grid' and 'ppoly'. No consistency check is performed here.
156 !------------------------------------------------------------------------------
157 
158  ! Arguments
159  integer, intent(in) :: n !< Number of cells
160  real, dimension(:), intent(in) :: h !< cell widths (size N) [H]
161  real, dimension(:), intent(in) :: u !< cell averages (size N) [A]
162  real, dimension(:,:), intent(inout) :: edge_values !< edge values of piecewise polynomials [A]
163  real, dimension(:,:), intent(inout) :: ppoly_coef !< coefficients of piecewise polynomials, mainly [A]
164  real, optional, intent(in) :: h_neglect !< A negligibly small width for
165  !! the purpose of cell reconstructions [H]
166 
167  ! Local variables
168  integer :: i0, i1
169  real :: u0, u1
170  real :: h0, h1
171  real :: a, b, c
172  real :: u0_l, u0_r
173  real :: u1_l, u1_r
174  real :: slope
175  real :: exp1, exp2
176  real :: hneglect
177 
178  hneglect = hneglect_dflt ; if (present(h_neglect)) hneglect = h_neglect
179 
180  ! ----- Left boundary -----
181  i0 = 1
182  i1 = 2
183  h0 = h(i0)
184  h1 = h(i1)
185  u0 = u(i0)
186  u1 = u(i1)
187 
188  ! Compute the left edge slope in neighboring cell and express it in
189  ! the global coordinate system
190  b = ppoly_coef(i1,2)
191  u1_r = b *((h0+hneglect)/(h1+hneglect)) ! derivative evaluated at xi = 0.0,
192  ! expressed w.r.t. xi (local coord. system)
193 
194  ! Limit the right slope by the PLM limited slope
195  slope = 2.0 * ( u1 - u0 )
196  if ( abs(u1_r) > abs(slope) ) then
197  u1_r = slope
198  endif
199 
200  ! The right edge value in the boundary cell is taken to be the left
201  ! edge value in the neighboring cell
202  u0_r = edge_values(i1,1)
203 
204  ! Given the right edge value and slope, we determine the left
205  ! edge value and slope by computing the parabola as determined by
206  ! the right edge value and slope and the boundary cell average
207  u0_l = 3.0 * u0 + 0.5 * u1_r - 2.0 * u0_r
208 
209  ! Apply the traditional PPM limiter
210  exp1 = (u0_r - u0_l) * (u0 - 0.5*(u0_l+u0_r))
211  exp2 = (u0_r - u0_l) * (u0_r - u0_l) / 6.0
212 
213  if ( exp1 > exp2 ) then
214  u0_l = 3.0 * u0 - 2.0 * u0_r
215  endif
216 
217  if ( exp1 < -exp2 ) then
218  u0_r = 3.0 * u0 - 2.0 * u0_l
219  endif
220 
221  edge_values(i0,1) = u0_l
222  edge_values(i0,2) = u0_r
223 
224  a = u0_l
225  b = 6.0 * u0 - 4.0 * u0_l - 2.0 * u0_r
226  c = 3.0 * ( u0_r + u0_l - 2.0 * u0 )
227 
228  ppoly_coef(i0,1) = a
229  ppoly_coef(i0,2) = b
230  ppoly_coef(i0,3) = c
231 
232  ! ----- Right boundary -----
233  i0 = n-1
234  i1 = n
235  h0 = h(i0)
236  h1 = h(i1)
237  u0 = u(i0)
238  u1 = u(i1)
239 
240  ! Compute the right edge slope in neighboring cell and express it in
241  ! the global coordinate system
242  b = ppoly_coef(i0,2)
243  c = ppoly_coef(i0,3)
244  u1_l = (b + 2*c) ! derivative evaluated at xi = 1.0
245  u1_l = u1_l * ((h1+hneglect)/(h0+hneglect))
246 
247  ! Limit the left slope by the PLM limited slope
248  slope = 2.0 * ( u1 - u0 )
249  if ( abs(u1_l) > abs(slope) ) then
250  u1_l = slope
251  endif
252 
253  ! The left edge value in the boundary cell is taken to be the right
254  ! edge value in the neighboring cell
255  u0_l = edge_values(i0,2)
256 
257  ! Given the left edge value and slope, we determine the right
258  ! edge value and slope by computing the parabola as determined by
259  ! the left edge value and slope and the boundary cell average
260  u0_r = 3.0 * u1 - 0.5 * u1_l - 2.0 * u0_l
261 
262  ! Apply the traditional PPM limiter
263  exp1 = (u0_r - u0_l) * (u1 - 0.5*(u0_l+u0_r))
264  exp2 = (u0_r - u0_l) * (u0_r - u0_l) / 6.0
265 
266  if ( exp1 > exp2 ) then
267  u0_l = 3.0 * u1 - 2.0 * u0_r
268  endif
269 
270  if ( exp1 < -exp2 ) then
271  u0_r = 3.0 * u1 - 2.0 * u0_l
272  endif
273 
274  edge_values(i1,1) = u0_l
275  edge_values(i1,2) = u0_r
276 
277  a = u0_l
278  b = 6.0 * u1 - 4.0 * u0_l - 2.0 * u0_r
279  c = 3.0 * ( u0_r + u0_l - 2.0 * u1 )
280 
281  ppoly_coef(i1,1) = a
282  ppoly_coef(i1,2) = b
283  ppoly_coef(i1,3) = c
284 
285 end subroutine ppm_boundary_extrapolation
286 
287 end module ppm_functions
Provides functions used with the Piecewise-Parabolic-Method in the vertical ALE algorithm.
Edge value estimation for high-order resconstruction.