Recon1d_EMPLM_WA.F90
1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> Extrapolated-Monotonized Piecewise Linear Method 1D reconstruction
6!!
7!! This extends MPLM_WA, following White and Adcroft, 2008 \cite white2008, by extrapolating for the slopes of the
8!! first and last cells. This extrapolation is used by White et al., 2009, during grid-generation.
9module recon1d_emplm_wa
10
11use recon1d_mplm_wa, only : mplm_wa, testing
12
13implicit none ; private
14
15public emplm_wa
16
17!> Extraplated Monotonic PLM reconstruction of White and Adcroft, 2008
18!!
19!! The source for the methods ultimately used by this class are:
20!! - init() -> recon1d_mplm_wa -> recon1d_plm_cw.init()
21!! - reconstruct() *locally defined
22!! - average() -> recon1d_mplm_wa -> recon1d_plm_cw.average()
23!! - f() -> recon1d_mplm_wa -> recon1d_plm_cw.f()
24!! - dfdx() -> recon1d_mplm_wa -> recon1d_plm_cw.dfdx()
25!! - check_reconstruction() -> recon1d_mplm_wa.check_reconstruction()
26!! - unit_tests() *locally defined
27!! - destroy() -> recon1d_mplm_wa -> recon1d_plm_cw.destroy()
28!! - remap_to_sub_grid() -> recon1d_mplm_wa -> recon1d_plm_cw -> recon1d_type.remap_to_sub_grid()
29!! - init_parent() -> recon1d_mplm_wa -> recon1d_plm_cw.init()
30!! - reconstruct_parent() -> recon1d_mplm_wa.reconstruct()
32
33contains
34 !> Implementation of the EMPLM_WA reconstruction with boundary extrapolation
35 procedure :: reconstruct => reconstruct
36 !> Implementation of unit tests for the EMPLM_WA reconstruction
37 procedure :: unit_tests => unit_tests
38
39end type emplm_wa
40
41contains
42
43!> Calculate a 1D PLM reconstruction based on h(:) and u(:)
44subroutine reconstruct(this, h, u)
45 class(emplm_wa), intent(inout) :: this !< This reconstruction
46 real, intent(in) :: h(*) !< Grid spacing (thickness) [typically H]
47 real, intent(in) :: u(*) !< Cell mean values [A]
48 ! Local variables
49 integer :: n
50 real :: slope ! Difference of u across cell [A]
51 real :: edge_h2 ! Edge value found by linear interpolation [A]
52 real :: slope_h2 ! Twice the difference between cell center and 2nd order edge value [A]
53 real :: slope_e ! Twice the difference between cell center and neighbor edge value [A]
54 real :: hn, hc ! Neighbor and central cell thicknesses adjusted by h_neglect [H]
55 real :: u_min, u_max ! Working values for bounding edge values [A]
56
57 ! Use parent (MPLM_WA) reconstruction
58 call this%reconstruct_parent(h, u)
59
60 ! Fix reconstruction for first cell
61 ! Avoid division by zero for vanished cells
62 hn = h(2) + this%h_neglect
63 hc = h(1) + this%h_neglect
64 edge_h2 = ( u(2) * hc + u(1) * hn ) / ( hn + hc )
65 slope_h2 = 2.0 * ( edge_h2 - u(1) )
66 slope_e = 2.0 * ( this%ul(2) - u(1) )
67 slope = sign( min( abs(slope_h2), abs(slope_e) ), u(2) - u(1) )
68 edge_h2 = u(1) + 0.5 * slope
69 u_min = min( this%ul(2), u(1) )
70 u_max = max( this%ul(2), u(1) )
71 this%ur(1) = max( u_min, min( u_max, edge_h2 ) )
72 this%ul(1) = u(1) - 0.5 * slope
73! slope = - PLM_extrapolate_slope( h(2), h(1), this%h_neglect, this%ul(2), u(1) )
74! this%ul(1) = u(1) - 0.5 * slope
75! this%ur(1) = u(1) + 0.5 * slope
76
77 ! Fix reconstruction for last cell
78 n = this%n
79 ! Avoid division by zero for vanished cells
80 hn = h(n-1) + this%h_neglect
81 hc = h(n) + this%h_neglect
82 edge_h2 = ( u(n-1) * hc + u(n) * hn ) / ( hn + hc )
83 slope_h2 = 2.0 * ( u(n) - edge_h2 )
84 slope_e = 2.0 * ( u(n) - this%ur(n-1) )
85 slope = sign( min( abs(slope_h2), abs(slope_e) ), u(n) - u(n-1) )
86 edge_h2 = u(n) - 0.5 * slope
87 u_min = min( this%ur(n-1), u(n) )
88 u_max = max( this%ur(n-1), u(n) )
89 this%ul(n) = max( u_min, min( u_max, edge_h2 ) )
90 this%ur(n) = u(n) + 0.5 * slope
91! slope = PLM_extrapolate_slope( h(n-1), h(n), this%h_neglect, this%ur(n-1), u(n) )
92! this%ul(n) = u(n) - 0.5 * slope
93! this%ur(n) = u(n) + 0.5 * slope
94
95end subroutine reconstruct
96
97!> Returns a PLM slope using h2 extrapolation from a cell to the left, in the same
98!! arbitrary units as the input values [A].
99!! Use the negative to extrapolate from the cell to the right.
100real elemental pure function plm_extrapolate_slope(h_l, h_c, h_neglect, u_l, u_c)
101 real, intent(in) :: h_l !< Thickness of left cell in arbitrary grid thickness units [H]
102 real, intent(in) :: h_c !< Thickness of center cell in arbitrary grid thickness units [H]
103 real, intent(in) :: h_neglect !< A negligible thickness [H]
104 real, intent(in) :: u_l !< Value of left cell in arbitrary units [A]
105 real, intent(in) :: u_c !< Value of center cell in arbitrary units [A]
106 ! Local variables
107 real :: left_edge ! Left edge value [A]
108 real :: hl, hc ! Left and central cell thicknesses [H]
109
110 ! Avoid division by zero for vanished cells
111 hl = h_l + h_neglect
112 hc = h_c + h_neglect
113
114 ! The h2 scheme is used to compute the left edge value
115 left_edge = (u_l*hc + u_c*hl) / (hl + hc)
116
117 plm_extrapolate_slope = 2.0 * ( u_c - left_edge )
118
119end function plm_extrapolate_slope
120
121!> Runs PLM reconstruction unit tests and returns True for any fails, False otherwise
122logical function unit_tests(this, verbose, stdout, stderr)
123 class(emplm_wa), intent(inout) :: this !< This reconstruction
124 logical, intent(in) :: verbose !< True, if verbose
125 integer, intent(in) :: stdout !< I/O channel for stdout
126 integer, intent(in) :: stderr !< I/O channel for stderr
127 ! Local variables
128 real, allocatable :: ul(:), ur(:), um(:) ! test values [A]
129 real, allocatable :: ull(:), urr(:) ! test values [A]
130 type(testing) :: test ! convenience functions
131 integer :: k
132
133 call test%set( stdout=stdout ) ! Sets the stdout channel in test
134 call test%set( stderr=stderr ) ! Sets the stderr channel in test
135 call test%set( verbose=verbose ) ! Sets the verbosity flag in test
136
137 call this%init(3)
138 call test%test( this%n /= 3, 'Setting number of levels')
139 allocate( um(3), ul(3), ur(3), ull(3), urr(3) )
140
141 call this%reconstruct( (/2.,2.,2./), (/1.,3.,5./) )
142 call test%real_arr(3, this%u_mean, (/1.,3.,5./), 'Setting cell values')
143
144 do k = 1, 3
145 ul(k) = this%f(k, 0.)
146 um(k) = this%f(k, 0.5)
147 ur(k) = this%f(k, 1.)
148 enddo
149 call test%real_arr(3, ul, (/0.,2.,4./), 'Evaluation on left edge')
150 call test%real_arr(3, um, (/1.,3.,5./), 'Evaluation in center')
151 call test%real_arr(3, ur, (/2.,4.,6./), 'Evaluation on right edge')
152
153 do k = 1, 3
154 ul(k) = this%dfdx(k, 0.)
155 um(k) = this%dfdx(k, 0.5)
156 ur(k) = this%dfdx(k, 1.)
157 enddo
158 call test%real_arr(3, ul, (/2.,2.,2./), 'dfdx on left edge')
159 call test%real_arr(3, um, (/2.,2.,2./), 'dfdx in center')
160 call test%real_arr(3, ur, (/2.,2.,2./), 'dfdx on right edge')
161
162 do k = 1, 3
163 um(k) = this%average(k, 0.5, 0.75) ! Average from x=0.25 to 0.75 in each cell
164 enddo
165 call test%real_arr(3, um, (/1.25,3.25,5.25/), 'Return interval average')
166
167 unit_tests = test%summarize('EMPLM_WA:unit_tests')
168
169end function unit_tests
170
171!> \namespace recon1d_emplm_wa
172!!
173
174end module recon1d_emplm_wa