6 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
18 implicit none ;
private 20 #include <MOM_memory.h> 33 real,
allocatable,
dimension(:,:,:) :: upol
34 real,
allocatable,
dimension(:,:,:) :: upor
35 integer,
allocatable,
dimension(:,:,:) :: ukol
36 integer,
allocatable,
dimension(:,:,:) :: ukor
37 real,
allocatable,
dimension(:,:,:) :: uheff
38 real,
allocatable,
dimension(:,:,:) :: vpol
39 real,
allocatable,
dimension(:,:,:) :: vpor
40 integer,
allocatable,
dimension(:,:,:) :: vkol
41 integer,
allocatable,
dimension(:,:,:) :: vkor
42 real,
allocatable,
dimension(:,:,:) :: vheff
45 integer,
allocatable,
dimension(:) :: id_neutral_diff_tracer_conc_tend
46 integer,
allocatable,
dimension(:) :: id_neutral_diff_tracer_cont_tend
47 integer,
allocatable,
dimension(:) :: id_neutral_diff_tracer_cont_tend_2d
48 integer,
allocatable,
dimension(:) :: id_neutral_diff_tracer_trans_x_2d
49 integer,
allocatable,
dimension(:) :: id_neutral_diff_tracer_trans_y_2d
56 #include "version_variable.h" 57 character(len=40) ::
mdl =
"MOM_neutral_diffusion" 65 type(time_type),
target,
intent(in) :: Time
67 type(
diag_ctrl),
target,
intent(inout) :: diag
72 character(len=256) :: mesg
74 if (
associated(cs))
then 75 call mom_error(fatal,
"neutral_diffusion_init called with associated control structure.")
81 "This module implements neutral diffusion of tracers")
83 "If true, enables the neutral diffusion module.", &
102 allocate(cs%uPoL(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%uPoL(g%isc-1:g%iec,g%jsc:g%jec,:) = 0.
103 allocate(cs%uPoR(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%uPoR(g%isc-1:g%iec,g%jsc:g%jec,:) = 0.
104 allocate(cs%uKoL(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%uKoL(g%isc-1:g%iec,g%jsc:g%jec,:) = 0
105 allocate(cs%uKoR(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%uKoR(g%isc-1:g%iec,g%jsc:g%jec,:) = 0
106 allocate(cs%uHeff(g%isd:g%ied,g%jsd:g%jed,2*g%ke+1)); cs%uHeff(g%isc-1:g%iec,g%jsc:g%jec,:) = 0
108 allocate(cs%vPoL(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%vPoL(g%isc:g%iec,g%jsc-1:g%jec,:) = 0.
109 allocate(cs%vPoR(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%vPoR(g%isc:g%iec,g%jsc-1:g%jec,:) = 0.
110 allocate(cs%vKoL(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%vKoL(g%isc:g%iec,g%jsc-1:g%jec,:) = 0
111 allocate(cs%vKoR(g%isd:g%ied,g%jsd:g%jed,2*g%ke+2)); cs%vKoR(g%isc:g%iec,g%jsc-1:g%jec,:) = 0
112 allocate(cs%vHeff(g%isd:g%ied,g%jsd:g%jed,2*g%ke+1)); cs%vHeff(g%isc:g%iec,g%jsc-1:g%jec,:) = 0
119 type(time_type),
target,
intent(in) :: Time
123 real,
intent(in) :: C_p
129 if(.not.
associated(cs))
return 134 allocate(cs%id_neutral_diff_tracer_conc_tend(ntr))
135 allocate(cs%id_neutral_diff_tracer_cont_tend(ntr))
136 allocate(cs%id_neutral_diff_tracer_cont_tend_2d(ntr))
137 allocate(cs%id_neutral_diff_tracer_trans_x_2d(ntr))
138 allocate(cs%id_neutral_diff_tracer_trans_y_2d(ntr))
139 cs%id_neutral_diff_tracer_conc_tend(:) = -1
140 cs%id_neutral_diff_tracer_cont_tend(:) = -1
141 cs%id_neutral_diff_tracer_cont_tend_2d(:) = -1
142 cs%id_neutral_diff_tracer_trans_x_2d(:) = -1
143 cs%id_neutral_diff_tracer_trans_y_2d(:) = -1
147 if(trim(reg%Tr(n)%name) ==
'T')
then 150 'ndiff_tracer_conc_tendency_'//trim(reg%Tr(n)%name), diag%axesTL, time, &
151 'Neutral diffusion tracer concentration tendency for '//trim(reg%Tr(n)%name),&
152 'Degree C per second')
155 'ndiff_tracer_cont_tendency_'//trim(reg%Tr(n)%name), diag%axesTL, time, &
156 'Neutral diffusion tracer content tendency for '//trim(reg%Tr(n)%name), &
157 'Watts/m2',cmor_field_name=
'opottemppmdiff', cmor_units=
'W m-2', &
158 cmor_standard_name= &
159 'tendency_of_sea_water_potential_temperature_expressed_as_heat_content_due_to_parameterized_mesocale_diffusion', &
161 'Tendency of sea water potential temperature expressed as heat content due to parameterized mesocale diffusion', &
165 'ndiff_tracer_cont_tendency_2d_'//trim(reg%Tr(n)%name), diag%axesT1, time, &
166 'Depth integrated neutral diffusion tracer content tendency for '//trim(reg%Tr(n)%name), &
167 'Watts/m2',cmor_field_name=
'opottemppmdiff_2d', cmor_units=
'W m-2', &
168 cmor_standard_name= &
169 'tendency_of_sea_water_potential_temperature_expressed_as_heat_content_due_to_parameterized_mesocale_diffusion_depth_integrated',&
171 'Tendency of sea water potential temperature expressed as heat content due to parameterized mesocale diffusion depth integrated')
174 'ndiff_tracer_trans_x_2d_'//trim(reg%Tr(n)%name), diag%axesCu1, time, &
175 'Depth integrated neutral diffusion zonal tracer transport for '//trim(reg%Tr(n)%name),&
179 'ndiff_tracer_trans_y_2d_'//trim(reg%Tr(n)%name), diag%axesCv1, time, &
180 'Depth integrated neutral diffusion merid tracer transport for '//trim(reg%Tr(n)%name),&
183 elseif(trim(reg%Tr(n)%name) ==
'S')
then 186 'ndiff_tracer_conc_tendency_'//trim(reg%Tr(n)%name), diag%axesTL, time, &
187 'Neutral diffusion tracer concentration tendency for '//trim(reg%Tr(n)%name),&
188 'tracer concentration units per second')
191 'ndiff_tracer_cont_tendency_'//trim(reg%Tr(n)%name), diag%axesTL, time, &
192 'Neutral diffusion tracer content tendency for '//trim(reg%Tr(n)%name), &
193 'kg m-2 s-1',cmor_field_name=
'osaltpmdiff', cmor_units=
'kg m-2 s-1', &
194 cmor_standard_name= &
195 'tendency_of_sea_water_salinity_expressed_as_salt_content_due_to_parameterized_mesocale_diffusion', &
197 'Tendency of sea water salinity expressed as salt content due to parameterized mesocale diffusion', &
201 'ndiff_tracer_cont_tendency_2d_'//trim(reg%Tr(n)%name), diag%axesT1, time, &
202 'Depth integrated neutral diffusion tracer content tendency for '//trim(reg%Tr(n)%name), &
203 'kg m-2 s-1',cmor_field_name=
'osaltpmdiff_2d', cmor_units=
'kg m-2 s-1', &
204 cmor_standard_name= &
205 'tendency_of_sea_water_salinity_expressed_as_salt_content_due_to_parameterized_mesocale_diffusion_depth_integrated',&
207 'Tendency of sea water salinity expressed as salt content due to parameterized mesocale diffusion depth integrated')
210 'ndiff_tracer_trans_x_2d_'//trim(reg%Tr(n)%name), diag%axesCu1, time, &
211 'Depth integrated neutral diffusion zonal tracer transport for '//trim(reg%Tr(n)%name),&
215 'ndiff_tracer_trans_y_2d_'//trim(reg%Tr(n)%name), diag%axesCv1, time, &
216 'Depth integrated neutral diffusion merid tracer transport for '//trim(reg%Tr(n)%name),&
222 'ndiff_tracer_conc_tendency_'//trim(reg%Tr(n)%name), diag%axesTL, time, &
223 'Neutral diffusion tracer concentration tendency for '//trim(reg%Tr(n)%name),&
224 'tracer concentration * m-2 s-1')
227 'ndiff_tracer_cont_tendency_'//trim(reg%Tr(n)%name), diag%axesTL, time, &
228 'Neutral diffusion tracer content tendency for '//trim(reg%Tr(n)%name), &
229 'tracer content * m-2 s-1', v_extensive=.true.)
232 'ndiff_tracer_cont_tendency_2d_'//trim(reg%Tr(n)%name), diag%axesTL, time, &
233 'Depth integrated neutral diffusion tracer content tendency for '//trim(reg%Tr(n)%name),&
234 'tracer content * m-2 s-1')
237 'ndiff_tracer_trans_x_2d_'//trim(reg%Tr(n)%name), diag%axesCu1, time, &
238 'Depth integrated neutral diffusion zonal tracer transport for '//trim(reg%Tr(n)%name),&
242 'ndiff_tracer_trans_y_2d_'//trim(reg%Tr(n)%name), diag%axesCv1, time, &
243 'Depth integrated neutral diffusion merid tracer transport for '//trim(reg%Tr(n)%name),&
258 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: h
259 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: T
260 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: S
266 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)+1) :: Tint
267 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)+1) :: Sint
268 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)+1) :: Pint
269 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)+1) :: dRdT
270 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)+1) :: dRdS
272 do j = g%jsc-1, g%jec+1
274 do i = g%isc-1, g%iec+1
275 call interface_scalar(g%ke, h(i,j,:), t(i,j,:), tint(i,j,:), 2)
276 call interface_scalar(g%ke, h(i,j,:), s(i,j,:), sint(i,j,:), 2)
283 drdt(:,j,k), drds(:,j,k), g%isc-1, g%iec-g%isc+3, eos)
284 if (k<=g%ke) pint(:,j,k+1) = pint(:,j,k) + h(:,j,k) * gv%H_to_Pa
290 do i = g%isc-1, g%iec
292 pint(i,j,:), tint(i,j,:), sint(i,j,:), drdt(i,j,:), drds(i,j,:), &
293 pint(i+1,j,:), tint(i+1,j,:), sint(i+1,j,:), drdt(i+1,j,:), drds(i+1,j,:),&
294 cs%uPoL(i,j,:), cs%uPoR(i,j,:), cs%uKoL(i,j,:), cs%uKoR(i,j,:), cs%uhEff(i,j,:) )
299 do j = g%jsc-1, g%jec
302 pint(i,j,:), tint(i,j,:), sint(i,j,:), drdt(i,j,:), drds(i,j,:), &
303 pint(i,j+1,:), tint(i,j+1,:), sint(i,j+1,:), drdt(i,j+1,:), drds(i,j+1,:),&
304 cs%vPoL(i,j,:), cs%vPoR(i,j,:), cs%vKoL(i,j,:), cs%vKoR(i,j,:), cs%vhEff(i,j,:) )
308 cs%uhEff(:,:,:) = cs%uhEff(:,:,:) / gv%H_to_pa
309 cs%vhEff(:,:,:) = cs%vhEff(:,:,:) / gv%H_to_pa
318 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: h
319 real,
dimension(SZIB_(G),SZJ_(G)),
intent(in) :: Coef_x
320 real,
dimension(SZI_(G),SZJB_(G)),
intent(in) :: Coef_y
321 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(inout) :: Tracer
322 integer,
intent(in) :: m
323 real,
intent(in) :: dt
324 character(len=32),
intent(in) :: name
328 real,
dimension(SZIB_(G),SZJ_(G),2*G%ke+1) :: uFlx
329 real,
dimension(SZI_(G),SZJB_(G),2*G%ke+1) :: vFlx
330 real,
dimension(SZI_(G),SZJ_(G),G%ke) :: tendency
331 real,
dimension(SZI_(G),SZJ_(G)) :: tendency_2d
332 real,
dimension(SZIB_(G),SZJ_(G)) :: trans_x_2d
333 real,
dimension(SZI_(G),SZJB_(G)) :: trans_y_2d
334 real,
dimension(G%ke) :: dTracer
335 integer :: i, j, k, ks, nk
336 real :: ppt2mks, Idt, convert
341 if(cs%id_neutral_diff_tracer_conc_tend(m) > 0 .or. &
342 cs%id_neutral_diff_tracer_cont_tend(m) > 0 .or. &
343 cs%id_neutral_diff_tracer_cont_tend_2d(m) > 0 .or. &
344 cs%id_neutral_diff_tracer_trans_x_2d(m) > 0 .or. &
345 cs%id_neutral_diff_tracer_trans_y_2d(m) > 0)
then 348 tendency(:,:,:) = 0.0
349 tendency_2d(:,:) = 0.0
350 trans_x_2d(:,:) = 0.0
351 trans_y_2d(:,:) = 0.0
353 if(trim(name) ==
'T') convert = cs%C_p * gv%H_to_kg_m2
354 if(trim(name) ==
'S') convert = ppt2mks * gv%H_to_kg_m2
359 do j = g%jsc,g%jec ;
do i = g%isc-1,g%iec
360 if (g%mask2dCu(i,j)>0.)
then 362 tracer(i,j,:), tracer(i+1,j,:), &
363 cs%uPoL(i,j,:), cs%uPoR(i,j,:), &
364 cs%uKoL(i,j,:), cs%uKoR(i,j,:), &
365 cs%uhEff(i,j,:), uflx(i,j,:))
372 do j = g%jsc-1,g%jec ;
do i = g%isc,g%iec
373 if (g%mask2dCv(i,j)>0.)
then 375 tracer(i,j,:), tracer(i,j+1,:), &
376 cs%vPoL(i,j,:), cs%vPoR(i,j,:), &
377 cs%vKoL(i,j,:), cs%vKoR(i,j,:), &
378 cs%vhEff(i,j,:), vflx(i,j,:))
385 do j = g%jsc,g%jec ;
do i = g%isc,g%iec
386 if (g%mask2dT(i,j)>0.)
then 391 dtracer(k) = dtracer(k) + coef_x(i,j) * uflx(i,j,ks)
392 k = cs%uKoR(i-1,j,ks)
393 dtracer(k) = dtracer(k) - coef_x(i-1,j) * uflx(i-1,j,ks)
395 dtracer(k) = dtracer(k) + coef_y(i,j) * vflx(i,j,ks)
396 k = cs%vKoR(i,j-1,ks)
397 dtracer(k) = dtracer(k) - coef_y(i,j-1) * vflx(i,j-1,ks)
400 tracer(i,j,k) = tracer(i,j,k) + dtracer(k) * &
401 ( g%IareaT(i,j) / ( h(i,j,k) + gv%H_subroundoff ) )
404 if(cs%id_neutral_diff_tracer_conc_tend(m) > 0 .or. &
405 cs%id_neutral_diff_tracer_cont_tend(m) > 0 .or. &
406 cs%id_neutral_diff_tracer_cont_tend_2d(m) > 0 )
then 408 tendency(i,j,k) = dtracer(k) * g%IareaT(i,j) * idt
418 if(cs%id_neutral_diff_tracer_trans_x_2d(m) > 0)
then 419 do j = g%jsc,g%jec ;
do i = g%isc-1,g%iec
421 if (g%mask2dCu(i,j)>0.)
then 423 trans_x_2d(i,j) = trans_x_2d(i,j) - coef_x(i,j) * uflx(i,j,ks)
425 trans_x_2d(i,j) = trans_x_2d(i,j) * idt * convert
428 call post_data(cs%id_neutral_diff_tracer_trans_x_2d(m), trans_x_2d(:,:), cs%diag)
433 if(cs%id_neutral_diff_tracer_trans_y_2d(m) > 0)
then 434 do j = g%jsc-1,g%jec ;
do i = g%isc,g%iec
436 if (g%mask2dCv(i,j)>0.)
then 438 trans_y_2d(i,j) = trans_y_2d(i,j) - coef_y(i,j) * vflx(i,j,ks)
440 trans_y_2d(i,j) = trans_y_2d(i,j) * idt * convert
443 call post_data(cs%id_neutral_diff_tracer_trans_y_2d(m), trans_y_2d(:,:), cs%diag)
447 if(cs%id_neutral_diff_tracer_cont_tend(m) > 0)
then 448 call post_data(cs%id_neutral_diff_tracer_cont_tend(m), tendency(:,:,:)*convert, cs%diag)
452 if(cs%id_neutral_diff_tracer_cont_tend_2d(m) > 0)
then 453 do j = g%jsc,g%jec ;
do i = g%isc,g%iec
455 tendency_2d(i,j) = tendency_2d(i,j) + tendency(i,j,k)
458 call post_data(cs%id_neutral_diff_tracer_cont_tend_2d(m), tendency_2d(:,:)*convert, cs%diag)
464 if(cs%id_neutral_diff_tracer_conc_tend(m) > 0)
then 465 do k = 1, gv%ke ;
do j = g%jsc,g%jec ;
do i = g%isc,g%iec
466 tendency(i,j,k) = tendency(i,j,k) / ( h(i,j,k) + gv%H_subroundoff )
467 enddo ;
enddo ;
enddo 468 call post_data(cs%id_neutral_diff_tracer_conc_tend(m), tendency, cs%diag)
477 integer,
intent(in) :: nk
478 real,
dimension(nk),
intent(in) :: h
479 real,
dimension(nk),
intent(in) :: S
480 real,
dimension(nk+1),
intent(inout) :: Si
481 integer,
intent(in) :: i_method
484 integer :: k, km2, kp1
485 real,
dimension(nk) :: diff
489 si(1) = s(1) - 0.5 * diff(1)
490 if (i_method==1)
then 494 sa = s(k-1) + 0.5 * diff(k-1)
495 sb = s(k) - 0.5 * diff(k)
496 si(k) = 0.5 * ( sa + sb )
498 elseif (i_method==2)
then 504 si(k) =
ppm_edge(h(km2), h(k-1), h(k), h(kp1), s(k-1), s(k), diff(k-1), diff(k))
507 si(nk+1) = s(nk) + 0.5 * diff(nk)
513 real function ppm_edge(hkm1, hk, hkp1, hkp2, Ak, Akp1, Pk, Pkp1)
514 real,
intent(in) :: hkm1
515 real,
intent(in) :: hk
516 real,
intent(in) :: hkp1
517 real,
intent(in) :: hkp2
518 real,
intent(in) :: Ak
519 real,
intent(in) :: Akp1
520 real,
intent(in) :: Pk
521 real,
intent(in) :: Pkp1
524 real :: R_hk_hkp1, R_2hk_hkp1, R_hk_2hkp1, f1, f2, f3, f4
525 real,
parameter :: h_neglect = 1.e-30
527 r_hk_hkp1 = hk + hkp1
528 if (r_hk_hkp1 <= 0.)
then 532 r_hk_hkp1 = 1. / r_hk_hkp1
534 ppm_edge = ak + ( hk * r_hk_hkp1 ) * ( akp1 - ak )
536 ppm_edge = akp1 + ( hkp1 * r_hk_hkp1 ) * ( ak - akp1 )
539 r_2hk_hkp1 = 1. / ( ( 2. * hk + hkp1 ) + h_neglect )
540 r_hk_2hkp1 = 1. / ( ( hk + 2. * hkp1 ) + h_neglect )
541 f1 = 1./ ( ( hk + hkp1) + ( hkm1 + hkp2 ) )
542 f2 = 2. * ( hkp1 * hk ) * r_hk_hkp1 * &
543 ( ( hkm1 + hk ) * r_2hk_hkp1 - ( hkp2 + hkp1 ) * r_hk_2hkp1 )
544 f3 = hk * ( hkm1 + hk ) * r_2hk_hkp1
545 f4 = hkp1 * ( hkp1 + hkp2 ) * r_hk_2hkp1
553 real function ppm_ave(xL, xR, aL, aR, aMean)
554 real,
intent(in) :: xL
555 real,
intent(in) :: xR
556 real,
intent(in) :: aL
557 real,
intent(in) :: aR
558 real,
intent(in) :: aMean
561 real :: dx, xave, a6, a6o3
564 xave = 0.5 * ( xr + xl )
565 a6o3 = 2. * amean - ( al + ar )
569 stop
'ppm_ave: dx<0 should not happend!' 571 stop
'ppm_ave: dx>1 should not happend!' 573 ppm_ave = al + ( ar - al ) * xr + a6 * xr * ( 1. - xr )
575 ppm_ave = ( al + xave * ( ( ar - al ) + a6 ) ) - a6o3 * ( xr**2 + xr * xl + xl**2 )
582 real,
intent(in) :: a
583 real,
intent(in) :: x
592 subroutine plm_diff(nk, h, S, c_method, b_method, diff)
593 integer,
intent(in) :: nk
594 real,
dimension(nk),
intent(in) :: h
595 real,
dimension(nk),
intent(in) :: S
596 integer,
intent(in) :: c_method
597 integer,
intent(in) :: b_method
598 real,
dimension(nk),
intent(inout) :: diff
608 real :: hkm1, hk, hkp1, Skm1, Sk, Skp1, diff_l, diff_r, diff_c
615 if ( ( hkp1 + hk ) * ( hkm1 + hk ) > 0.)
then 619 if (c_method==1)
then 621 if ( hk + 0.5 * (hkm1 + hkp1) /= 0. )
then 622 diff_c = ( skp1 - skm1 ) * ( hk / ( hk + 0.5 * (hkm1 + hkp1) ) )
626 elseif (c_method==2)
then 628 diff_c =
fv_diff(hkm1, hk, hkp1, skm1, sk, skp1)
629 elseif (c_method==3)
then 631 diff_c = hk *
fvlsq_slope(hkm1, hk, hkp1, skm1, sk, skp1)
634 diff_l = 2. * ( sk - skm1 )
635 diff_r = 2. * ( skp1 - sk )
636 if (
signum(1., diff_l) *
signum(1., diff_r) <= 0. )
then 639 diff(k) = sign( min( abs(diff_l), abs(diff_c), abs(diff_r) ), diff_c )
645 if (b_method==1)
then 648 elseif (b_method==2)
then 649 diff(1) = ( s(2) - s(1) ) * 2. * ( h(1) / ( h(1) + h(2) ) )
650 diff(nk) = s(nk) - s(nk-1) * 2. * ( h(nk) / ( h(nk-1) + h(nk) ) )
659 real function fv_diff(hkm1, hk, hkp1, Skm1, Sk, Skp1)
660 real,
intent(in) :: hkm1
661 real,
intent(in) :: hk
662 real,
intent(in) :: hkp1
663 real,
intent(in) :: Skm1
664 real,
intent(in) :: Sk
665 real,
intent(in) :: Skp1
668 real :: h_sum, hp, hm
670 h_sum = ( hkm1 + hkp1 ) + hk
671 if (h_sum /= 0.) h_sum = 1./ h_sum
673 if (hm /= 0.) hm = 1./ hm
675 if (hp /= 0.) hp = 1./ hp
677 ( ( 2. * hkm1 + hk ) * hp * ( skp1 - sk ) &
678 + ( 2. * hkp1 + hk ) * hm * ( sk - skm1 ) )
685 real function fvlsq_slope(hkm1, hk, hkp1, Skm1, Sk, Skp1)
686 real,
intent(in) :: hkm1
687 real,
intent(in) :: hk
688 real,
intent(in) :: hkp1
689 real,
intent(in) :: Skm1
690 real,
intent(in) :: Sk
691 real,
intent(in) :: Skp1
695 real :: h_sum, hx_sum, hxsq_sum, hxy_sum, hy_sum, det
697 xkm1 = -0.5 * ( hk + hkm1 )
698 xkp1 = 0.5 * ( hk + hkp1 )
699 h_sum = ( hkm1 + hkp1 ) + hk
700 hx_sum = hkm1*xkm1 + hkp1*xkp1
701 hxsq_sum = hkm1*(xkm1**2) + hkp1*(xkp1**2)
702 hxy_sum = hkm1*xkm1*skm1 + hkp1*xkp1*skp1
703 hy_sum = ( hkm1*skm1 + hkp1*skp1 ) + hk*sk
704 det = h_sum * hxsq_sum - hx_sum**2
707 fvlsq_slope = ( h_sum * hxy_sum - hx_sum*hy_sum ) / det
715 subroutine find_neutral_surface_positions(nk, Pl, Tl, Sl, dRdTl, dRdSl, Pr, Tr, Sr, dRdTr, dRdSr, PoL, PoR, KoL, KoR, hEff)
716 integer,
intent(in) :: nk
717 real,
dimension(nk+1),
intent(in) :: Pl
718 real,
dimension(nk+1),
intent(in) :: Tl
719 real,
dimension(nk+1),
intent(in) :: Sl
720 real,
dimension(nk+1),
intent(in) :: dRdTl
721 real,
dimension(nk+1),
intent(in) :: dRdSl
722 real,
dimension(nk+1),
intent(in) :: Pr
723 real,
dimension(nk+1),
intent(in) :: Tr
724 real,
dimension(nk+1),
intent(in) :: Sr
725 real,
dimension(nk+1),
intent(in) :: dRdTr
726 real,
dimension(nk+1),
intent(in) :: dRdSr
727 real,
dimension(2*nk+2),
intent(inout) :: PoL
728 real,
dimension(2*nk+2),
intent(inout) :: PoR
729 integer,
dimension(2*nk+2),
intent(inout) :: KoL
730 integer,
dimension(2*nk+2),
intent(inout) :: KoR
731 real,
dimension(2*nk+1),
intent(inout) :: hEff
738 logical :: searching_left_column
739 logical :: searching_right_column
740 logical :: reached_bottom
741 integer :: krm1, klm1
742 real :: dRho, dRhoTop, dRhoBot, hL, hR
743 integer :: lastK_left, lastK_right
744 real :: lastP_left, lastP_right
747 kr = 1 ; lastk_right = 1 ; lastp_right = 0.
748 kl = 1 ; lastk_left = 1 ; lastp_left = 0.
749 reached_bottom = .false.
752 neutral_surfaces:
do k_surface = 1, 2*nk+2
754 if (klm1>nk) stop
'find_neutral_surface_positions(): klm1 went out of bounds!' 756 if (krm1>nk) stop
'find_neutral_surface_positions(): krm1 went out of bounds!' 759 drho = 0.5 * ( ( drdtr(kr) + drdtl(kl) ) * ( tr(kr) - tl(kl) ) &
760 + ( drdsr(kr) + drdsl(kl) ) * ( sr(kr) - sl(kl) ) )
762 if (.not. reached_bottom)
then 764 searching_left_column = .true.
765 searching_right_column = .false.
766 elseif (drho > 0.)
then 767 searching_right_column = .true.
768 searching_left_column = .false.
770 if (kl + kr == 2)
then 771 searching_left_column = .true.
772 searching_right_column = .false.
774 searching_left_column = .not. searching_left_column
775 searching_right_column = .not. searching_right_column
780 if (searching_left_column)
then 783 drhotop = 0.5 * ( ( drdtl(klm1) + drdtr(kr) ) * ( tl(klm1) - tr(kr) ) &
784 + ( drdsl(klm1) + drdsr(kr) ) * ( sl(klm1) - sr(kr) ) )
786 drhobot = 0.5 * ( ( drdtl(klm1+1) + drdtr(kr) ) * ( tl(klm1+1) - tr(kr) ) &
787 + ( drdsl(klm1+1) + drdsr(kr) ) * ( sl(klm1+1) - sr(kr) ) )
791 if (drhotop > 0. .or. kr+kl==2)
then 793 elseif (drhotop >= drhobot)
then 800 if (pol(k_surface)>=1. .and. klm1<nk)
then 802 pol(k_surface) = pol(k_surface) - 1.
804 if (
real(klm1-lastk_left)+(pol(k_surface)-lastp_left)<0.) then
805 pol(k_surface) = lastp_left
808 kol(k_surface) = klm1
819 reached_bottom = .true.
820 searching_right_column = .true.
821 searching_left_column = .false.
823 elseif (searching_right_column)
then 826 drhotop = 0.5 * ( ( drdtr(krm1) + drdtl(kl) ) * ( tr(krm1) - tl(kl) ) &
827 + ( drdsr(krm1) + drdsl(kl) ) * ( sr(krm1) - sl(kl) ) )
829 drhobot = 0.5 * ( ( drdtr(krm1+1) + drdtl(kl) ) * ( tr(krm1+1) - tl(kl) ) &
830 + ( drdsr(krm1+1) + drdsl(kl) ) * ( sr(krm1+1) - sl(kl) ) )
834 if (drhotop >= 0. .or. kr+kl==2)
then 836 elseif (drhotop >= drhobot)
then 843 if (por(k_surface)>=1. .and. krm1<nk)
then 845 por(k_surface) = por(k_surface) - 1.
847 if (
real(krm1-lastk_right)+(por(k_surface)-lastp_right)<0.) then
848 por(k_surface) = lastp_right
851 kor(k_surface) = krm1
862 reached_bottom = .true.
863 searching_right_column = .false.
864 searching_left_column = .true.
870 lastk_left = kol(k_surface) ; lastp_left = pol(k_surface)
871 lastk_right = kor(k_surface) ; lastp_right = por(k_surface)
876 if (k_surface>1)
then 879 if ( hl + hr > 0.)
then 880 heff(k_surface-1) = 2. * hl * hr / ( hl + hr )
882 heff(k_surface-1) = 0.
886 enddo neutral_surfaces
892 integer,
intent(in) :: n
893 real,
intent(in) :: Pint(n+1)
894 integer,
intent(in) :: Karr(2*n+2)
895 real,
intent(in) :: NParr(2*n+2)
898 integer :: k_surface, k
901 if (k>n) stop
'absolute_position: k>nk is out of bounds!' 908 integer,
intent(in) :: n
909 real,
intent(in) :: Pint(n+1)
910 integer,
intent(in) :: Karr(2*n+2)
911 real,
intent(in) :: NParr(2*n+2)
913 real,
dimension(2*n+2) :: absolute_positions
916 integer :: k_surface, k
918 do k_surface = 1, 2*n+2
928 real,
intent(in) :: dRhoNeg
929 real,
intent(in) :: Pneg
930 real,
intent(in) :: dRhoPos
931 real,
intent(in) :: Ppos
933 if (ppos<pneg) stop
'interpolate_for_nondim_position: Houston, we have a problem! Ppos<Pneg' 934 if (drhoneg>drhopos)
write(0,*)
'dRhoNeg, Pneg, dRhoPos, Ppos=',drhoneg, pneg, drhopos, ppos
935 if (drhoneg>drhopos) stop
'interpolate_for_nondim_position: Houston, we have a problem! dRhoNeg>dRhoPos' 938 elseif ( drhopos - drhoneg > 0. )
then 940 elseif ( drhopos - drhoneg == 0)
then 943 elseif (drhoneg<0.)
then 957 subroutine neutral_surface_flux(nk, hl, hr, Tl, Tr, PiL, PiR, KoL, KoR, hEff, Flx)
958 integer,
intent(in) :: nk
959 real,
dimension(nk),
intent(in) :: hl
960 real,
dimension(nk),
intent(in) :: hr
961 real,
dimension(nk),
intent(in) :: Tl
962 real,
dimension(nk),
intent(in) :: Tr
963 real,
dimension(2*nk+2),
intent(in) :: PiL
964 real,
dimension(2*nk+2),
intent(in) :: PiR
965 integer,
dimension(2*nk+2),
intent(in) :: KoL
966 integer,
dimension(2*nk+2),
intent(in) :: KoR
967 real,
dimension(2*nk+1),
intent(in) :: hEff
968 real,
dimension(2*nk+1),
intent(inout) :: Flx
971 integer :: k_sublayer, klb, klt, krb, krt, k
972 real :: T_right_top, T_right_bottom, T_right_layer
973 real :: T_left_top, T_left_bottom, T_left_layer
974 real :: dT_top, dT_bottom, dT_layer, dT_ave
975 real,
dimension(nk+1) :: Til
976 real,
dimension(nk+1) :: Tir
977 real,
dimension(nk) :: aL_l
978 real,
dimension(nk) :: aR_l
979 real,
dimension(nk) :: aL_r
980 real,
dimension(nk) :: aR_r
989 if (
signum(1., ar_l(k) - tl(k))*
signum(1., tl(k) - al_l(k)) <= 0.0 )
then 992 elseif ( sign(3., ar_l(k) - al_l(k)) * ( (tl(k) - al_l(k)) + (tl(k) - ar_l(k))) > abs(ar_l(k) - al_l(k)) )
then 993 al_l(k) = tl(k) + 2.0 * ( tl(k) - ar_l(k) )
994 elseif ( sign(3., ar_l(k) - al_l(k)) * ( (tl(k) - al_l(k)) + (tl(k) - ar_l(k))) < -abs(ar_l(k) - al_l(k)) )
then 995 ar_l(k) = tl(k) + 2.0 * ( tl(k) - al_l(k) )
999 if (
signum(1., ar_r(k) - tr(k))*
signum(1., tr(k) - al_r(k)) <= 0.0 )
then 1002 elseif ( sign(3., ar_r(k) - al_r(k)) * ( (tr(k) - al_r(k)) + (tr(k) - ar_r(k))) > abs(ar_r(k) - al_r(k)) )
then 1003 al_r(k) = tr(k) + 2.0 * ( tr(k) - ar_r(k) )
1004 elseif ( sign(3., ar_r(k) - al_r(k)) * ( (tr(k) - al_r(k)) + (tr(k) - ar_r(k))) < -abs(ar_r(k) - al_r(k)) )
then 1005 ar_r(k) = tr(k) + 2.0 * ( tr(k) - al_r(k) )
1009 do k_sublayer = 1, 2*nk+1
1010 if (heff(k_sublayer) == 0.)
then 1011 flx(k_sublayer) = 0.
1014 klb = kol(k_sublayer+1)
1015 t_left_bottom = ( 1. - pil(k_sublayer+1) ) * til(klb) + pil(k_sublayer+1) * til(klb+1)
1017 klt = kol(k_sublayer)
1018 t_left_top = ( 1. - pil(k_sublayer) ) * til(klt) + pil(k_sublayer) * til(klt+1)
1020 t_left_layer =
ppm_ave(pil(k_sublayer), pil(k_sublayer+1) +
real(klb-klt), &
1021 aL_l(klt), aR_l(klt), Tl(klt))
1023 krb = kor(k_sublayer+1)
1024 t_right_bottom = ( 1. - pir(k_sublayer+1) ) * tir(krb) + pir(k_sublayer+1) * tir(krb+1)
1026 krt = kor(k_sublayer)
1027 t_right_top = ( 1. - pir(k_sublayer) ) * tir(krt) + pir(k_sublayer) * tir(krt+1)
1029 t_right_layer =
ppm_ave(pir(k_sublayer), pir(k_sublayer+1) +
real(krb-krt), &
1030 aL_r(krt), aR_r(krt), Tr(krt))
1032 dt_top = t_right_top - t_left_top
1033 dt_bottom = t_right_bottom - t_left_bottom
1034 dt_ave = 0.5 * ( dt_top + dt_bottom )
1035 dt_layer = t_right_layer - t_left_layer
1041 flx(k_sublayer) = dt_ave * heff(k_sublayer)
1049 logical,
intent(in) :: verbose
1051 integer,
parameter :: nk = 4
1052 real,
dimension(nk+1) :: TiL, TiR1, TiR2, TiR4, Tio
1053 real,
dimension(nk) :: TL
1054 real,
dimension(nk+1) :: SiL
1055 real,
dimension(nk+1) :: PiL, PiR4
1056 real,
dimension(2*nk+2) :: PiLRo, PiRLo
1057 integer,
dimension(2*nk+2) :: KoL, KoR
1058 real,
dimension(2*nk+1) :: hEff
1059 real,
dimension(2*nk+1) :: Flx
1066 write(*,*)
'==== MOM_neutral_diffusion: neutral_diffusion_unit_tests =' 1086 call interface_scalar(4, (/10.,10.,10.,10./), (/24.,18.,12.,6./), tio, 1)
1089 call interface_scalar(4, (/10.,10.,10.,10./), (/24.,18.,12.,6./), tio, 2)
1106 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1107 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1108 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1109 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1110 pilro, pirlo, kol, kor, heff)
1112 (/1,1,2,2,3,3,3,3/), &
1113 (/1,1,2,2,3,3,3,3/), &
1114 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1115 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1116 (/0.,10.,0.,10.,0.,10.,0./), &
1117 'Identical columns')
1120 (/0.,0.,10.,10.,20.,20.,30.,30./),
'... left positions')
1123 (/0.,0.,10.,10.,20.,20.,30.,30./),
'... right positions')
1125 (/20.,16.,12./), (/20.,16.,12./), &
1126 pilro, pirlo, kol, kor, heff, flx)
1128 (/0.,0.,0.,0.,0.,0.,0./),
'Identical columns, rho flux (=0)')
1130 (/-1.,-1.,-1./), (/1.,1.,1./), &
1131 pilro, pirlo, kol, kor, heff, flx)
1133 (/0.,20.,0.,20.,0.,20.,0./),
'Identical columns, S flux')
1137 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1138 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1139 (/0.,10.,20.,30./), (/20.,16.,12.,8./), (/0.,0.,0.,0./), &
1140 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1141 pilro, pirlo, kol, kor, heff)
1143 (/1,1,2,2,3,3,3,3/), &
1144 (/1,1,1,2,2,3,3,3/), &
1145 (/0.,0.5,0.,0.5,0.,0.5,1.,1./), &
1146 (/0.,0.,0.5,0.,0.5,0.,0.5,1./), &
1147 (/0.,5.,5.,5.,5.,5.,0./), &
1148 'Right column slightly cooler')
1151 (/0.,5.,10.,15.,20.,25.,30.,30./),
'... left positions')
1154 (/0.,0.,5.,10.,15.,20.,25.,30./),
'... right positions')
1158 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1159 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1160 (/0.,10.,20.,30./), (/24.,20.,16.,12./), (/0.,0.,0.,0./), &
1161 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1162 pilro, pirlo, kol, kor, heff)
1164 (/1,1,1,2,2,3,3,3/), &
1165 (/1,1,2,2,3,3,3,3/), &
1166 (/0.,0.,0.5,0.,0.5,0.,0.5,1./), &
1167 (/0.,0.5,0.,0.5,0.,0.5,1.,1./), &
1168 (/0.,5.,5.,5.,5.,5.,0./), &
1169 'Right column slightly warmer')
1173 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1174 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1175 (/0.,10.,20.,30./), (/16.,12.,8.,4./), (/0.,0.,0.,0./), &
1176 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1177 pilro, pirlo, kol, kor, heff)
1179 (/1,2,2,3,3,3,3,3/), &
1180 (/1,1,1,1,2,2,3,3/), &
1181 (/0.,0.,0.5,0.,0.5,1.,1.,1./), &
1182 (/0.,0.,0.,0.5,0.,0.5,0.,1./), &
1183 (/0.,0.,5.,5.,5.,0.,0./), &
1184 'Right column somewhat cooler')
1188 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1189 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1190 (/0.,10.,20.,30./), (/9.,7.,5.,3./), (/0.,0.,0.,0./), &
1191 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1192 pilro, pirlo, kol, kor, heff)
1194 (/1,2,3,3,3,3,3,3/), &
1195 (/1,1,1,1,1,2,3,3/), &
1196 (/0.,0.,0.,1.,1.,1.,1.,1./), &
1197 (/0.,0.,0.,0.,0.,0.,0.,1./), &
1198 (/0.,0.,0.,0.,0.,0.,0./), &
1199 'Right column much cooler')
1203 (/0.,10.,20.,30./), (/22.,18.,14.,10./), (/0.,0.,0.,0./), &
1204 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1205 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1206 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1207 pilro, pirlo, kol, kor, heff)
1209 (/1,2,3,3,3,3,3,3/), &
1210 (/1,1,1,1,2,3,3,3/), &
1211 (/0.,0.,0.,0.,0.,1.,1.,1./), &
1212 (/0.,0.,0.,0.,0.,0.,0.,1./), &
1213 (/0.,0.,0.,0.,10.,0.,0./), &
1214 'Right column with mixed layer')
1218 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1219 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1220 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1221 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1222 pilro, pirlo, kol, kor, heff)
1224 (/1,1,2,2,3,3,3,3/), &
1225 (/1,1,2,2,3,3,3,3/), &
1226 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1227 (/0.,0.,0.,0.,0.,0.,1.,1./), &
1228 (/0.,10.,0.,10.,0.,10.,0./), &
1229 'Indentical columns with mixed layer')
1233 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1234 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1235 (/0.,10.,20.,30./), (/10.,14.,12.,4./), (/0.,0.,0.,0./), &
1236 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1237 pilro, pirlo, kol, kor, heff)
1239 (/1,2,3,3,3,3,3,3/), &
1240 (/1,1,1,2,3,3,3,3/), &
1241 (/0.,0.,0.,0.,0.,0.,.75,1./), &
1242 (/0.,0.,0.,0.,0.,0.25,1.,1./), &
1243 (/0.,0.,0.,0.,0.,7.5,0./), &
1244 'Right column with unstable mixed layer')
1248 (/0.,10.,20.,30./), (/10.,14.,12.,4./), (/0.,0.,0.,0./), &
1249 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1250 (/0.,10.,20.,30./), (/14.,14.,10.,2./), (/0.,0.,0.,0./), &
1251 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1252 pilro, pirlo, kol, kor, heff)
1254 (/1,1,1,2,3,3,3,3/), &
1255 (/1,2,3,3,3,3,3,3/), &
1256 (/0.,0.,0.,0.,0.,0.25,1.,1./), &
1257 (/0.,0.,0.,0.,0.,0.,.75,1./), &
1258 (/0.,0.,0.,0.,0.,7.5,0./), &
1259 'Left column with unstable mixed layer')
1263 (/0.,10.,20.,30./), (/8.,12.,10.,2./), (/0.,0.,0.,0./), &
1264 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1265 (/0.,10.,20.,30./), (/10.,14.,12.,4./), (/0.,0.,0.,0./), &
1266 (/-1.,-1.,-1.,-1./), (/1.,1.,1.,1./), &
1267 pilro, pirlo, kol, kor, heff)
1269 (/1,1,1,1,2,3,3,3/), &
1270 (/1,2,3,3,3,3,3,3/), &
1271 (/0.,0.,0.,0.,0.,0.,0.75,1./), &
1272 (/0.,0.,0.,0.5,0.5,0.5,1.,1./), &
1273 (/0.,0.,0.,0.,0.,6.,0./), &
1274 'Two unstable mixed layers')
1281 logical function test_fv_diff(verbose, hkm1, hk, hkp1, Skm1, Sk, Skp1, Ptrue, title)
1282 logical,
intent(in) :: verbose
1283 real,
intent(in) :: hkm1
1284 real,
intent(in) :: hk
1285 real,
intent(in) :: hkp1
1286 real,
intent(in) :: Skm1
1287 real,
intent(in) :: Sk
1288 real,
intent(in) :: Skp1
1289 real,
intent(in) :: Ptrue
1290 character(len=*),
intent(in) :: title
1296 pret =
fv_diff(hkm1, hk, hkp1, skm1, sk, skp1)
1302 write(stdunit,
'(a)') title
1304 write(stdunit,
'(2(x,a,f20.16),x,a)')
'pRet=',pret,
'pTrue=',ptrue,
'WRONG!' 1306 write(stdunit,
'(2(x,a,f20.16))')
'pRet=',pret,
'pTrue=',ptrue
1313 logical function test_fvlsq_slope(verbose, hkm1, hk, hkp1, Skm1, Sk, Skp1, Ptrue, title)
1314 logical,
intent(in) :: verbose
1315 real,
intent(in) :: hkm1
1316 real,
intent(in) :: hk
1317 real,
intent(in) :: hkp1
1318 real,
intent(in) :: Skm1
1319 real,
intent(in) :: Sk
1320 real,
intent(in) :: Skp1
1321 real,
intent(in) :: Ptrue
1322 character(len=*),
intent(in) :: title
1328 pret =
fvlsq_slope(hkm1, hk, hkp1, skm1, sk, skp1)
1334 write(stdunit,
'(a)') title
1336 write(stdunit,
'(2(x,a,f20.16),x,a)')
'pRet=',pret,
'pTrue=',ptrue,
'WRONG!' 1338 write(stdunit,
'(2(x,a,f20.16))')
'pRet=',pret,
'pTrue=',ptrue
1345 logical function test_ifndp(verbose, rhoNeg, Pneg, rhoPos, Ppos, Ptrue, title)
1346 logical,
intent(in) :: verbose
1347 real,
intent(in) :: rhoNeg
1348 real,
intent(in) :: Pneg
1349 real,
intent(in) :: rhoPos
1350 real,
intent(in) :: Ppos
1351 real,
intent(in) :: Ptrue
1352 character(len=*),
intent(in) :: title
1364 write(stdunit,
'(a)') title
1366 write(stdunit,
'(4(x,a,f20.16),2(x,a,1pe22.15),x,a)')
'r1=',rhoneg,
'p1=',pneg,
'r2=',rhopos,
'p2=',ppos,
'pRet=',pret,
'pTrue=',ptrue,
'WRONG!' 1368 write(stdunit,
'(4(x,a,f20.16),2(x,a,1pe22.15))')
'r1=',rhoneg,
'p1=',pneg,
'r2=',rhopos,
'p2=',ppos,
'pRet=',pret,
'pTrue=',ptrue
1375 logical function test_data1d(verbose, nk, Po, Ptrue, title)
1376 logical,
intent(in) :: verbose
1377 integer,
intent(in) :: nk
1378 real,
dimension(nk),
intent(in) :: Po
1379 real,
dimension(nk),
intent(in) :: Ptrue
1380 character(len=*),
intent(in) :: title
1383 integer :: k, stdunit
1393 write(stdunit,
'(a)') title
1395 if (po(k) /= ptrue(k))
then 1397 write(stdunit,
'(a,i2,2(x,a,f20.16),x,a,1pe22.15,x,a)')
'k=',k,
'Po=',po(k),
'Ptrue=',ptrue(k),
'err=',po(k)-ptrue(k),
'WRONG!' 1400 write(stdunit,
'(a,i2,2(x,a,f20.16),x,a,1pe22.15)')
'k=',k,
'Po=',po(k),
'Ptrue=',ptrue(k),
'err=',po(k)-ptrue(k)
1408 logical function test_data1di(verbose, nk, Po, Ptrue, title)
1409 logical,
intent(in) :: verbose
1410 integer,
intent(in) :: nk
1411 integer,
dimension(nk),
intent(in) :: Po
1412 integer,
dimension(nk),
intent(in) :: Ptrue
1413 character(len=*),
intent(in) :: title
1416 integer :: k, stdunit
1426 write(stdunit,
'(a)') title
1428 if (po(k) /= ptrue(k))
then 1430 write(stdunit,
'(a,i2,2(x,a,i5),x,a)')
'k=',k,
'Io=',po(k),
'Itrue=',ptrue(k),
'WRONG!' 1433 write(stdunit,
'(a,i2,2(x,a,i5))')
'k=',k,
'Io=',po(k),
'Itrue=',ptrue(k)
1441 logical function test_nsp(verbose, nk, KoL, KoR, pL, pR, hEff, KoL0, KoR0, pL0, pR0, hEff0, title)
1442 logical,
intent(in) :: verbose
1443 integer,
intent(in) :: nk
1444 integer,
dimension(2*nk+2),
intent(in) :: KoL
1445 integer,
dimension(2*nk+2),
intent(in) :: KoR
1446 real,
dimension(2*nk+2),
intent(in) :: pL
1447 real,
dimension(2*nk+2),
intent(in) :: pR
1448 real,
dimension(2*nk+1),
intent(in) :: hEff
1449 integer,
dimension(2*nk+2),
intent(in) :: KoL0
1450 integer,
dimension(2*nk+2),
intent(in) :: KoR0
1451 real,
dimension(2*nk+2),
intent(in) :: pL0
1452 real,
dimension(2*nk+2),
intent(in) :: pR0
1453 real,
dimension(2*nk+1),
intent(in) :: hEff0
1454 character(len=*),
intent(in) :: title
1457 integer :: k, stdunit
1458 logical :: this_row_failed
1463 if (k < 2*nk+2)
then 1464 if (heff(k) /= heff0(k))
test_nsp = .true.
1471 write(stdunit,
'(a)') title
1473 this_row_failed =
compare_nsp_row(kol(k), kor(k), pl(k), pr(k), kol0(k), kor0(k), pl0(k), pr0(k))
1474 if (this_row_failed)
then 1475 write(stdunit,10) k,kol(k),pl(k),kor(k),pr(k),
' <-- WRONG!' 1476 write(stdunit,10) k,kol0(k),pl0(k),kor0(k),pr0(k),
' <-- should be this' 1478 write(stdunit,10) k,kol(k),pl(k),kor(k),pr(k)
1480 if (k < 2*nk+2)
then 1481 if (heff(k) /= heff0(k))
then 1482 write(stdunit,
'(i3,8x,"layer hEff =",2(f20.16,a))') k,heff(k),
" .neq. ",heff0(k),
' <-- WRONG!' 1484 write(stdunit,
'(i3,8x,"layer hEff =",f20.16)') k,heff(k)
1490 10
format(
"ks=",i3,
" kL=",i3,
" pL=",f20.16,
" kR=",i3,
" pR=",f20.16,a)
1494 logical function compare_nsp_row(KoL, KoR, pL, pR, KoL0, KoR0, pL0, pR0)
1495 integer,
intent(in) :: KoL
1496 integer,
intent(in) :: KoR
1497 real,
intent(in) :: pL
1498 real,
intent(in) :: pR
1499 integer,
intent(in) :: KoL0
1500 integer,
intent(in) :: KoR0
1501 real,
intent(in) :: pL0
1502 real,
intent(in) :: pR0
1515 if (
associated(cs))
deallocate(cs)
logical function test_data1d(verbose, nk, Po, Ptrue, title)
Returns true if comparison of Po and Ptrue fails, and conditionally writes results to stream...
real function, dimension(2 *n+2) absolute_positions(n, Pint, Karr, NParr)
Converts non-dimensional positions within layers to absolute positions (for debugging) ...
real function fv_diff(hkm1, hk, hkp1, Skm1, Sk, Skp1)
Returns the cell-centered second-order finite volume (unlimited PLM) slope using three consecutive ce...
Ocean grid type. See mom_grid for details.
logical function test_fv_diff(verbose, hkm1, hk, hkp1, Skm1, Sk, Skp1, Ptrue, title)
Returns true if a test of fv_diff() fails, and conditionally writes results to stream.
logical function test_data1di(verbose, nk, Po, Ptrue, title)
Returns true if comparison of Po and Ptrue fails, and conditionally writes results to stream...
subroutine neutral_surface_flux(nk, hl, hr, Tl, Tr, PiL, PiR, KoL, KoR, hEff, Flx)
Returns a single column of neutral diffusion fluxes of a tracer.
Provides the ocean grid type.
real function fvlsq_slope(hkm1, hk, hkp1, Skm1, Sk, Skp1)
Returns the cell-centered second-order weighted least squares slope using three consecutive cell widt...
logical function, public neutral_diffusion_unit_tests(verbose)
Returns true if unit tests of neutral_diffusion functions fail. Otherwise returns false...
real function ppm_ave(xL, xR, aL, aR, aMean)
Returns the average of a PPM reconstruction between two fractional positions.
logical function, public neutral_diffusion_init(Time, G, param_file, diag, CS)
Read parameters and allocate control structure for neutral_diffusion module.
subroutine interface_scalar(nk, h, S, Si, i_method)
Returns interface scalar, Si, for a column of layer values, S.
real function ppm_edge(hkm1, hk, hkp1, hkp2, Ak, Akp1, Pk, Pkp1)
Returns the PPM quasi-fourth order edge value at k+1/2 following equation 1.6 in Colella & Woodward...
logical function test_fvlsq_slope(verbose, hkm1, hk, hkp1, Skm1, Sk, Skp1, Ptrue, title)
Returns true if a test of fvlsq_slope() fails, and conditionally writes results to stream...
logical function test_ifndp(verbose, rhoNeg, Pneg, rhoPos, Ppos, Ptrue, title)
Returns true if a test of interpolate_for_nondim_position() fails, and conditionally writes results t...
subroutine, public neutral_diffusion(G, GV, h, Coef_x, Coef_y, Tracer, m, dt, name, CS)
Update tracer concentration due to neutral diffusion; layer thickness unchanged by this update...
subroutine, public calculate_compress(T, S, pressure, rho, drho_dp, start, npts, EOS)
Calls the appropriate subroutine to calculate the density and compressibility for 1-D array inputs...
real function absolute_position(n, Pint, Karr, NParr, k_surface)
Converts non-dimensional position within a layer to absolute position (for debugging) ...
This module contains the tracer_registry_type and the subroutines that handle registration of tracers...
subroutine plm_diff(nk, h, S, c_method, b_method, diff)
Returns PLM slopes for a column where the slopes are the difference in value across each cell...
subroutine, public calculate_density_derivs(T, S, pressure, drho_dT, drho_dS, start, npts, EOS)
Calls the appropriate subroutine to calculate density derivatives for 1-D array inputs.
subroutine, public closeparameterblock(CS)
subroutine, public neutral_diffusion_calc_coeffs(G, GV, h, T, S, EOS, CS)
Calculate remapping factors for u/v columns used to map adjoining columns to a shared coordinate spac...
subroutine, public openparameterblock(CS, blockName, desc)
Type to carry basic tracer information.
logical function, public is_root_pe()
subroutine find_neutral_surface_positions(nk, Pl, Tl, Sl, dRdTl, dRdSl, Pr, Tr, Sr, dRdTr, dRdSr, PoL, PoR, KoL, KoR, hEff)
Returns positions within left/right columns of combined interfaces.
subroutine, public mom_mesg(message, verb, all_print)
Provides subroutines for quantities specific to the equation of state.
real function interpolate_for_nondim_position(dRhoNeg, Pneg, dRhoPos, Ppos)
Returns the non-dimensional position between Pneg and Ppos where the interpolated density difference ...
subroutine, public neutral_diffusion_end(CS)
Deallocates neutral_diffusion control structure.
logical, parameter debug_this_module
A column-wise toolbox for implementing neutral diffusion.
real function signum(a, x)
A true signum function that returns either -abs(a), when x<0; or abs(a) when x>0; or 0 when x=0...
logical function test_nsp(verbose, nk, KoL, KoR, pL, pR, hEff, KoL0, KoR0, pL0, pR0, hEff0, title)
Returns true if output of find_neutral_surface_positions() does not match correct values...
subroutine, public mom_error(level, message, all_print)
subroutine, public neutral_diffusion_diag_init(Time, G, diag, C_p, Reg, CS)
Diagnostic handles for neutral diffusion tendencies.
logical function compare_nsp_row(KoL, KoR, pL, pR, KoL0, KoR0, pL0, pR0)
Compares a single row, k, of output from find_neutral_surface_positions()
A control structure for the equation of state.