6 use mom_cpu_clock,
only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
12 use mom_eos,
only : extract_member_eos, eos_linear, eos_teos10, eos_wright
22 use mom_remapping,
only : extract_member_remapping_cs, build_reconstructions_1d
23 use mom_remapping,
only : average_value_ppoly, remappingschemesdoc, remappingdefaultscheme
27 use ppm_functions,
only : ppm_reconstruction, ppm_boundary_extrapolation
30 implicit none ;
private
32 #include <MOM_memory.h>
34 public neutral_diffusion, neutral_diffusion_init, neutral_diffusion_end
35 public neutral_diffusion_calc_coeffs
36 public neutral_diffusion_unit_tests
43 logical :: continuous_reconstruction = .true.
44 logical :: refine_position = .false.
46 logical :: debug = .false.
52 real,
allocatable,
dimension(:,:,:) :: upol
53 real,
allocatable,
dimension(:,:,:) :: upor
54 integer,
allocatable,
dimension(:,:,:) :: ukol
56 integer,
allocatable,
dimension(:,:,:) :: ukor
58 real,
allocatable,
dimension(:,:,:) :: uheff
59 real,
allocatable,
dimension(:,:,:) :: vpol
60 real,
allocatable,
dimension(:,:,:) :: vpor
61 integer,
allocatable,
dimension(:,:,:) :: vkol
63 integer,
allocatable,
dimension(:,:,:) :: vkor
65 real,
allocatable,
dimension(:,:,:) :: vheff
67 real,
allocatable,
dimension(:,:,:,:) :: ppoly_coeffs_t
68 real,
allocatable,
dimension(:,:,:,:) :: ppoly_coeffs_s
70 real,
allocatable,
dimension(:,:,:) :: drdt
71 real,
allocatable,
dimension(:,:,:) :: drds
72 real,
allocatable,
dimension(:,:,:) :: tint
73 real,
allocatable,
dimension(:,:,:) :: sint
74 real,
allocatable,
dimension(:,:,:) :: pint
76 real,
allocatable,
dimension(:,:,:,:) :: t_i
77 real,
allocatable,
dimension(:,:,:,:) :: s_i
78 real,
allocatable,
dimension(:,:,:,:) :: drdt_i
79 real,
allocatable,
dimension(:,:,:,:) :: drds_i
80 integer,
allocatable,
dimension(:,:) :: ns
81 logical,
allocatable,
dimension(:,:,:) :: stable_cell
84 integer :: id_uheff_2d = -1
85 integer :: id_vheff_2d = -1
94 #include "version_variable.h"
95 character(len=40) :: mdl =
"MOM_neutral_diffusion"
100 logical function neutral_diffusion_init(Time, G, param_file, diag, EOS, CS)
101 type(time_type),
target,
intent(in) :: time
103 type(
diag_ctrl),
target,
intent(inout) :: diag
105 type(
eos_type),
target,
intent(in) :: eos
109 character(len=256) :: mesg
110 character(len=80) :: string
111 logical :: boundary_extrap
114 real :: drho_tol, xtol, ref_pres
116 if (
associated(cs))
then
117 call mom_error(fatal,
"neutral_diffusion_init called with associated control structure.")
123 "This module implements neutral diffusion of tracers")
124 call get_param(param_file, mdl,
"USE_NEUTRAL_DIFFUSION", neutral_diffusion_init, &
125 "If true, enables the neutral diffusion module.", &
128 if (.not.neutral_diffusion_init)
then
133 allocate(cs%ndiff_aux_CS)
139 call get_param(param_file, mdl,
"NDIFF_CONTINUOUS", cs%continuous_reconstruction, &
140 "If true, uses a continuous reconstruction of T and S when "//&
141 "finding neutral surfaces along which diffusion will happen. "//&
142 "If false, a PPM discontinuous reconstruction of T and S "//&
143 "is done which results in a higher order routine but exacts "//&
144 "a higher computational cost.", default=.true.)
145 call get_param(param_file, mdl,
"NDIFF_REF_PRES", cs%ref_pres, &
146 "The reference pressure (Pa) used for the derivatives of "//&
147 "the equation of state. If negative (default), local "//&
148 "pressure is used.", &
151 if (cs%continuous_reconstruction .eqv. .false.)
then
152 call get_param(param_file, mdl,
"NDIFF_BOUNDARY_EXTRAP", boundary_extrap, &
153 "Uses a rootfinding approach to find the position of a "//&
154 "neutral surface within a layer taking into account the "//&
155 "nonlinearity of the equation of state and the "//&
156 "polynomial reconstructions of T/S.", &
158 call get_param(param_file, mdl,
"NDIFF_REMAPPING_SCHEME", string, &
159 "This sets the reconstruction scheme used "//&
160 "for vertical remapping for all variables. "//&
161 "It can be one of the following schemes: "//&
162 trim(remappingschemesdoc), default=remappingdefaultscheme)
163 call initialize_remapping( cs%remap_CS, string, boundary_extrapolation = boundary_extrap )
164 call extract_member_remapping_cs(cs%remap_CS, degree=cs%deg)
165 call get_param(param_file, mdl,
"NDIFF_REFINE_POSITION", cs%refine_position, &
166 "Uses a rootfinding approach to find the position of a "//&
167 "neutral surface within a layer taking into account the "//&
168 "nonlinearity of the equation of state and the "//&
169 "polynomial reconstructions of T/S.", &
171 if (cs%refine_position)
then
172 call get_param(param_file, mdl,
"NDIFF_DRHO_TOL", drho_tol, &
173 "Sets the convergence criterion for finding the neutral "//&
174 "position within a layer in kg m-3.", &
176 call get_param(param_file, mdl,
"NDIFF_X_TOL", xtol, &
177 "Sets the convergence criterion for a change in nondim "//&
178 "position within a layer.", &
180 call get_param(param_file, mdl,
"NDIFF_MAX_ITER", max_iter, &
181 "The maximum number of iterations to be done before "//&
182 "exiting the iterative loop to find the neutral surface", &
184 call set_ndiff_aux_params(cs%ndiff_aux_CS, max_iter = max_iter, drho_tol = drho_tol, xtol = xtol)
186 call get_param(param_file, mdl,
"NDIFF_DEBUG", cs%debug, &
187 "Turns on verbose output for discontinuous neutral "//&
188 "diffusion routines.", &
190 call set_ndiff_aux_params(cs%ndiff_aux_CS, deg=cs%deg, ref_pres = cs%ref_pres, eos = eos, debug = cs%debug)
197 if (cs%continuous_reconstruction)
then
199 allocate(cs%dRdT(szi_(g),szj_(g),szk_(g)+1)) ; cs%dRdT(:,:,:) = 0.
200 allocate(cs%dRdS(szi_(g),szj_(g),szk_(g)+1)) ; cs%dRdS(:,:,:) = 0.
203 allocate(cs%T_i(szi_(g),szj_(g),szk_(g),2)) ; cs%T_i(:,:,:,:) = 0.
204 allocate(cs%S_i(szi_(g),szj_(g),szk_(g),2)) ; cs%S_i(:,:,:,:) = 0.
205 allocate(cs%dRdT_i(szi_(g),szj_(g),szk_(g),2)) ; cs%dRdT_i(:,:,:,:) = 0.
206 allocate(cs%dRdS_i(szi_(g),szj_(g),szk_(g),2)) ; cs%dRdS_i(:,:,:,:) = 0.
207 allocate(cs%ppoly_coeffs_T(szi_(g),szj_(g),szk_(g),cs%deg+1)) ; cs%ppoly_coeffs_T(:,:,:,:) = 0.
208 allocate(cs%ppoly_coeffs_S(szi_(g),szj_(g),szk_(g),cs%deg+1)) ; cs%ppoly_coeffs_S(:,:,:,:) = 0.
209 allocate(cs%ns(szi_(g),szj_(g))) ; cs%ns(:,:) = 0.
212 allocate(cs%Tint(szi_(g),szj_(g),szk_(g)+1)) ; cs%Tint(:,:,:) = 0.
213 allocate(cs%Sint(szi_(g),szj_(g),szk_(g)+1)) ; cs%Sint(:,:,:) = 0.
214 allocate(cs%Pint(szi_(g),szj_(g),szk_(g)+1)) ; cs%Pint(:,:,:) = 0.
215 allocate(cs%stable_cell(szi_(g),szj_(g),szk_(g))) ; cs%stable_cell(:,:,:) = .true.
217 allocate(cs%uPoL(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%uPoL(g%isc-1:g%iec,g%jsc:g%jec,:) = 0.
218 allocate(cs%uPoR(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%uPoR(g%isc-1:g%iec,g%jsc:g%jec,:) = 0.
219 allocate(cs%uKoL(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%uKoL(g%isc-1:g%iec,g%jsc:g%jec,:) = 0
220 allocate(cs%uKoR(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%uKoR(g%isc-1:g%iec,g%jsc:g%jec,:) = 0
221 allocate(cs%uHeff(g%isd:g%ied,g%jsd:g%jed,cs%nsurf-1)); cs%uHeff(g%isc-1:g%iec,g%jsc:g%jec,:) = 0
223 allocate(cs%vPoL(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%vPoL(g%isc:g%iec,g%jsc-1:g%jec,:) = 0.
224 allocate(cs%vPoR(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%vPoR(g%isc:g%iec,g%jsc-1:g%jec,:) = 0.
225 allocate(cs%vKoL(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%vKoL(g%isc:g%iec,g%jsc-1:g%jec,:) = 0
226 allocate(cs%vKoR(g%isd:g%ied,g%jsd:g%jed, cs%nsurf)); cs%vKoR(g%isc:g%iec,g%jsc-1:g%jec,:) = 0
227 allocate(cs%vHeff(g%isd:g%ied,g%jsd:g%jed,cs%nsurf-1)); cs%vHeff(g%isc:g%iec,g%jsc-1:g%jec,:) = 0
229 end function neutral_diffusion_init
233 subroutine neutral_diffusion_calc_coeffs(G, GV, h, T, S, CS)
236 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: h
237 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: t
238 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: s
244 real,
dimension(SZK_(G),2) :: ppoly_r_s
245 real,
dimension(SZI_(G), SZJ_(G)) :: heff_sum
247 real,
dimension(SZI_(G)) :: ref_pres
248 real :: h_neglect, h_neglect_edge
251 pa_to_h = 1. / gv%H_to_pa
254 if (gv%Boussinesq)
then
255 h_neglect = gv%m_to_H*1.0e-30 ; h_neglect_edge = gv%m_to_H*1.0e-10
257 h_neglect = gv%kg_m2_to_H*1.0e-30 ; h_neglect_edge = gv%kg_m2_to_H*1.0e-10
261 if (cs%ref_pres>=0.)
then
262 ref_pres(:) = cs%ref_pres
265 if (cs%continuous_reconstruction)
then
271 cs%dRdT_i(:,:,:,:) = 0.
272 cs%dRdS_i(:,:,:,:) = 0.
274 cs%stable_cell(:,:,:) = .true.
279 do k=1,g%ke ;
do j=g%jsc-1, g%jec+1 ;
do i=g%isc-1,g%iec+1
280 cs%Pint(i,j,k+1) = cs%Pint(i,j,k) + h(i,j,k)*gv%H_to_Pa
281 enddo ;
enddo ;
enddo
283 do j = g%jsc-1, g%jec+1
285 do i = g%isc-1, g%iec+1
286 if (cs%continuous_reconstruction)
then
287 call interface_scalar(g%ke, h(i,j,:), t(i,j,:), cs%Tint(i,j,:), 2, h_neglect)
288 call interface_scalar(g%ke, h(i,j,:), s(i,j,:), cs%Sint(i,j,:), 2, h_neglect)
290 call build_reconstructions_1d( cs%remap_CS, g%ke, h(i,j,:), t(i,j,:), cs%ppoly_coeffs_T(i,j,:,:), &
291 cs%T_i(i,j,:,:), ppoly_r_s, imethod, h_neglect, h_neglect_edge )
292 call build_reconstructions_1d( cs%remap_CS, g%ke, h(i,j,:), s(i,j,:), cs%ppoly_coeffs_S(i,j,:,:), &
293 cs%S_i(i,j,:,:), ppoly_r_s, imethod, h_neglect, h_neglect_edge )
298 if (cs%continuous_reconstruction)
then
300 if (cs%ref_pres<0) ref_pres(:) = cs%Pint(:,j,k)
302 cs%dRdT(:,j,k), cs%dRdS(:,j,k), g%isc-1, g%iec-g%isc+3, cs%EOS)
306 if (cs%ref_pres<0) ref_pres(:) = cs%Pint(:,j,k)
308 cs%dRdT_i(:,j,k,1), cs%dRdS_i(:,j,k,1), g%isc-1, g%iec-g%isc+3, cs%EOS)
309 if (cs%ref_pres<0)
then
310 ref_pres(:) = cs%Pint(:,j,k+1)
313 cs%dRdT_i(:,j,k,2), cs%dRdS_i(:,j,k,2), g%isc-1, g%iec-g%isc+3, cs%EOS)
318 if (.not. cs%continuous_reconstruction)
then
319 do j = g%jsc-1, g%jec+1 ;
do i = g%isc-1, g%iec+1
320 call mark_unstable_cells( g%ke, cs%dRdT_i(i,j,:,:), cs%dRdS_i(i,j,:,:), cs%T_i(i,j,:,:), cs%S_i(i,j,:,:), &
321 cs%stable_cell(i,j,:), cs%ns(i,j) )
337 do j = g%jsc, g%jec ;
do i = g%isc-1, g%iec
338 if (g%mask2dCu(i,j) > 0.)
then
339 if (cs%continuous_reconstruction)
then
340 call find_neutral_surface_positions_continuous(g%ke, &
341 cs%Pint(i,j,:), cs%Tint(i,j,:), cs%Sint(i,j,:), cs%dRdT(i,j,:), cs%dRdS(i,j,:), &
342 cs%Pint(i+1,j,:), cs%Tint(i+1,j,:), cs%Sint(i+1,j,:), cs%dRdT(i+1,j,:), cs%dRdS(i+1,j,:), &
343 cs%uPoL(i,j,:), cs%uPoR(i,j,:), cs%uKoL(i,j,:), cs%uKoR(i,j,:), cs%uhEff(i,j,:) )
345 call find_neutral_surface_positions_discontinuous(cs, g%ke, cs%ns(i,j)+cs%ns(i+1,j), &
346 cs%Pint(i,j,:), h(i,j,:), cs%T_i(i,j,:,:), cs%S_i(i,j,:,:), &
347 cs%dRdT_i(i,j,:,:), cs%dRdS_i(i,j,:,:), cs%stable_cell(i,j,:), &
348 cs%Pint(i+1,j,:), h(i+1,j,:), cs%T_i(i+1,j,:,:), cs%S_i(i+1,j,:,:), &
349 cs%dRdT_i(i+1,j,:,:), cs%dRdS_i(i+1,j,:,:), cs%stable_cell(i+1,j,:), &
350 cs%uPoL(i,j,:), cs%uPoR(i,j,:), cs%uKoL(i,j,:), cs%uKoR(i,j,:), cs%uhEff(i,j,:), &
351 cs%ppoly_coeffs_T(i,j,:,:), cs%ppoly_coeffs_S(i,j,:,:), &
352 cs%ppoly_coeffs_T(i+1,j,:,:), cs%ppoly_coeffs_S(i+1,j,:,:))
358 do j = g%jsc-1, g%jec ;
do i = g%isc, g%iec
359 if (g%mask2dCv(i,j) > 0.)
then
360 if (cs%continuous_reconstruction)
then
361 call find_neutral_surface_positions_continuous(g%ke, &
362 cs%Pint(i,j,:), cs%Tint(i,j,:), cs%Sint(i,j,:), cs%dRdT(i,j,:), cs%dRdS(i,j,:), &
363 cs%Pint(i,j+1,:), cs%Tint(i,j+1,:), cs%Sint(i,j+1,:), cs%dRdT(i,j+1,:), cs%dRdS(i,j+1,:), &
364 cs%vPoL(i,j,:), cs%vPoR(i,j,:), cs%vKoL(i,j,:), cs%vKoR(i,j,:), cs%vhEff(i,j,:) )
366 call find_neutral_surface_positions_discontinuous(cs, g%ke, cs%ns(i,j)+cs%ns(i,j+1), &
367 cs%Pint(i,j,:), h(i,j,:), cs%T_i(i,j,:,:), cs%S_i(i,j,:,:), &
368 cs%dRdT_i(i,j,:,:), cs%dRdS_i(i,j,:,:), cs%stable_cell(i,j,:), &
369 cs%Pint(i,j+1,:), h(i,j+1,:), cs%T_i(i,j+1,:,:), cs%S_i(i,j+1,:,:), &
370 cs%dRdT_i(i,j+1,:,:), cs%dRdS_i(i,j+1,:,:), cs%stable_cell(i,j+1,:), &
371 cs%vPoL(i,j,:), cs%vPoR(i,j,:), cs%vKoL(i,j,:), cs%vKoR(i,j,:), cs%vhEff(i,j,:), &
372 cs%ppoly_coeffs_T(i,j,:,:), cs%ppoly_coeffs_S(i,j,:,:), &
373 cs%ppoly_coeffs_T(i,j+1,:,:), cs%ppoly_coeffs_S(i,j+1,:,:))
383 if (cs%continuous_reconstruction)
then
384 do k = 1, cs%nsurf-1 ;
do j = g%jsc, g%jec ;
do i = g%isc-1, g%iec
385 if (g%mask2dCu(i,j) > 0.) cs%uhEff(i,j,k) = cs%uhEff(i,j,k) * pa_to_h
386 enddo ;
enddo ;
enddo
387 do k = 1, cs%nsurf-1 ;
do j = g%jsc-1, g%jec ;
do i = g%isc, g%iec
388 if (g%mask2dCv(i,j) > 0.) cs%vhEff(i,j,k) = cs%vhEff(i,j,k) * pa_to_h
389 enddo ;
enddo ;
enddo
392 if (cs%id_uhEff_2d>0)
then
394 do k = 1,cs%nsurf-1 ;
do j=g%jsc,g%jec ;
do i=g%isc-1,g%iec
395 heff_sum(i,j) = heff_sum(i,j) + cs%uhEff(i,j,k)
396 enddo ;
enddo ;
enddo
397 call post_data(cs%id_uhEff_2d, heff_sum, cs%diag)
399 if (cs%id_vhEff_2d>0)
then
401 do k = 1,cs%nsurf-1 ;
do j=g%jsc-1,g%jec ;
do i=g%isc,g%iec
402 heff_sum(i,j) = heff_sum(i,j) + cs%vhEff(i,j,k)
403 enddo ;
enddo ;
enddo
404 call post_data(cs%id_vhEff_2d, heff_sum, cs%diag)
407 end subroutine neutral_diffusion_calc_coeffs
410 subroutine neutral_diffusion(G, GV, h, Coef_x, Coef_y, dt, Reg, CS)
413 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: h
414 real,
dimension(SZIB_(G),SZJ_(G)),
intent(in) :: coef_x
415 real,
dimension(SZI_(G),SZJB_(G)),
intent(in) :: coef_y
416 real,
intent(in) :: dt
422 real,
dimension(SZIB_(G),SZJ_(G),CS%nsurf-1) :: uflx
423 real,
dimension(SZI_(G),SZJB_(G),CS%nsurf-1) :: vflx
425 real,
dimension(SZI_(G),SZJ_(G),G%ke) :: tendency
426 real,
dimension(SZI_(G),SZJ_(G)) :: tendency_2d
427 real,
dimension(SZIB_(G),SZJ_(G)) :: trans_x_2d
428 real,
dimension(SZI_(G),SZJB_(G)) :: trans_y_2d
429 real,
dimension(G%ke) :: dtracer
433 integer :: i, j, k, m, ks, nk
435 real :: h_neglect, h_neglect_edge
438 h_neglect_edge = gv%m_to_H*1.0e-10
439 h_neglect = gv%m_to_H*1.0e-30
448 if (tracer%id_dfxy_conc > 0 .or. tracer%id_dfxy_cont > 0 .or. tracer%id_dfxy_cont_2d > 0 .or. &
449 tracer%id_dfx_2d > 0 .or. tracer%id_dfy_2d > 0)
then
451 tendency(:,:,:) = 0.0
458 do j = g%jsc,g%jec ;
do i = g%isc-1,g%iec
459 if (g%mask2dCu(i,j)>0.)
then
460 call neutral_surface_flux(nk, cs%nsurf, cs%deg, h(i,j,:), h(i+1,j,:), &
461 tracer%t(i,j,:), tracer%t(i+1,j,:), &
462 cs%uPoL(i,j,:), cs%uPoR(i,j,:), &
463 cs%uKoL(i,j,:), cs%uKoR(i,j,:), &
464 cs%uhEff(i,j,:), uflx(i,j,:), &
465 cs%continuous_reconstruction, h_neglect, cs%remap_CS, h_neglect_edge)
470 do j = g%jsc-1,g%jec ;
do i = g%isc,g%iec
471 if (g%mask2dCv(i,j)>0.)
then
472 call neutral_surface_flux(nk, cs%nsurf, cs%deg, h(i,j,:), h(i,j+1,:), &
473 tracer%t(i,j,:), tracer%t(i,j+1,:), &
474 cs%vPoL(i,j,:), cs%vPoR(i,j,:), &
475 cs%vKoL(i,j,:), cs%vKoR(i,j,:), &
476 cs%vhEff(i,j,:), vflx(i,j,:), &
477 cs%continuous_reconstruction, h_neglect, cs%remap_CS, h_neglect_edge)
482 do j = g%jsc,g%jec ;
do i = g%isc,g%iec
483 if (g%mask2dT(i,j)>0.)
then
488 dtracer(k) = dtracer(k) + coef_x(i,j) * uflx(i,j,ks)
489 k = cs%uKoR(i-1,j,ks)
490 dtracer(k) = dtracer(k) - coef_x(i-1,j) * uflx(i-1,j,ks)
492 dtracer(k) = dtracer(k) + coef_y(i,j) * vflx(i,j,ks)
493 k = cs%vKoR(i,j-1,ks)
494 dtracer(k) = dtracer(k) - coef_y(i,j-1) * vflx(i,j-1,ks)
497 tracer%t(i,j,k) = tracer%t(i,j,k) + dtracer(k) * &
498 ( g%IareaT(i,j) / ( h(i,j,k) + gv%H_subroundoff ) )
501 if (tracer%id_dfxy_conc > 0 .or. tracer%id_dfxy_cont > 0 .or. tracer%id_dfxy_cont_2d > 0 )
then
503 tendency(i,j,k) = dtracer(k) * g%IareaT(i,j) * idt
512 if (tracer%id_dfx_2d > 0)
then
513 do j = g%jsc,g%jec ;
do i = g%isc-1,g%iec
515 if (g%mask2dCu(i,j)>0.)
then
517 trans_x_2d(i,j) = trans_x_2d(i,j) - coef_x(i,j) * uflx(i,j,ks)
519 trans_x_2d(i,j) = trans_x_2d(i,j) * idt
522 call post_data(tracer%id_dfx_2d, trans_x_2d(:,:), cs%diag)
527 if (tracer%id_dfy_2d > 0)
then
528 do j = g%jsc-1,g%jec ;
do i = g%isc,g%iec
530 if (g%mask2dCv(i,j)>0.)
then
532 trans_y_2d(i,j) = trans_y_2d(i,j) - coef_y(i,j) * vflx(i,j,ks)
534 trans_y_2d(i,j) = trans_y_2d(i,j) * idt
537 call post_data(tracer%id_dfy_2d, trans_y_2d(:,:), cs%diag)
541 if (tracer%id_dfxy_cont > 0)
then
542 call post_data(tracer%id_dfxy_cont, tendency(:,:,:), cs%diag)
546 if (tracer%id_dfxy_cont_2d > 0)
then
547 tendency_2d(:,:) = 0.
548 do j = g%jsc,g%jec ;
do i = g%isc,g%iec
550 tendency_2d(i,j) = tendency_2d(i,j) + tendency(i,j,k)
553 call post_data(tracer%id_dfxy_cont_2d, tendency_2d(:,:), cs%diag)
559 if (tracer%id_dfxy_conc > 0)
then
560 do k = 1, gv%ke ;
do j = g%jsc,g%jec ;
do i = g%isc,g%iec
561 tendency(i,j,k) = tendency(i,j,k) / ( h(i,j,k) + gv%H_subroundoff )
562 enddo ;
enddo ;
enddo
563 call post_data(tracer%id_dfxy_conc, tendency, cs%diag)
567 end subroutine neutral_diffusion
570 subroutine interface_scalar(nk, h, S, Si, i_method, h_neglect)
571 integer,
intent(in) :: nk
572 real,
dimension(nk),
intent(in) :: h
573 real,
dimension(nk),
intent(in) :: S
574 real,
dimension(nk+1),
intent(inout) :: Si
575 integer,
intent(in) :: i_method
577 real,
intent(in) :: h_neglect
579 integer :: k, km2, kp1
580 real,
dimension(nk) :: diff
583 call plm_diff(nk, h, s, 2, 1, diff)
584 si(1) = s(1) - 0.5 * diff(1)
585 if (i_method==1)
then
589 sa = s(k-1) + 0.5 * diff(k-1)
590 sb = s(k) - 0.5 * diff(k)
591 si(k) = 0.5 * ( sa + sb )
593 elseif (i_method==2)
then
599 si(k) = ppm_edge(h(km2), h(k-1), h(k), h(kp1), s(k-1), s(k), diff(k-1), diff(k), h_neglect)
602 si(nk+1) = s(nk) + 0.5 * diff(nk)
604 end subroutine interface_scalar
608 real function ppm_edge(hkm1, hk, hkp1, hkp2, Ak, Akp1, Pk, Pkp1, h_neglect)
609 real,
intent(in) :: hkm1
610 real,
intent(in) :: hk
611 real,
intent(in) :: hkp1
612 real,
intent(in) :: hkp2
613 real,
intent(in) :: ak
614 real,
intent(in) :: akp1
615 real,
intent(in) :: pk
616 real,
intent(in) :: pkp1
617 real,
intent(in) :: h_neglect
620 real :: r_hk_hkp1, r_2hk_hkp1, r_hk_2hkp1, f1, f2, f3, f4
622 r_hk_hkp1 = hk + hkp1
623 if (r_hk_hkp1 <= 0.)
then
624 ppm_edge = 0.5 * ( ak + akp1 )
627 r_hk_hkp1 = 1. / r_hk_hkp1
629 ppm_edge = ak + ( hk * r_hk_hkp1 ) * ( akp1 - ak )
631 ppm_edge = akp1 + ( hkp1 * r_hk_hkp1 ) * ( ak - akp1 )
634 r_2hk_hkp1 = 1. / ( ( 2. * hk + hkp1 ) + h_neglect )
635 r_hk_2hkp1 = 1. / ( ( hk + 2. * hkp1 ) + h_neglect )
636 f1 = 1./ ( ( hk + hkp1) + ( hkm1 + hkp2 ) )
637 f2 = 2. * ( hkp1 * hk ) * r_hk_hkp1 * &
638 ( ( hkm1 + hk ) * r_2hk_hkp1 - ( hkp2 + hkp1 ) * r_hk_2hkp1 )
639 f3 = hk * ( hkm1 + hk ) * r_2hk_hkp1
640 f4 = hkp1 * ( hkp1 + hkp2 ) * r_hk_2hkp1
642 ppm_edge = ppm_edge + f1 * ( f2 * ( akp1 - ak ) - ( f3 * pkp1 - f4 * pk ) )
644 end function ppm_edge
648 real function ppm_ave(xL, xR, aL, aR, aMean)
649 real,
intent(in) :: xl
650 real,
intent(in) :: xr
651 real,
intent(in) :: al
652 real,
intent(in) :: ar
653 real,
intent(in) :: amean
656 real :: dx, xave, a6, a6o3
659 xave = 0.5 * ( xr + xl )
660 a6o3 = 2. * amean - ( al + ar )
664 stop
'ppm_ave: dx<0 should not happend!'
666 stop
'ppm_ave: dx>1 should not happend!'
668 ppm_ave = al + ( ar - al ) * xr + a6 * xr * ( 1. - xr )
670 ppm_ave = ( al + xave * ( ( ar - al ) + a6 ) ) - a6o3 * ( xr**2 + xr * xl + xl**2 )
675 real function signum(a,x)
676 real,
intent(in) :: a
677 real,
intent(in) :: x
680 if (x==0.) signum = 0.
686 subroutine plm_diff(nk, h, S, c_method, b_method, diff)
687 integer,
intent(in) :: nk
688 real,
dimension(nk),
intent(in) :: h
689 real,
dimension(nk),
intent(in) :: S
690 integer,
intent(in) :: c_method
691 integer,
intent(in) :: b_method
692 real,
dimension(nk),
intent(inout) :: diff
702 real :: hkm1, hk, hkp1, Skm1, Sk, Skp1, diff_l, diff_r, diff_c
709 if ( ( hkp1 + hk ) * ( hkm1 + hk ) > 0.)
then
713 if (c_method==1)
then
715 if ( hk + 0.5 * (hkm1 + hkp1) /= 0. )
then
716 diff_c = ( skp1 - skm1 ) * ( hk / ( hk + 0.5 * (hkm1 + hkp1) ) )
720 elseif (c_method==2)
then
722 diff_c = fv_diff(hkm1, hk, hkp1, skm1, sk, skp1)
723 elseif (c_method==3)
then
725 diff_c = hk * fvlsq_slope(hkm1, hk, hkp1, skm1, sk, skp1)
728 diff_l = 2. * ( sk - skm1 )
729 diff_r = 2. * ( skp1 - sk )
730 if ( signum(1., diff_l) * signum(1., diff_r) <= 0. )
then
733 diff(k) = sign( min( abs(diff_l), abs(diff_c), abs(diff_r) ), diff_c )
739 if (b_method==1)
then
742 elseif (b_method==2)
then
743 diff(1) = ( s(2) - s(1) ) * 2. * ( h(1) / ( h(1) + h(2) ) )
744 diff(nk) = s(nk) - s(nk-1) * 2. * ( h(nk) / ( h(nk-1) + h(nk) ) )
747 end subroutine plm_diff
753 real function fv_diff(hkm1, hk, hkp1, Skm1, Sk, Skp1)
754 real,
intent(in) :: hkm1
755 real,
intent(in) :: hk
756 real,
intent(in) :: hkp1
757 real,
intent(in) :: skm1
758 real,
intent(in) :: sk
759 real,
intent(in) :: skp1
762 real :: h_sum, hp, hm
764 h_sum = ( hkm1 + hkp1 ) + hk
765 if (h_sum /= 0.) h_sum = 1./ h_sum
767 if (hm /= 0.) hm = 1./ hm
769 if (hp /= 0.) hp = 1./ hp
770 fv_diff = ( hk * h_sum ) * &
771 ( ( 2. * hkm1 + hk ) * hp * ( skp1 - sk ) &
772 + ( 2. * hkp1 + hk ) * hm * ( sk - skm1 ) )
779 real function fvlsq_slope(hkm1, hk, hkp1, Skm1, Sk, Skp1)
780 real,
intent(in) :: hkm1
781 real,
intent(in) :: hk
782 real,
intent(in) :: hkp1
783 real,
intent(in) :: skm1
784 real,
intent(in) :: sk
785 real,
intent(in) :: skp1
789 real :: h_sum, hx_sum, hxsq_sum, hxy_sum, hy_sum, det
791 xkm1 = -0.5 * ( hk + hkm1 )
792 xkp1 = 0.5 * ( hk + hkp1 )
793 h_sum = ( hkm1 + hkp1 ) + hk
794 hx_sum = hkm1*xkm1 + hkp1*xkp1
795 hxsq_sum = hkm1*(xkm1**2) + hkp1*(xkp1**2)
796 hxy_sum = hkm1*xkm1*skm1 + hkp1*xkp1*skp1
797 hy_sum = ( hkm1*skm1 + hkp1*skp1 ) + hk*sk
798 det = h_sum * hxsq_sum - hx_sum**2
801 fvlsq_slope = ( h_sum * hxy_sum - hx_sum*hy_sum ) / det
805 end function fvlsq_slope
809 subroutine find_neutral_surface_positions_continuous(nk, Pl, Tl, Sl, dRdTl, dRdSl, Pr, Tr, Sr, &
810 dRdTr, dRdSr, PoL, PoR, KoL, KoR, hEff)
811 integer,
intent(in) :: nk
812 real,
dimension(nk+1),
intent(in) :: Pl
813 real,
dimension(nk+1),
intent(in) :: Tl
814 real,
dimension(nk+1),
intent(in) :: Sl
815 real,
dimension(nk+1),
intent(in) :: dRdTl
816 real,
dimension(nk+1),
intent(in) :: dRdSl
817 real,
dimension(nk+1),
intent(in) :: Pr
818 real,
dimension(nk+1),
intent(in) :: Tr
819 real,
dimension(nk+1),
intent(in) :: Sr
820 real,
dimension(nk+1),
intent(in) :: dRdTr
821 real,
dimension(nk+1),
intent(in) :: dRdSr
822 real,
dimension(2*nk+2),
intent(inout) :: PoL
824 real,
dimension(2*nk+2),
intent(inout) :: PoR
826 integer,
dimension(2*nk+2),
intent(inout) :: KoL
827 integer,
dimension(2*nk+2),
intent(inout) :: KoR
828 real,
dimension(2*nk+1),
intent(inout) :: hEff
836 logical :: searching_left_column
837 logical :: searching_right_column
838 logical :: reached_bottom
839 integer :: krm1, klm1
840 real :: dRho, dRhoTop, dRhoBot, hL, hR
841 integer :: lastK_left, lastK_right
842 real :: lastP_left, lastP_right
846 kr = 1 ; lastk_right = 1 ; lastp_right = 0.
847 kl = 1 ; lastk_left = 1 ; lastp_left = 0.
848 reached_bottom = .false.
851 neutral_surfaces:
do k_surface = 1, ns
853 if (klm1>nk) stop
'find_neutral_surface_positions(): klm1 went out of bounds!'
855 if (krm1>nk) stop
'find_neutral_surface_positions(): krm1 went out of bounds!'
858 drho = 0.5 * ( ( drdtr(kr) + drdtl(kl) ) * ( tr(kr) - tl(kl) ) &
859 + ( drdsr(kr) + drdsl(kl) ) * ( sr(kr) - sl(kl) ) )
861 if (.not. reached_bottom)
then
863 searching_left_column = .true.
864 searching_right_column = .false.
865 elseif (drho > 0.)
then
866 searching_right_column = .true.
867 searching_left_column = .false.
869 if (kl + kr == 2)
then
870 searching_left_column = .true.
871 searching_right_column = .false.
873 searching_left_column = .not. searching_left_column
874 searching_right_column = .not. searching_right_column
879 if (searching_left_column)
then
882 drhotop = 0.5 * ( ( drdtl(klm1) + drdtr(kr) ) * ( tl(klm1) - tr(kr) ) &
883 + ( drdsl(klm1) + drdsr(kr) ) * ( sl(klm1) - sr(kr) ) )
885 drhobot = 0.5 * ( ( drdtl(klm1+1) + drdtr(kr) ) * ( tl(klm1+1) - tr(kr) ) &
886 + ( drdsl(klm1+1) + drdsr(kr) ) * ( sl(klm1+1) - sr(kr) ) )
890 if (drhotop > 0. .or. kr+kl==2)
then
892 elseif (drhotop >= drhobot)
then
897 pol(k_surface) = interpolate_for_nondim_position( drhotop, pl(klm1), drhobot, pl(klm1+1) )
899 if (pol(k_surface)>=1. .and. klm1<nk)
then
901 pol(k_surface) = pol(k_surface) - 1.
903 if (real(klm1-lastk_left)+(pol(k_surface)-lastp_left)<0.)
then
904 pol(k_surface) = lastp_left
907 kol(k_surface) = klm1
918 reached_bottom = .true.
919 searching_right_column = .true.
920 searching_left_column = .false.
922 elseif (searching_right_column)
then
925 drhotop = 0.5 * ( ( drdtr(krm1) + drdtl(kl) ) * ( tr(krm1) - tl(kl) ) &
926 + ( drdsr(krm1) + drdsl(kl) ) * ( sr(krm1) - sl(kl) ) )
928 drhobot = 0.5 * ( ( drdtr(krm1+1) + drdtl(kl) ) * ( tr(krm1+1) - tl(kl) ) &
929 + ( drdsr(krm1+1) + drdsl(kl) ) * ( sr(krm1+1) - sl(kl) ) )
933 if (drhotop >= 0. .or. kr+kl==2)
then
935 elseif (drhotop >= drhobot)
then
940 por(k_surface) = interpolate_for_nondim_position( drhotop, pr(krm1), drhobot, pr(krm1+1) )
942 if (por(k_surface)>=1. .and. krm1<nk)
then
944 por(k_surface) = por(k_surface) - 1.
946 if (real(krm1-lastk_right)+(por(k_surface)-lastp_right)<0.)
then
947 por(k_surface) = lastp_right
950 kor(k_surface) = krm1
961 reached_bottom = .true.
962 searching_right_column = .false.
963 searching_left_column = .true.
969 lastk_left = kol(k_surface) ; lastp_left = pol(k_surface)
970 lastk_right = kor(k_surface) ; lastp_right = por(k_surface)
975 if (k_surface>1)
then
976 hl = absolute_position(nk,ns,pl,kol,pol,k_surface) - absolute_position(nk,ns,pl,kol,pol,k_surface-1)
977 hr = absolute_position(nk,ns,pr,kor,por,k_surface) - absolute_position(nk,ns,pr,kor,por,k_surface-1)
978 if ( hl + hr > 0.)
then
979 heff(k_surface-1) = 2. * hl * hr / ( hl + hr )
981 heff(k_surface-1) = 0.
985 enddo neutral_surfaces
987 end subroutine find_neutral_surface_positions_continuous
991 subroutine find_neutral_surface_positions_discontinuous(CS, nk, ns, Pres_l, hcol_l, Tl, Sl, &
992 dRdT_l, dRdS_l, stable_l, Pres_r, hcol_r, Tr, Sr, dRdT_r, dRdS_r, stable_r, &
993 PoL, PoR, KoL, KoR, hEff, ppoly_T_l, ppoly_S_l, ppoly_T_r, ppoly_S_r)
995 integer,
intent(in) :: nk
996 integer,
intent(in) :: ns
997 real,
dimension(nk+1),
intent(in) :: Pres_l
998 real,
dimension(nk),
intent(in) :: hcol_l
999 real,
dimension(nk,2),
intent(in) :: Tl
1000 real,
dimension(nk,2),
intent(in) :: Sl
1001 real,
dimension(nk,2),
intent(in) :: dRdT_l
1002 real,
dimension(nk,2),
intent(in) :: dRdS_l
1003 logical,
dimension(nk),
intent(in) :: stable_l
1004 real,
dimension(nk+1),
intent(in) :: Pres_r
1005 real,
dimension(nk),
intent(in) :: hcol_r
1006 real,
dimension(nk,2),
intent(in) :: Tr
1007 real,
dimension(nk,2),
intent(in) :: Sr
1008 real,
dimension(nk,2),
intent(in) :: dRdT_r
1009 real,
dimension(nk,2),
intent(in) :: dRdS_r
1010 logical,
dimension(nk),
intent(in) :: stable_r
1011 real,
dimension(4*nk),
intent(inout) :: PoL
1013 real,
dimension(4*nk),
intent(inout) :: PoR
1015 integer,
dimension(4*nk),
intent(inout) :: KoL
1016 integer,
dimension(4*nk),
intent(inout) :: KoR
1017 real,
dimension(4*nk-1),
intent(inout) :: hEff
1018 real,
dimension(nk,CS%deg+1), &
1019 optional,
intent(in) :: ppoly_T_l
1020 real,
dimension(nk,CS%deg+1), &
1021 optional,
intent(in) :: ppoly_S_l
1022 real,
dimension(nk,CS%deg+1), &
1023 optional,
intent(in) :: ppoly_T_r
1024 real,
dimension(nk,CS%deg+1), &
1025 optional,
intent(in) :: ppoly_S_r
1028 integer :: k_surface
1029 integer :: kl_left, kl_right
1030 integer :: ki_left, ki_right
1031 logical :: searching_left_column
1032 logical :: searching_right_column
1033 logical :: reached_bottom
1034 logical :: search_layer
1035 integer :: k, kl_left_0, kl_right_0
1036 real :: dRho, dRhoTop, dRhoBot, hL, hR
1037 integer :: lastK_left, lastK_right
1038 real :: lastP_left, lastP_right
1040 real :: T_other, S_other, P_other, dRdT_other, dRdS_other
1041 logical,
dimension(nk) :: top_connected_l, top_connected_r
1042 logical,
dimension(nk) :: bot_connected_l, bot_connected_r
1044 top_connected_l(:) = .false. ; top_connected_r(:) = .false.
1045 bot_connected_l(:) = .false. ; bot_connected_r(:) = .false.
1048 if (cs%refine_position)
then
1049 if (.not. (
present(ppoly_t_l) .and.
present(ppoly_s_l) .and. &
1050 present(ppoly_t_r) .and.
present(ppoly_s_r) ) ) &
1051 call mom_error(fatal,
"fine_neutral_surface_positions_discontinuous: refine_pos is requested, but " //&
1052 "polynomial coefficients not available for T and S")
1055 if (stable_l(k))
then
1062 if (stable_r(k))
then
1070 ki_right = 1 ; lastk_right = 1 ; lastp_right = -1.
1071 ki_left = 1 ; lastk_left = 1 ; lastp_left = -1.
1073 reached_bottom = .false.
1074 searching_left_column = .false.
1075 searching_right_column = .false.
1078 neutral_surfaces:
do k_surface = 1, ns
1080 drho = 0.5 * ( ( drdt_r(kl_right,ki_right) + drdt_l(kl_left,ki_left) ) * &
1081 ( tr(kl_right,ki_right) - tl(kl_left,ki_left) ) &
1082 + ( drds_r(kl_right,ki_right) + drds_l(kl_left,ki_left) ) * &
1083 ( sr(kl_right,ki_right) - sl(kl_left,ki_left) ) )
1084 if (cs%debug)
write(*,
'(A,I2,A,E12.4,A,I2,A,I2,A,I2,A,I2)')
"k_surface=",k_surface,
" dRho=",drho, &
1085 "kl_left=",kl_left,
" ki_left=",ki_left,
" kl_right=",kl_right,
" ki_right=",ki_right
1087 if (.not. reached_bottom)
then
1089 searching_left_column = .true.
1090 searching_right_column = .false.
1091 elseif (drho > 0.)
then
1092 searching_right_column = .true.
1093 searching_left_column = .false.
1095 if ( ( kl_left == kl_left_0) .and. ( kl_right == kl_right_0 ) .and. &
1096 (ki_left + ki_right == 2) )
then
1097 searching_left_column = .true.
1098 searching_right_column = .false.
1100 searching_left_column = .not. searching_left_column
1101 searching_right_column = .not. searching_right_column
1106 if (searching_left_column)
then
1108 if (cs%ref_pres>=0.)
then
1109 p_other = cs%ref_pres
1111 if (ki_right == 1) p_other = pres_r(kl_right)
1112 if (ki_right == 2) p_other = pres_r(kl_right+1)
1114 t_other = tr(kl_right, ki_right)
1115 s_other = sr(kl_right, ki_right)
1116 drdt_other = drdt_r(kl_right, ki_right)
1117 drds_other = drds_r(kl_right, ki_right)
1118 if (cs%refine_position .and. (lastk_left == kl_left))
then
1119 call drho_at_pos(cs%ndiff_aux_CS, t_other, s_other, drdt_other, drds_other, pres_l(kl_left), &
1120 pres_l(kl_left+1), ppoly_t_l(kl_left,:), ppoly_s_l(kl_left,:), lastp_left, drhotop)
1122 drhotop = calc_drho(tl(kl_left,1), sl(kl_left,1), drdt_l(kl_left,1), drds_l(kl_left,1), t_other, s_other, &
1123 drdt_other, drds_other)
1126 drhobot = calc_drho(tl(kl_left,2), sl(kl_left,2), drdt_l(kl_left,2), drds_l(kl_left,2), &
1127 t_other, s_other, drdt_other, drds_other)
1129 write(*,
'(A,I2,A,E12.4,A,E12.4,A,E12.4)')
"Searching left layer ", kl_left, &
1130 " dRhoTop=", drhotop,
" dRhoBot=", drhobot
1131 write(*,
'(A,I2,X,I2)')
"Searching from right: ", kl_right, ki_right
1132 write(*,*)
"Temp/Salt Reference: ", t_other, s_other
1133 write(*,*)
"Temp/Salt Top L: ", tl(kl_left,1), sl(kl_left,1)
1134 write(*,*)
"Temp/Salt Bot L: ", tl(kl_left,2), sl(kl_left,2)
1138 por(k_surface) = real(ki_right-1)
1139 kor(k_surface) = kl_right
1142 call search_other_column(drhotop, drhobot, pres_l(kl_left), pres_l(kl_left+1), &
1143 lastp_left, lastk_left, kl_left, kl_left_0, ki_left, &
1144 top_connected_l, bot_connected_l, pol(k_surface), kol(k_surface), search_layer)
1146 if ( cs%refine_position .and. search_layer )
then
1148 if (k_surface > 1)
then
1149 if ( kol(k_surface) == kol(k_surface-1) ) min_bound = pol(k_surface-1)
1151 pol(k_surface) = refine_nondim_position( cs%ndiff_aux_CS, t_other, s_other, drdt_other, drds_other, &
1152 pres_l(kl_left), pres_l(kl_left+1), ppoly_t_l(kl_left,:), ppoly_s_l(kl_left,:), &
1153 drhotop, drhobot, min_bound )
1155 if (pol(k_surface) == 0.) top_connected_l(kol(k_surface)) = .true.
1156 if (pol(k_surface) == 1.) bot_connected_l(kol(k_surface)) = .true.
1157 call increment_interface(nk, kl_right, ki_right, stable_r, reached_bottom, &
1158 searching_right_column, searching_left_column)
1160 elseif (searching_right_column)
then
1161 if (cs%ref_pres>=0.)
then
1162 p_other = cs%ref_pres
1164 if (ki_left == 1) p_other = pres_l(kl_left)
1165 if (ki_left == 2) p_other = pres_l(kl_left+1)
1167 t_other = tl(kl_left, ki_left)
1168 s_other = sl(kl_left, ki_left)
1169 drdt_other = drdt_l(kl_left, ki_left)
1170 drds_other = drds_l(kl_left, ki_left)
1174 if (cs%refine_position .and. (lastk_right == kl_right))
then
1175 call drho_at_pos(cs%ndiff_aux_CS, t_other, s_other, drdt_other, drds_other, pres_r(kl_right), &
1176 pres_l(kl_right+1), ppoly_t_r(kl_right,:), ppoly_s_r(kl_right,:), lastp_right, drhotop)
1178 drhotop = calc_drho(tr(kl_right,1), sr(kl_right,1), drdt_r(kl_right,1), drds_r(kl_right,1), &
1179 t_other, s_other, drdt_other, drds_other)
1181 drhobot = calc_drho(tr(kl_right,2), sr(kl_right,2), drdt_r(kl_right,2), drds_r(kl_right,2), &
1182 t_other, s_other, drdt_other, drds_other)
1184 write(*,
'(A,I2,A,E12.4,A,E12.4,A,E12.4)')
"Searching right layer ", kl_right, &
1185 " dRhoTop=", drhotop,
" dRhoBot=", drhobot
1186 write(*,
'(A,I2,X,I2)')
"Searching from left: ", kl_left, ki_left
1187 write(*,*)
"Temp/Salt Reference: ", t_other, s_other
1188 write(*,*)
"Temp/Salt Top R: ", tr(kl_right,1), sr(kl_right,1)
1189 write(*,*)
"Temp/Salt Bot R: ", tr(kl_right,2), sr(kl_right,2)
1192 pol(k_surface) = real(ki_left-1)
1193 kol(k_surface) = kl_left
1196 call search_other_column(drhotop, drhobot, pres_r(kl_right), pres_r(kl_right+1), lastp_right, lastk_right, &
1197 kl_right, kl_right_0, ki_right, top_connected_r, bot_connected_r, por(k_surface), &
1198 kor(k_surface), search_layer)
1199 if ( cs%refine_position .and. search_layer)
then
1201 if (k_surface > 1)
then
1202 if ( kor(k_surface) == kor(k_surface-1) ) min_bound = por(k_surface-1)
1204 por(k_surface) = refine_nondim_position(cs%ndiff_aux_CS, t_other, s_other, drdt_other, drds_other, &
1205 pres_r(kl_right), pres_r(kl_right+1), ppoly_t_r(kl_right,:), ppoly_s_r(kl_right,:), &
1206 drhotop, drhobot, min_bound )
1208 if (por(k_surface) == 0.) top_connected_r(kor(k_surface)) = .true.
1209 if (por(k_surface) == 1.) bot_connected_r(kor(k_surface)) = .true.
1210 call increment_interface(nk, kl_left, ki_left, stable_l, reached_bottom, &
1211 searching_left_column, searching_right_column)
1216 lastk_left = kol(k_surface) ; lastp_left = pol(k_surface)
1217 lastk_right = kor(k_surface) ; lastp_right = por(k_surface)
1219 if (cs%debug)
write(*,
'(A,I3,A,ES16.6,A,I2,A,ES16.6)')
"KoL:", kol(k_surface),
" PoL:", pol(k_surface), &
1220 " KoR:", kor(k_surface),
" PoR:", por(k_surface)
1222 if (k_surface>1)
then
1224 hl = absolute_position(nk,ns,pres_l,kol,pol,k_surface) - absolute_position(nk,ns,pres_l,kol,pol,k_surface-1)
1225 hr = absolute_position(nk,ns,pres_r,kor,por,k_surface) - absolute_position(nk,ns,pres_r,kor,por,k_surface-1)
1228 if ( hl<0. .or. hr<0. )
then
1229 heff(k_surface-1) = 0.
1230 call mom_error(warning,
"hL or hR is negative")
1231 elseif ( hl > 0. .and. hr > 0.)
then
1232 hl = (pol(k_surface) - pol(k_surface-1))*hcol_l(kol(k_surface))
1233 hr = (por(k_surface) - por(k_surface-1))*hcol_r(kor(k_surface))
1234 heff(k_surface-1) = 2. * ( (hl * hr) / ( hl + hr ) )
1236 heff(k_surface-1) = 0.
1239 enddo neutral_surfaces
1241 write (*,*)
"==========Start Neutral Surfaces=========="
1243 if (heff(k)>0.)
then
1246 write (*,
'(A,I3,X,ES16.6,X,I3,X,ES16.6)')
"Top surface KoL, PoL, KoR, PoR: ", kl_left, pol(k), kl_right, por(k)
1247 call check_neutral_positions(cs%ndiff_aux_CS, pres_l(kl_left), pres_l(kl_left+1), pres_r(kl_right), &
1248 pres_r(kl_right+1), pol(k), por(k), ppoly_t_l(kl_left,:), ppoly_t_r(kl_right,:), &
1249 ppoly_s_l(kl_left,:), ppoly_s_r(kl_right,:))
1252 write (*,
'(A,I3,X,ES16.6,X,I3,X,ES16.6)')
"Bot surface KoL, PoL, KoR, PoR: ", kl_left, pol(k+1), kl_right, por(k)
1253 call check_neutral_positions(cs%ndiff_aux_CS, pres_l(kl_left), pres_l(kl_left+1), pres_r(kl_right), &
1254 pres_r(kl_right+1), pol(k), por(k), ppoly_t_l(kl_left,:), ppoly_t_r(kl_right,:), &
1255 ppoly_s_l(kl_left,:), ppoly_s_r(kl_right,:))
1258 write(*,
'(A,E16.6)')
"Total thickness of sublayers: ", sum(heff)
1259 write(*,*)
"==========End Neutral Surfaces=========="
1262 end subroutine find_neutral_surface_positions_discontinuous
1265 real function absolute_position(n,ns,Pint,Karr,NParr,k_surface)
1266 integer,
intent(in) :: n
1267 integer,
intent(in) :: ns
1268 real,
intent(in) :: pint(n+1)
1269 integer,
intent(in) :: karr(ns)
1270 real,
intent(in) :: nparr(ns)
1271 integer,
intent(in) :: k_surface
1276 if (k>n) stop
'absolute_position: k>nk is out of bounds!'
1277 absolute_position = pint(k) + nparr(k_surface) * ( pint(k+1) - pint(k) )
1279 end function absolute_position
1282 function absolute_positions(n,ns,Pint,Karr,NParr)
1283 integer,
intent(in) :: n
1284 integer,
intent(in) :: ns
1285 real,
intent(in) :: pint(n+1)
1286 integer,
intent(in) :: karr(ns)
1287 real,
intent(in) :: nparr(ns)
1289 real,
dimension(ns) :: absolute_positions
1292 integer :: k_surface, k
1294 do k_surface = 1, ns
1295 absolute_positions(k_surface) = absolute_position(n,ns,pint,karr,nparr,k_surface)
1298 end function absolute_positions
1301 subroutine neutral_surface_flux(nk, nsurf, deg, hl, hr, Tl, Tr, PiL, PiR, KoL, KoR, &
1302 hEff, Flx, continuous, h_neglect, remap_CS, h_neglect_edge)
1303 integer,
intent(in) :: nk
1304 integer,
intent(in) :: nsurf
1305 integer,
intent(in) :: deg
1306 real,
dimension(nk),
intent(in) :: hl
1307 real,
dimension(nk),
intent(in) :: hr
1308 real,
dimension(nk),
intent(in) :: Tl
1309 real,
dimension(nk),
intent(in) :: Tr
1310 real,
dimension(nsurf),
intent(in) :: PiL
1312 real,
dimension(nsurf),
intent(in) :: PiR
1314 integer,
dimension(nsurf),
intent(in) :: KoL
1315 integer,
dimension(nsurf),
intent(in) :: KoR
1316 real,
dimension(nsurf-1),
intent(in) :: hEff
1317 real,
dimension(nsurf-1),
intent(inout) :: Flx
1318 logical,
intent(in) :: continuous
1319 real,
intent(in) :: h_neglect
1324 real,
optional,
intent(in) :: h_neglect_edge
1328 integer :: k_sublayer, klb, klt, krb, krt, k
1329 real :: T_right_top, T_right_bottom, T_right_layer, T_right_sub, T_right_top_int, T_right_bot_int
1330 real :: T_left_top, T_left_bottom, T_left_layer, T_left_sub, T_left_top_int, T_left_bot_int
1331 real :: dT_top, dT_bottom, dT_layer, dT_ave, dT_sublayer, dT_top_int, dT_bot_int
1332 real,
dimension(nk+1) :: Til
1333 real,
dimension(nk+1) :: Tir
1334 real,
dimension(nk) :: aL_l
1335 real,
dimension(nk) :: aR_l
1336 real,
dimension(nk) :: aL_r
1337 real,
dimension(nk) :: aR_r
1340 real,
dimension(nk,2) :: Tid_l
1341 real,
dimension(nk,2) :: Tid_r
1342 real,
dimension(nk,deg+1) :: ppoly_r_coeffs_l
1343 real,
dimension(nk,deg+1) :: ppoly_r_coeffs_r
1344 real,
dimension(nk,deg+1) :: ppoly_r_S_l
1345 real,
dimension(nk,deg+1) :: ppoly_r_S_r
1346 logical :: down_flux
1348 if (continuous)
then
1349 call interface_scalar(nk, hl, tl, til, 2, h_neglect)
1350 call interface_scalar(nk, hr, tr, tir, 2, h_neglect)
1351 call ppm_left_right_edge_values(nk, tl, til, al_l, ar_l)
1352 call ppm_left_right_edge_values(nk, tr, tir, al_r, ar_r)
1354 ppoly_r_coeffs_l(:,:) = 0.
1355 ppoly_r_coeffs_r(:,:) = 0.
1359 call build_reconstructions_1d( remap_cs, nk, hl, tl, ppoly_r_coeffs_l, tid_l, &
1360 ppoly_r_s_l, imethod, h_neglect, h_neglect_edge )
1361 call build_reconstructions_1d( remap_cs, nk, hr, tr, ppoly_r_coeffs_r, tid_r, &
1362 ppoly_r_s_r, imethod, h_neglect, h_neglect_edge )
1365 do k_sublayer = 1, nsurf-1
1366 if (heff(k_sublayer) == 0.)
then
1367 flx(k_sublayer) = 0.
1369 if (continuous)
then
1370 klb = kol(k_sublayer+1)
1371 t_left_bottom = ( 1. - pil(k_sublayer+1) ) * til(klb) + pil(k_sublayer+1) * til(klb+1)
1372 klt = kol(k_sublayer)
1373 t_left_top = ( 1. - pil(k_sublayer) ) * til(klt) + pil(k_sublayer) * til(klt+1)
1374 t_left_layer = ppm_ave(pil(k_sublayer), pil(k_sublayer+1) + real(klb-klt), &
1375 al_l(klt), ar_l(klt), tl(klt))
1377 krb = kor(k_sublayer+1)
1378 t_right_bottom = ( 1. - pir(k_sublayer+1) ) * tir(krb) + pir(k_sublayer+1) * tir(krb+1)
1379 krt = kor(k_sublayer)
1380 t_right_top = ( 1. - pir(k_sublayer) ) * tir(krt) + pir(k_sublayer) * tir(krt+1)
1381 t_right_layer = ppm_ave(pir(k_sublayer), pir(k_sublayer+1) + real(krb-krt), &
1382 al_r(krt), ar_r(krt), tr(krt))
1383 dt_top = t_right_top - t_left_top
1384 dt_bottom = t_right_bottom - t_left_bottom
1385 dt_ave = 0.5 * ( dt_top + dt_bottom )
1386 dt_layer = t_right_layer - t_left_layer
1387 if (signum(1.,dt_top) * signum(1.,dt_bottom) <= 0. .or. signum(1.,dt_ave) * signum(1.,dt_layer) <= 0.)
then
1392 flx(k_sublayer) = dt_ave * heff(k_sublayer)
1395 call neutral_surface_t_eval(nk, nsurf, k_sublayer, kol, pil, tl, tid_l, deg, imethod, &
1396 ppoly_r_coeffs_l, t_left_top, t_left_bottom, t_left_sub, &
1397 t_left_top_int, t_left_bot_int, t_left_layer)
1398 call neutral_surface_t_eval(nk, nsurf, k_sublayer, kor, pir, tr, tid_r, deg, imethod, &
1399 ppoly_r_coeffs_r, t_right_top, t_right_bottom, t_right_sub, &
1400 t_right_top_int, t_right_bot_int, t_right_layer)
1402 dt_top = t_right_top - t_left_top
1403 dt_bottom = t_right_bottom - t_left_bottom
1404 dt_sublayer = t_right_sub - t_left_sub
1405 dt_top_int = t_right_top_int - t_left_top_int
1406 dt_bot_int = t_right_bot_int - t_left_bot_int
1410 down_flux = dt_top <= 0. .and. dt_bottom <= 0. .and. &
1411 dt_sublayer <= 0. .and. dt_top_int <= 0. .and. &
1413 down_flux = down_flux .or. &
1414 (dt_top >= 0. .and. dt_bottom >= 0. .and. &
1415 dt_sublayer >= 0. .and. dt_top_int >= 0. .and. &
1418 flx(k_sublayer) = dt_sublayer * heff(k_sublayer)
1420 flx(k_sublayer) = 0.
1426 end subroutine neutral_surface_flux
1429 subroutine neutral_surface_t_eval(nk, ns, k_sub, Ks, Ps, T_mean, T_int, deg, iMethod, T_poly, &
1430 T_top, T_bot, T_sub, T_top_int, T_bot_int, T_layer)
1431 integer,
intent(in ) :: nk
1432 integer,
intent(in ) :: ns
1433 integer,
intent(in ) :: k_sub
1434 integer,
dimension(ns),
intent(in ) :: Ks
1435 real,
dimension(ns),
intent(in ) :: Ps
1436 real,
dimension(nk),
intent(in ) :: T_mean
1437 real,
dimension(nk,2),
intent(in ) :: T_int
1438 integer,
intent(in ) :: deg
1439 integer,
intent(in ) :: iMethod
1440 real,
dimension(nk,deg+1),
intent(in ) :: T_poly
1441 real,
intent( out) :: T_top
1442 real,
intent( out) :: T_bot
1443 real,
intent( out) :: T_sub
1444 real,
intent( out) :: T_top_int
1445 real,
intent( out) :: T_bot_int
1446 real,
intent( out) :: T_layer
1448 integer :: kl, ks_top, ks_bot
1452 if ( ks(ks_top) /= ks(ks_bot) )
then
1453 call mom_error(fatal,
"Neutral surfaces span more than one layer")
1457 if ( (ps(ks_top) == 0.) .and. (ps(ks_bot) == 1.))
then
1462 if ( (kl > 1) .and. (ps(ks_top) == 0.) )
then
1463 t_top = t_int(kl-1,2)
1465 t_top = evaluation_polynomial( t_poly(kl,:), deg+1, ps(ks_top) )
1468 if ( (kl < nk) .and. (ps(ks_bot) == 1.) )
then
1469 t_bot = t_int(kl+1,1)
1471 t_bot = evaluation_polynomial( t_poly(kl,:), deg+1, ps(ks_bot) )
1474 t_sub = average_value_ppoly(nk, t_mean, t_int, t_poly, imethod, kl, ps(ks_top), ps(ks_bot))
1475 t_top_int = evaluation_polynomial( t_poly(kl,:), deg+1, ps(ks_top))
1476 t_bot_int = evaluation_polynomial( t_poly(kl,:), deg+1, ps(ks_bot))
1477 t_layer = t_mean(kl)
1479 end subroutine neutral_surface_t_eval
1482 subroutine ppm_left_right_edge_values(nk, Tl, Ti, aL, aR)
1483 integer,
intent(in) :: nk
1484 real,
dimension(nk),
intent(in) :: Tl
1485 real,
dimension(nk+1),
intent(in) :: Ti
1486 real,
dimension(nk),
intent(inout) :: aL
1487 real,
dimension(nk),
intent(inout) :: aR
1494 if ( signum(1., ar(k) - tl(k))*signum(1., tl(k) - al(k)) <= 0.0 )
then
1497 elseif ( sign(3., ar(k) - al(k)) * ( (tl(k) - al(k)) + (tl(k) - ar(k))) > abs(ar(k) - al(k)) )
then
1498 al(k) = tl(k) + 2.0 * ( tl(k) - ar(k) )
1499 elseif ( sign(3., ar(k) - al(k)) * ( (tl(k) - al(k)) + (tl(k) - ar(k))) < -abs(ar(k) - al(k)) )
then
1500 ar(k) = tl(k) + 2.0 * ( tl(k) - al(k) )
1503 end subroutine ppm_left_right_edge_values
1506 logical function neutral_diffusion_unit_tests(verbose)
1507 logical,
intent(in) :: verbose
1509 neutral_diffusion_unit_tests = .false. .or. &
1510 ndiff_unit_tests_continuous(verbose) .or. ndiff_unit_tests_discontinuous(verbose)
1513 end function neutral_diffusion_unit_tests
1516 logical function ndiff_unit_tests_continuous(verbose)
1517 logical,
intent(in) :: verbose
1519 integer,
parameter :: nk = 4
1520 real,
dimension(nk+1) :: til, tir1, tir2, tir4, tio
1521 real,
dimension(nk) :: tl
1522 real,
dimension(nk+1) :: sil
1523 real,
dimension(nk+1) :: pil, pir4
1524 real,
dimension(2*nk+2) :: pilro, pirlo
1525 integer,
dimension(2*nk+2) :: kol, kor
1526 real,
dimension(2*nk+1) :: heff
1527 real,
dimension(2*nk+1) :: flx
1530 real :: h_neglect, h_neglect_edge
1532 h_neglect_edge = 1.0e-10 ; h_neglect = 1.0e-30
1536 ndiff_unit_tests_continuous = .false.
1537 write(*,*)
'==== MOM_neutral_diffusion: ndiff_unit_tests_continuous ='
1539 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1540 test_fv_diff(v,1.,1.,1., 0.,1.,2., 1.,
'FV: Straight line on uniform grid')
1541 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1542 test_fv_diff(v,1.,1.,0., 0.,4.,8., 7.,
'FV: Vanished right cell')
1543 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1544 test_fv_diff(v,0.,1.,1., 0.,4.,8., 7.,
'FV: Vanished left cell')
1545 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1546 test_fv_diff(v,1.,2.,4., 0.,3.,9., 4.,
'FV: Stretched grid')
1547 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1548 test_fv_diff(v,2.,0.,2., 0.,1.,2., 0.,
'FV: Vanished middle cell')
1549 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1550 test_fv_diff(v,0.,1.,0., 0.,1.,2., 2.,
'FV: Vanished on both sides')
1551 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1552 test_fv_diff(v,1.,0.,0., 0.,1.,2., 0.,
'FV: Two vanished cell sides')
1553 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1554 test_fv_diff(v,0.,0.,0., 0.,1.,2., 0.,
'FV: All vanished cells')
1556 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1557 test_fvlsq_slope(v,1.,1.,1., 0.,1.,2., 1.,
'LSQ: Straight line on uniform grid')
1558 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1559 test_fvlsq_slope(v,1.,1.,0., 0.,1.,2., 1.,
'LSQ: Vanished right cell')
1560 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1561 test_fvlsq_slope(v,0.,1.,1., 0.,1.,2., 1.,
'LSQ: Vanished left cell')
1562 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1563 test_fvlsq_slope(v,1.,2.,4., 0.,3.,9., 2.,
'LSQ: Stretched grid')
1564 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1565 test_fvlsq_slope(v,1.,0.,1., 0.,1.,2., 2.,
'LSQ: Vanished middle cell')
1566 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1567 test_fvlsq_slope(v,0.,1.,0., 0.,1.,2., 0.,
'LSQ: Vanished on both sides')
1568 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1569 test_fvlsq_slope(v,1.,0.,0., 0.,1.,2., 0.,
'LSQ: Two vanished cell sides')
1570 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1571 test_fvlsq_slope(v,0.,0.,0., 0.,1.,2., 0.,
'LSQ: All vanished cells')
1573 call interface_scalar(4, (/10.,10.,10.,10./), (/24.,18.,12.,6./), tio, 1, h_neglect)
1576 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1577 test_data1d(v,5, tio, (/24.,22.5,15.,7.5,6./),
'Linear profile, linear interface temperatures')
1578 call interface_scalar(4, (/10.,10.,10.,10./), (/24.,18.,12.,6./), tio, 2, h_neglect)
1579 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1580 test_data1d(v,5, tio, (/24.,22.,15.,8.,6./),
'Linear profile, PPM interface temperatures')
1582 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1583 test_ifndp(v,-1.0, 0., 1.0, 1.0, 0.5,
'Check mid-point')
1584 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1585 test_ifndp(v, 0.0, 0., 1.0, 1.0, 0.0,
'Check bottom')
1586 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1587 test_ifndp(v, 0.1, 0., 1.1, 1.0, 0.0,
'Check below')
1588 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1589 test_ifndp(v,-1.0, 0., 0.0, 1.0, 1.0,
'Check top')
1590 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1591 test_ifndp(v,-1.0, 0., -0.1, 1.0, 1.0,
'Check above')
1592 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1593 test_ifndp(v,-1.0, 0., 3.0, 1.0, 0.25,
'Check 1/4')
1594 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1595 test_ifndp(v,-3.0, 0., 1.0, 1.0, 0.75,
'Check 3/4')
1596 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1597 test_ifndp(v, 1.0, 0., 1.0, 1.0, 0.0,
'Check dRho=0 below')
1598 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1599 test_ifndp(v,-1.0, 0., -1.0, 1.0, 1.0,
'Check dRho=0 above')
1600 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1601 test_ifndp(v, 0.0, 0., 0.0, 1.0, 0.5,
'Check dRho=0 mid')
1602 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. &
1603 test_ifndp(v,-2.0, .5, 5.0, 0.5, 0.5,
'Check dP=0')
1606 call find_neutral_surface_positions_continuous(3, &
1607 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1608 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1609 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1610 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1611 pilro, pirlo, kol, kor, heff)
1612 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1613 (/1,1,2,2,3,3,3,3/), &
1614 (/1,1,2,2,3,3,3,3/), &
1615 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1616 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1617 (/0.,10.,0.,10.,0.,10.,0./), &
1618 'Identical columns')
1619 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_data1d(v, 8, &
1620 absolute_positions(3, 8, (/0.,10.,20.,30./), kol, pilro), &
1621 (/0.,0.,10.,10.,20.,20.,30.,30./),
'... left positions')
1622 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_data1d(v, 8, &
1623 absolute_positions(3, 8, (/0.,10.,20.,30./), kor, pirlo), &
1624 (/0.,0.,10.,10.,20.,20.,30.,30./),
'... right positions')
1625 call neutral_surface_flux(3, 2*3+2, 2, (/10.,10.,10./), (/10.,10.,10./), &
1626 (/20.,16.,12./), (/20.,16.,12./), &
1627 pilro, pirlo, kol, kor, heff, flx, .true., &
1628 h_neglect, h_neglect_edge=h_neglect_edge)
1629 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_data1d(v, 7, flx, &
1630 (/0.,0.,0.,0.,0.,0.,0./),
'Identical columns, rho flux (=0)')
1631 call neutral_surface_flux(3, 2*3+2, 2, (/10.,10.,10./), (/10.,10.,10./), &
1632 (/-1.,-1.,-1./), (/1.,1.,1./), &
1633 pilro, pirlo, kol, kor, heff, flx, .true., &
1634 h_neglect, h_neglect_edge=h_neglect_edge)
1635 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_data1d(v, 7, flx, &
1636 (/0.,20.,0.,20.,0.,20.,0./),
'Identical columns, S flux')
1639 call find_neutral_surface_positions_continuous(3, &
1640 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1641 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1642 (/0.,10.,20.,30./), (/20.,16.,12.,8./), (/0.,0.,0.,0./), &
1643 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1644 pilro, pirlo, kol, kor, heff)
1645 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1646 (/1,1,2,2,3,3,3,3/), &
1647 (/1,1,1,2,2,3,3,3/), &
1648 (/0.,0.5,0.,0.5,0.,0.5,1.,1./), &
1649 (/0.,0.,0.5,0.,0.5,0.,0.5,1./), &
1650 (/0.,5.,5.,5.,5.,5.,0./), &
1651 'Right column slightly cooler')
1652 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_data1d(v, 8, &
1653 absolute_positions(3, 8, (/0.,10.,20.,30./), kol, pilro), &
1654 (/0.,5.,10.,15.,20.,25.,30.,30./),
'... left positions')
1655 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_data1d(v, 8, &
1656 absolute_positions(3, 8, (/0.,10.,20.,30./), kor, pirlo), &
1657 (/0.,0.,5.,10.,15.,20.,25.,30./),
'... right positions')
1660 call find_neutral_surface_positions_continuous(3, &
1661 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1662 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1663 (/0.,10.,20.,30./), (/24.,20.,16.,12./), (/0.,0.,0.,0./), &
1664 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1665 pilro, pirlo, kol, kor, heff)
1666 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1667 (/1,1,1,2,2,3,3,3/), &
1668 (/1,1,2,2,3,3,3,3/), &
1669 (/0.,0.,0.5,0.,0.5,0.,0.5,1./), &
1670 (/0.,0.5,0.,0.5,0.,0.5,1.,1./), &
1671 (/0.,5.,5.,5.,5.,5.,0./), &
1672 'Right column slightly warmer')
1675 call find_neutral_surface_positions_continuous(3, &
1676 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1677 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1678 (/0.,10.,20.,30./), (/16.,12.,8.,4./), (/0.,0.,0.,0./), &
1679 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1680 pilro, pirlo, kol, kor, heff)
1681 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1682 (/1,2,2,3,3,3,3,3/), &
1683 (/1,1,1,1,2,2,3,3/), &
1684 (/0.,0.,0.5,0.,0.5,1.,1.,1./), &
1685 (/0.,0.,0.,0.5,0.,0.5,0.,1./), &
1686 (/0.,0.,5.,5.,5.,0.,0./), &
1687 'Right column somewhat cooler')
1690 call find_neutral_surface_positions_continuous(3, &
1691 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1692 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1693 (/0.,10.,20.,30./), (/9.,7.,5.,3./), (/0.,0.,0.,0./), &
1694 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1695 pilro, pirlo, kol, kor, heff)
1696 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1697 (/1,2,3,3,3,3,3,3/), &
1698 (/1,1,1,1,1,2,3,3/), &
1699 (/0.,0.,0.,1.,1.,1.,1.,1./), &
1700 (/0.,0.,0.,0.,0.,0.,0.,1./), &
1701 (/0.,0.,0.,0.,0.,0.,0./), &
1702 'Right column much cooler')
1705 call find_neutral_surface_positions_continuous(3, &
1706 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1707 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1708 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1709 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1710 pilro, pirlo, kol, kor, heff)
1711 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1712 (/1,2,3,3,3,3,3,3/), &
1713 (/1,1,1,1,2,3,3,3/), &
1714 (/0.,0.,0.,0.,0.,1.,1.,1./), &
1715 (/0.,0.,0.,0.,0.,0.,0.,1./), &
1716 (/0.,0.,0.,0.,10.,0.,0./), &
1717 'Right column with mixed layer')
1720 call find_neutral_surface_positions_continuous(3, &
1721 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1722 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1723 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1724 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1725 pilro, pirlo, kol, kor, heff)
1726 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1727 (/1,1,2,2,3,3,3,3/), &
1728 (/1,1,2,2,3,3,3,3/), &
1729 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1730 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1731 (/0.,10.,0.,10.,0.,10.,0./), &
1732 'Indentical columns with mixed layer')
1735 call find_neutral_surface_positions_continuous(3, &
1736 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1737 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1738 (/0.,10.,20.,30./), (/10.,14.,12.,4./), (/0.,0.,0.,0./), &
1739 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1740 pilro, pirlo, kol, kor, heff)
1741 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1742 (/1,2,3,3,3,3,3,3/), &
1743 (/1,1,1,2,3,3,3,3/), &
1744 (/0.,0.,0.,0.,0.,0.,.75,1./), &
1745 (/0.,0.,0.,0.,0.,0.25,1.,1./), &
1746 (/0.,0.,0.,0.,0.,7.5,0./), &
1747 'Right column with unstable mixed layer')
1750 call find_neutral_surface_positions_continuous(3, &
1751 (/0.,10.,20.,30./), (/10.,14.,12.,4./), (/0.,0.,0.,0./), &
1752 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1753 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1754 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1755 pilro, pirlo, kol, kor, heff)
1756 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1757 (/1,1,1,2,3,3,3,3/), &
1758 (/1,2,3,3,3,3,3,3/), &
1759 (/0.,0.,0.,0.,0.,0.25,1.,1./), &
1760 (/0.,0.,0.,0.,0.,0.,.75,1./), &
1761 (/0.,0.,0.,0.,0.,7.5,0./), &
1762 'Left column with unstable mixed layer')
1765 call find_neutral_surface_positions_continuous(3, &
1766 (/0.,10.,20.,30./), (/8.,12.,10.,2./), (/0.,0.,0.,0./), &
1767 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1768 (/0.,10.,20.,30./), (/10.,14.,12.,4./), (/0.,0.,0.,0./), &
1769 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1770 pilro, pirlo, kol, kor, heff)
1771 ndiff_unit_tests_continuous = ndiff_unit_tests_continuous .or. test_nsp(v, 8, kol, kor, pilro, pirlo, heff, &
1772 (/1,1,1,1,2,3,3,3/), &
1773 (/1,2,3,3,3,3,3,3/), &
1774 (/0.,0.,0.,0.,0.,0.,0.75,1./), &
1775 (/0.,0.,0.,0.5,0.5,0.5,1.,1./), &
1776 (/0.,0.,0.,0.,0.,6.,0./), &
1777 'Two unstable mixed layers')
1779 if (.not. ndiff_unit_tests_continuous)
write(*,*)
'Pass'
1781 end function ndiff_unit_tests_continuous
1783 logical function ndiff_unit_tests_discontinuous(verbose)
1784 logical,
intent(in) :: verbose
1786 integer,
parameter :: nk = 3
1787 integer,
parameter :: ns = nk*4
1788 real,
dimension(nk) :: sl, sr, tl, tr, hl, hr
1789 real,
dimension(nk,2) :: til, sil, tir, sir
1790 real,
dimension(nk+1) :: pres_l, pres_r
1791 integer,
dimension(ns) :: kol, kor
1792 real,
dimension(ns) :: pol, por
1793 real,
dimension(ns-1) :: heff, flx
1797 real,
dimension(nk,2) :: poly_t_l, poly_t_r, poly_s, poly_slope
1798 real,
dimension(nk,2) :: drdt, drds
1799 logical,
dimension(nk) :: stable_l, stable_r
1801 integer :: ns_l, ns_r
1802 real :: h_neglect, h_neglect_edge
1807 ndiff_unit_tests_discontinuous = .false.
1808 write(*,*)
'==== MOM_neutral_diffusion: ndiff_unit_tests_discontinuous ='
1810 h_neglect = 1.0e-30 ; h_neglect_edge = 1.0e-10
1814 sl(:) = 0. ; sr(:) = 0. ; poly_s(:,:) = 0. ; sil(:,:) = 0. ; sir(:,:) = 0.
1815 drdt(:,:) = -1. ; drds(:,:) = 0.
1818 cs%refine_position = .false.
1821 call initialize_remapping( remap_cs,
"PLM", boundary_extrapolation = .true. )
1823 hl = (/10.,10.,10./) ; hr = (/10.,10.,10./) ; pres_l(1) = 0. ; pres_r(1) = 0.
1824 do k = 1,nk ; pres_l(k+1) = pres_l(k) + hl(k) ; pres_r(k+1) = pres_r(k) + hr(k) ;
enddo
1826 tl = (/20.,16.,12./) ; tr = (/20.,16.,12./)
1827 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1828 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1829 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1830 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1831 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1832 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1833 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 12, kol, kor, pol, por, heff, &
1834 (/1,1,1,1,2,2,2,2,3,3,3,3/), &
1835 (/1,1,1,1,2,2,2,2,3,3,3,3/), &
1836 (/0.,0.,1.,1.,0.,0.,1.,1.,0.,0.,1.,1./), &
1837 (/0.,0.,1.,1.,0.,0.,1.,1.,0.,0.,1.,1./), &
1838 (/0.,10.,0.,0.,0.,10.,0.,0.,0.,10.,0.,0./), &
1839 'Identical columns')
1840 tl = (/20.,16.,12./) ; tr = (/18.,14.,10./)
1841 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1842 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1843 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1844 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1845 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1846 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1847 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 12, kol, kor, pol, por, heff, &
1848 (/1,1,1,2,2,2,2,3,3,3,3,3/), &
1849 (/1,1,1,1,1,2,2,2,2,3,3,3/), &
1850 (/0.0, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, 1.0/), &
1851 (/0.0, 0.0, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, 0.0, 0.5, 1.0/), &
1852 (/0.0, 5.0, 0.0, 5.0, 0.0, 5.0, 0.0, 5.0, 0.0, 5.0, 0.0/), &
1853 'Right column slightly cooler')
1854 tl = (/18.,14.,10./) ; tr = (/20.,16.,12./)
1855 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1856 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1857 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1858 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1859 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1860 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1861 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 12, kol, kor, pol, por, heff, &
1862 (/1,1,1,1,1,2,2,2,2,3,3,3/), &
1863 (/1,1,1,2,2,2,2,3,3,3,3,3/), &
1864 (/0.0, 0.0, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, 0.0, 0.5, 1.0/), &
1865 (/0.0, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, 1.0/), &
1866 (/0.0, 5.0, 0.0, 5.0, 0.0, 5.0, 0.0, 5.0, 0.0, 5.0, 0.0/), &
1867 'Left column slightly cooler')
1868 tl = (/20.,16.,12./) ; tr = (/14.,10.,6./)
1869 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1870 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1871 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1872 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1873 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1874 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1875 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 12, kol, kor, pol, por, heff, &
1876 (/1,1,2,2,2,3,3,3,3,3,3,3/), &
1877 (/1,1,1,1,1,1,1,2,2,2,3,3/), &
1878 (/0.0, 1.0, 0.0, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0/), &
1879 (/0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 0.0, 0.5, 1.0, 0.0, 1.0/), &
1880 (/0.0, 0.0, 0.0, 5.0, 0.0, 5.0, 0.0, 5.0, 0.0, 0.0, 0.0/), &
1881 'Right column somewhat cooler')
1882 tl = (/20.,16.,12./) ; tr = (/8.,6.,4./)
1883 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1884 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1885 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1886 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1887 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1888 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1889 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 12, kol, kor, pol, por, heff, &
1890 (/1,1,2,2,3,3,3,3,3,3,3,3/), &
1891 (/1,1,1,1,1,1,1,1,2,2,3,3/), &
1892 (/0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0/), &
1893 (/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0/), &
1894 (/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0/), &
1895 'Right column much cooler')
1896 tl = (/14.,14.,10./) ; tr = (/14.,14.,10./)
1897 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1898 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1899 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1900 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1901 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1902 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1903 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 12, kol, kor, pol, por, heff, &
1904 (/1,1,1,1,2,2,2,2,3,3,3,3/), &
1905 (/1,1,1,1,2,2,2,2,3,3,3,3/), &
1906 (/0.,0.,1.,1.,0.,0.,1.,1.,0.,0.,1.,1./), &
1907 (/0.,0.,1.,1.,0.,0.,1.,1.,0.,0.,1.,1./), &
1908 (/0.,10.,0.,0.,0.,10.,0.,0.,0.,10.,0.,0./), &
1909 'Identical columns with mixed layer')
1910 tl = (/20.,16.,12./) ; tr = (/14.,14.,10./)
1911 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1912 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1913 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1914 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1915 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1916 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1917 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 12, kol, kor, pol, por, heff, &
1918 (/1,1,2,2,2,3,3,3,3,3,3,3/), &
1919 (/1,1,1,1,1,1,2,2,2,3,3,3/), &
1920 (/0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 1.0, 1.0/), &
1921 (/0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.5, 1.0/), &
1922 (/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0/), &
1923 'Right column with mixed layer')
1924 tl = (/14.,14.,6./) ; tr = (/12.,16.,8./)
1925 call build_reconstructions_1d( remap_cs, nk, hl, tl, poly_t_l, til, poly_slope, imethod, h_neglect, h_neglect_edge )
1926 call build_reconstructions_1d( remap_cs, nk, hr, tr, poly_t_r, tir, poly_slope, imethod, h_neglect, h_neglect_edge )
1927 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1928 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1929 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1930 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1931 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 10, kol, kor, pol, por, heff, &
1932 (/1,1,1,1,2,2,2,3,3,3/), &
1933 (/2,2,2,3,3,3,3,3,3,3/), &
1934 (/0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, .75, 1.0/), &
1935 (/0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, .25, 1.0, 1.0/), &
1936 (/0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.5, 0.0/), &
1937 'Left mixed layer, right unstable mixed layer')
1939 tl = (/10.,11.,6./) ; tr = (/12.,13.,8./)
1940 til(:,1) = (/8.,12.,10./) ; til(:,2) = (/12.,10.,2./)
1941 tir(:,1) = (/10.,14.,12./) ; tir(:,2) = (/14.,12.,4./)
1942 call mark_unstable_cells( nk, drdt, drds, til, sil, stable_l, ns_l )
1943 call mark_unstable_cells( nk, drdt, drds, tir, sir, stable_r, ns_r )
1944 call find_neutral_surface_positions_discontinuous(cs, nk, ns_l+ns_r, pres_l, hl, til, sil, drdt, drds, stable_l, &
1945 pres_r, hr, tir, sir, drdt, drds, stable_r, pol, por, kol, kor, heff)
1946 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. test_nsp(v, 8, kol, kor, pol, por, heff, &
1947 (/2,2,2,2,2,3,3,3/), &
1948 (/2,2,2,3,3,3,3,3/), &
1949 (/0.0, 0.0, 0.0, 0.0, 1.0, 0.0, .75, 1.0/), &
1950 (/0.0, 1.0, 1.0, 0.0, .25, .25, 1.0, 1.0/), &
1951 (/0.0, 0.0, 0.0, 4.0, 0.0, 7.5, 0.0/), &
1952 'Two unstable mixed layers')
1953 deallocate(remap_cs)
1956 call eos_manual_init(eos, form_of_eos = eos_linear, drho_dt = -1., drho_ds = 2.)
1958 allocate(cs%ndiff_aux_CS)
1959 call set_ndiff_aux_params(cs%ndiff_aux_CS, deg = 1, max_iter = 10, drho_tol = 0., xtol = 0., eos = eos)
1961 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. (test_rnp(0.5,refine_nondim_position( &
1962 cs%ndiff_aux_CS, 20., 35., -1., 2., 0., 1., (/21., -2./), (/35., 0./), -1., 1., 0.), &
1963 "Temperature stratified (Newton) "))
1964 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. (test_rnp(0.5,refine_nondim_position( &
1965 cs%ndiff_aux_CS, 20., 35., -1., 2., 0., 1., (/20., 0./), (/34., 2./), -2., 2., 0.), &
1966 "Salinity stratified (Newton) "))
1967 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. (test_rnp(0.5,refine_nondim_position( &
1968 cs%ndiff_aux_CS, 20., 35., -1., 2., 0., 1., (/21., -2./), (/34., 2./), -1., 1., 0.), &
1969 "Temp/Salt stratified (Newton) "))
1970 call set_ndiff_aux_params(cs%ndiff_aux_CS, force_brent = .true.)
1972 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. (test_rnp(0.5,refine_nondim_position( &
1973 cs%ndiff_aux_CS, 20., 35., -1., 2., 0., 1., (/21., -2./), (/35., 0./), -1., 1., 0.), &
1974 "Temperature stratified (Brent) "))
1975 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. (test_rnp(0.5,refine_nondim_position( &
1976 cs%ndiff_aux_CS, 20., 35., -1., 2., 0., 1., (/20., 0./), (/34., 2./), -2., 2., 0.), &
1977 "Salinity stratified (Brent) "))
1978 ndiff_unit_tests_discontinuous = ndiff_unit_tests_discontinuous .or. (test_rnp(0.5,refine_nondim_position( &
1979 cs%ndiff_aux_CS, 20., 35., -1., 2., 0., 1., (/21., -2./), (/34., 2./), -1., 1., 0.), &
1980 "Temp/Salt stratified (Brent) "))
1983 if (.not. ndiff_unit_tests_discontinuous)
write(*,*)
'Pass'
1985 end function ndiff_unit_tests_discontinuous
1988 logical function test_fv_diff(verbose, hkm1, hk, hkp1, Skm1, Sk, Skp1, Ptrue, title)
1989 logical,
intent(in) :: verbose
1990 real,
intent(in) :: hkm1
1991 real,
intent(in) :: hk
1992 real,
intent(in) :: hkp1
1993 real,
intent(in) :: skm1
1994 real,
intent(in) :: sk
1995 real,
intent(in) :: skp1
1996 real,
intent(in) :: ptrue
1997 character(len=*),
intent(in) :: title
2003 pret = fv_diff(hkm1, hk, hkp1, skm1, sk, skp1)
2004 test_fv_diff = (pret /= ptrue)
2006 if (test_fv_diff .or. verbose)
then
2008 if (test_fv_diff) stdunit = 0
2009 write(stdunit,
'(a)') title
2010 if (test_fv_diff)
then
2011 write(stdunit,
'(2(x,a,f20.16),x,a)')
'pRet=',pret,
'pTrue=',ptrue,
'WRONG!'
2013 write(stdunit,
'(2(x,a,f20.16))')
'pRet=',pret,
'pTrue=',ptrue
2017 end function test_fv_diff
2020 logical function test_fvlsq_slope(verbose, hkm1, hk, hkp1, Skm1, Sk, Skp1, Ptrue, title)
2021 logical,
intent(in) :: verbose
2022 real,
intent(in) :: hkm1
2023 real,
intent(in) :: hk
2024 real,
intent(in) :: hkp1
2025 real,
intent(in) :: skm1
2026 real,
intent(in) :: sk
2027 real,
intent(in) :: skp1
2028 real,
intent(in) :: ptrue
2029 character(len=*),
intent(in) :: title
2035 pret = fvlsq_slope(hkm1, hk, hkp1, skm1, sk, skp1)
2036 test_fvlsq_slope = (pret /= ptrue)
2038 if (test_fvlsq_slope .or. verbose)
then
2040 if (test_fvlsq_slope) stdunit = 0
2041 write(stdunit,
'(a)') title
2042 if (test_fvlsq_slope)
then
2043 write(stdunit,
'(2(x,a,f20.16),x,a)')
'pRet=',pret,
'pTrue=',ptrue,
'WRONG!'
2045 write(stdunit,
'(2(x,a,f20.16))')
'pRet=',pret,
'pTrue=',ptrue
2049 end function test_fvlsq_slope
2052 logical function test_ifndp(verbose, rhoNeg, Pneg, rhoPos, Ppos, Ptrue, title)
2053 logical,
intent(in) :: verbose
2054 real,
intent(in) :: rhoneg
2055 real,
intent(in) :: pneg
2056 real,
intent(in) :: rhopos
2057 real,
intent(in) :: ppos
2058 real,
intent(in) :: ptrue
2059 character(len=*),
intent(in) :: title
2065 pret = interpolate_for_nondim_position(rhoneg, pneg, rhopos, ppos)
2066 test_ifndp = (pret /= ptrue)
2068 if (test_ifndp .or. verbose)
then
2070 if (test_ifndp) stdunit = 0
2071 write(stdunit,
'(a)') title
2072 if (test_ifndp)
then
2073 write(stdunit,
'(4(x,a,f20.16),2(x,a,1pe22.15),x,a)') &
2074 'r1=',rhoneg,
'p1=',pneg,
'r2=',rhopos,
'p2=',ppos,
'pRet=',pret,
'pTrue=',ptrue,
'WRONG!'
2076 write(stdunit,
'(4(x,a,f20.16),2(x,a,1pe22.15))') &
2077 'r1=',rhoneg,
'p1=',pneg,
'r2=',rhopos,
'p2=',ppos,
'pRet=',pret,
'pTrue=',ptrue
2081 end function test_ifndp
2084 logical function test_data1d(verbose, nk, Po, Ptrue, title)
2085 logical,
intent(in) :: verbose
2086 integer,
intent(in) :: nk
2087 real,
dimension(nk),
intent(in) :: po
2088 real,
dimension(nk),
intent(in) :: ptrue
2089 character(len=*),
intent(in) :: title
2092 integer :: k, stdunit
2094 test_data1d = .false.
2096 if (po(k) /= ptrue(k)) test_data1d = .true.
2099 if (test_data1d .or. verbose)
then
2101 if (test_data1d) stdunit = 0
2102 write(stdunit,
'(a)') title
2104 if (po(k) /= ptrue(k))
then
2105 test_data1d = .true.
2106 write(stdunit,
'(a,i2,2(x,a,f20.16),x,a,1pe22.15,x,a)') &
2107 'k=',k,
'Po=',po(k),
'Ptrue=',ptrue(k),
'err=',po(k)-ptrue(k),
'WRONG!'
2110 write(stdunit,
'(a,i2,2(x,a,f20.16),x,a,1pe22.15)') &
2111 'k=',k,
'Po=',po(k),
'Ptrue=',ptrue(k),
'err=',po(k)-ptrue(k)
2116 end function test_data1d
2119 logical function test_data1di(verbose, nk, Po, Ptrue, title)
2120 logical,
intent(in) :: verbose
2121 integer,
intent(in) :: nk
2122 integer,
dimension(nk),
intent(in) :: po
2123 integer,
dimension(nk),
intent(in) :: ptrue
2124 character(len=*),
intent(in) :: title
2127 integer :: k, stdunit
2129 test_data1di = .false.
2131 if (po(k) /= ptrue(k)) test_data1di = .true.
2134 if (test_data1di .or. verbose)
then
2136 if (test_data1di) stdunit = 0
2137 write(stdunit,
'(a)') title
2139 if (po(k) /= ptrue(k))
then
2140 test_data1di = .true.
2141 write(stdunit,
'(a,i2,2(x,a,i5),x,a)')
'k=',k,
'Io=',po(k),
'Itrue=',ptrue(k),
'WRONG!'
2144 write(stdunit,
'(a,i2,2(x,a,i5))')
'k=',k,
'Io=',po(k),
'Itrue=',ptrue(k)
2149 end function test_data1di
2153 logical function test_nsp(verbose, ns, KoL, KoR, pL, pR, hEff, KoL0, KoR0, pL0, pR0, hEff0, title)
2154 logical,
intent(in) :: verbose
2155 integer,
intent(in) :: ns
2156 integer,
dimension(ns),
intent(in) :: kol
2157 integer,
dimension(ns),
intent(in) :: kor
2158 real,
dimension(ns),
intent(in) :: pl
2159 real,
dimension(ns),
intent(in) :: pr
2160 real,
dimension(ns-1),
intent(in) :: heff
2161 integer,
dimension(ns),
intent(in) :: kol0
2162 integer,
dimension(ns),
intent(in) :: kor0
2163 real,
dimension(ns),
intent(in) :: pl0
2164 real,
dimension(ns),
intent(in) :: pr0
2165 real,
dimension(ns-1),
intent(in) :: heff0
2166 character(len=*),
intent(in) :: title
2169 integer :: k, stdunit
2170 logical :: this_row_failed
2174 test_nsp = test_nsp .or. compare_nsp_row(kol(k), kor(k), pl(k), pr(k), kol0(k), kor0(k), pl0(k), pr0(k))
2176 if (heff(k) /= heff0(k)) test_nsp = .true.
2180 if (test_nsp .or. verbose)
then
2182 if (test_nsp) stdunit = 0
2183 write(stdunit,
'(a)') title
2185 this_row_failed = compare_nsp_row(kol(k), kor(k), pl(k), pr(k), kol0(k), kor0(k), pl0(k), pr0(k))
2186 if (this_row_failed)
then
2187 write(stdunit,10) k,kol(k),pl(k),kor(k),pr(k),
' <-- WRONG!'
2188 write(stdunit,10) k,kol0(k),pl0(k),kor0(k),pr0(k),
' <-- should be this'
2190 write(stdunit,10) k,kol(k),pl(k),kor(k),pr(k)
2193 if (heff(k) /= heff0(k))
then
2194 write(stdunit,
'(i3,8x,"layer hEff =",2(f20.16,a))') k,heff(k),
" .neq. ",heff0(k),
' <-- WRONG!'
2196 write(stdunit,
'(i3,8x,"layer hEff =",f20.16)') k,heff(k)
2201 if (test_nsp)
call mom_error(fatal,
"test_nsp failed")
2203 10
format(
"ks=",i3,
" kL=",i3,
" pL=",f20.16,
" kR=",i3,
" pR=",f20.16,a)
2204 end function test_nsp
2207 logical function compare_nsp_row(KoL, KoR, pL, pR, KoL0, KoR0, pL0, pR0)
2208 integer,
intent(in) :: kol
2209 integer,
intent(in) :: kor
2210 real,
intent(in) :: pl
2211 real,
intent(in) :: pr
2212 integer,
intent(in) :: kol0
2213 integer,
intent(in) :: kor0
2214 real,
intent(in) :: pl0
2215 real,
intent(in) :: pr0
2217 compare_nsp_row = .false.
2218 if (kol /= kol0) compare_nsp_row = .true.
2219 if (kor /= kor0) compare_nsp_row = .true.
2220 if (pl /= pl0) compare_nsp_row = .true.
2221 if (pr /= pr0) compare_nsp_row = .true.
2222 end function compare_nsp_row
2225 logical function test_rnp(expected_pos, test_pos, title)
2226 real,
intent(in) :: expected_pos
2227 real,
intent(in) :: test_pos
2228 character(len=*),
intent(in) :: title
2230 integer :: stdunit = 6
2231 test_rnp = expected_pos /= test_pos
2233 write(stdunit,
'(A, f20.16, " .neq. ", f20.16, " <-- WRONG")') title, expected_pos, test_pos
2235 write(stdunit,
'(A, f20.16, " == ", f20.16)') title, expected_pos, test_pos
2237 end function test_rnp
2239 subroutine neutral_diffusion_end(CS)
2242 if (
associated(cs))
deallocate(cs)
2244 end subroutine neutral_diffusion_end