15 implicit none ;
private 17 #include <MOM_memory.h> 19 public tracer_z_init, tracer_z_init_array, determine_temperature
30 function tracer_z_init(tr, h, filename, tr_name, G, US, missing_val, land_val)
31 logical :: tracer_z_init
34 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)), &
36 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)), &
38 character(len=*),
intent(in) :: filename
39 character(len=*),
intent(in) :: tr_name
41 real,
optional,
intent(in) :: missing_val
42 real,
optional,
intent(in) :: land_val
47 integer,
save :: init_calls = 0
49 #include "version_variable.h" 50 character(len=40) :: mdl =
"MOM_tracer_Z_init" 51 character(len=256) :: mesg
53 real,
allocatable,
dimension(:,:,:) :: &
55 real,
allocatable,
dimension(:) :: &
74 logical :: has_edges, use_missing, zero_surface
75 character(len=80) :: loc_msg
76 integer :: k_top, k_bot, k_bot_prev, k_start
77 integer :: i, j, k, kz, is, ie, js, je, nz, nz_in
78 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
80 landval = 0.0 ;
if (
present(land_val)) landval = land_val
82 zero_surface = .false.
85 if (
present(missing_val))
then 86 use_missing = .true. ; missing = missing_val
91 call read_z_edges(filename, tr_name, z_edges, nz_in, has_edges, use_missing, &
92 missing, scale=us%m_to_Z)
94 tracer_z_init = .false.
98 allocate(tr_in(g%isd:g%ied,g%jsd:g%jed,nz_in)) ; tr_in(:,:,:) = 0.0
99 allocate(tr_1d(nz_in)) ; tr_1d(:) = 0.0
100 call mom_read_data(filename, tr_name, tr_in(:,:,:), g%Domain)
104 if (
present(missing_val))
then 105 do j=js,je ;
do i=is,ie
106 if (g%mask2dT(i,j) == 0.0)
then 107 tr_in(i,j,1) = landval
108 elseif (abs(tr_in(i,j,1) - missing_val) <= 1e-6*abs(missing_val))
then 109 write(loc_msg,
'(f7.2," N ",f7.2," E")') g%geoLatT(i,j), g%geoLonT(i,j)
110 if (zero_surface)
then 111 call mom_error(warning,
"tracer_Z_init: Missing value of "// &
112 trim(tr_name)//
" found in an ocean point at "//trim(loc_msg)// &
113 " in "//trim(filename) )
116 call mom_error(fatal,
"tracer_Z_init: Missing value of "// &
117 trim(tr_name)//
" found in an ocean point at "//trim(loc_msg)// &
118 " in "//trim(filename) )
122 do k=2,nz_in ;
do j=js,je ;
do i=is,ie
123 if (abs(tr_in(i,j,k) - missing_val) <= 1e-6*abs(missing_val)) &
124 tr_in(i,j,k) = tr_in(i,j,k-1)
125 enddo ;
enddo ;
enddo 128 allocate(wt(nz_in+1)) ;
allocate(z1(nz_in+1)) ;
allocate(z2(nz_in+1))
134 do i=is,ie ; htot(i) = 0.0 ;
enddo 135 do k=1,nz ;
do i=is,ie ; htot(i) = htot(i) + h(i,j,k) ;
enddo ;
enddo 137 do i=is,ie ;
if (g%mask2dT(i,j)*htot(i) > 0.0)
then 139 dilate = (g%bathyT(i,j) - 0.0) / htot(i)
140 e(nz+1) = -g%bathyT(i,j)
141 do k=nz,1,-1 ; e(k) = e(k+1) + dilate * h(i,j,k) ;
enddo 144 do k=1,nz_in ; tr_1d(k) = tr_in(i,j,k) ;
enddo 145 k_bot = 1 ; k_bot_prev = -1
147 if (e(k+1) > z_edges(1))
then 149 elseif (e(k) < z_edges(nz_in+1))
then 150 tr(i,j,k) = tr_1d(nz_in)
153 call find_overlap(z_edges, e(k), e(k+1), nz_in, &
154 k_start, k_top, k_bot, wt, z1, z2)
156 if (kz /= k_bot_prev)
then 159 if ((kz < nz_in) .and. (kz > 1)) &
160 sl_tr = find_limited_slope(tr_1d, z_edges, kz)
163 tr(i,j,k) = wt(kz) * (tr_1d(kz) + 0.5*sl_tr*(z2(kz) + z1(kz)))
166 do kz=k_top+1,k_bot-1
167 tr(i,j,k) = tr(i,j,k) + wt(kz)*tr_1d(kz)
169 if (k_bot > k_top)
then 173 if ((kz < nz_in) .and. (kz > 1)) &
174 sl_tr = find_limited_slope(tr_1d, z_edges, kz)
176 tr(i,j,k) = tr(i,j,k) + wt(kz) * &
177 (tr_1d(kz) + 0.5*sl_tr*(z2(kz) + z1(kz)))
186 if ((e(k) > z_edges(1)) .and. (z_edges(nz_in+1) > e(k+1)))
then 187 tr(i,j,k) = (((e(k) - z_edges(1)) * tr_1d(1) + &
188 (z_edges(1) - z_edges(nz_in)) * tr(i,j,k)) + &
189 (z_edges(nz_in+1) - e(k+1)) * tr_1d(nz_in)) / &
191 elseif (e(k) > z_edges(1))
then 192 tr(i,j,k) = ((e(k) - z_edges(1)) * tr_1d(1) + &
193 (z_edges(1) - e(k+1)) * tr(i,j,k)) / &
195 elseif (z_edges(nz_in) > e(k+1))
then 196 tr(i,j,k) = ((e(k) - z_edges(nz_in+1)) * tr(i,j,k) + &
197 (z_edges(nz_in+1) - e(k+1)) * tr_1d(nz_in)) / &
203 do k=1,nz ; tr(i,j,k) = landval ;
enddo 209 do i=is,ie ; htot(i) = 0.0 ;
enddo 210 do k=1,nz ;
do i=is,ie ; htot(i) = htot(i) + h(i,j,k) ;
enddo ;
enddo 212 do i=is,ie ;
if (g%mask2dT(i,j)*htot(i) > 0.0)
then 214 dilate = (g%bathyT(i,j) - 0.0) / htot(i)
215 e(nz+1) = -g%bathyT(i,j)
216 do k=nz,1,-1 ; e(k) = e(k+1) + dilate * h(i,j,k) ;
enddo 219 do k=1,nz_in ; tr_1d(k) = tr_in(i,j,k) ;
enddo 222 if (e(k+1) > z_edges(1))
then 224 elseif (z_edges(nz_in) > e(k))
then 225 tr(i,j,k) = tr_1d(nz_in)
228 call find_overlap(z_edges, e(k), e(k+1), nz_in-1, &
229 k_start, k_top, k_bot, wt, z1, z2)
232 if (k_top < nz_in)
then 233 tr(i,j,k) = wt(kz)*0.5*((tr_1d(kz) + tr_1d(kz+1)) + &
234 (tr_1d(kz+1) - tr_1d(kz))*(z2(kz)+z1(kz)))
236 tr(i,j,k) = wt(kz)*tr_1d(nz_in)
238 do kz=k_top+1,k_bot-1
239 tr(i,j,k) = tr(i,j,k) + wt(kz)*0.5*(tr_1d(kz) + tr_1d(kz+1))
241 if (k_bot > k_top)
then 243 tr(i,j,k) = tr(i,j,k) + wt(kz)*0.5*((tr_1d(kz) + tr_1d(kz+1)) + &
244 (tr_1d(kz+1) - tr_1d(kz))*(z2(kz)+z1(kz)))
249 if ((e(k) > z_edges(1)) .and. (z_edges(nz_in) > e(k+1)))
then 250 tr(i,j,k) = (((e(k) - z_edges(1)) * tr_1d(1) + &
251 (z_edges(1) - z_edges(nz_in)) * tr(i,j,k)) + &
252 (z_edges(nz_in) - e(k+1)) * tr_1d(nz_in)) / &
254 elseif (e(k) > z_edges(1))
then 255 tr(i,j,k) = ((e(k) - z_edges(1)) * tr_1d(1) + &
256 (z_edges(1) - e(k+1)) * tr(i,j,k)) / &
258 elseif (z_edges(nz_in) > e(k+1))
then 259 tr(i,j,k) = ((e(k) - z_edges(nz_in)) * tr(i,j,k) + &
260 (z_edges(nz_in) - e(k+1)) * tr_1d(nz_in)) / &
266 do k=1,nz ; tr(i,j,k) = landval ;
enddo 271 deallocate(tr_in) ;
deallocate(tr_1d) ;
deallocate(z_edges)
272 deallocate(wt) ;
deallocate(z1) ;
deallocate(z2)
274 tracer_z_init = .true.
276 end function tracer_z_init
280 subroutine tracer_z_init_array(tr_in, z_edges, nk_data, e, land_fill, G, nlay, nlevs, &
283 integer,
intent(in) :: nk_data
284 real,
dimension(SZI_(G),SZJ_(G),nk_data), &
286 real,
dimension(nk_data+1),
intent(in) :: z_edges
288 integer,
intent(in) :: nlay
289 real,
dimension(SZI_(G),SZJ_(G),nlay+1), &
291 real,
intent(in) :: land_fill
292 integer,
dimension(SZI_(G),SZJ_(G)), &
294 real,
intent(in) :: eps_z
295 real,
dimension(SZI_(G),SZJ_(G),nlay), &
299 real,
dimension(nk_data) :: tr_1d
300 real,
dimension(nlay+1) :: e_1d
301 real,
dimension(nlay) :: tr_
302 integer :: k_top, k_bot, k_bot_prev, kstart
304 real,
dimension(nk_data) :: wt
305 real,
dimension(nk_data) :: z1, z2
309 integer :: i, j, k, kz, is, ie, js, je
311 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
315 if (nlevs(i,j) == 0 .or. g%mask2dT(i,j) == 0.)
then 316 tr(i,j,:) = land_fill
321 tr_1d(k) = tr_in(i,j,k)
327 k_bot = 1 ; k_bot_prev = -1
329 if (e_1d(k+1) > z_edges(1))
then 331 elseif (e_1d(k) < z_edges(nlevs(i,j)+1))
then 332 tr(i,j,k) = tr_1d(nlevs(i,j))
336 call find_overlap(z_edges, e_1d(k), e_1d(k+1), nlevs(i,j), &
337 kstart, k_top, k_bot, wt, z1, z2)
340 if (kz /= k_bot_prev)
then 342 if ((kz < nlevs(i,j)) .and. (kz > 1))
then 343 sl_tr = find_limited_slope(tr_1d, z_edges, kz)
346 if (kz > nlevs(i,j)) kz = nlevs(i,j)
348 tr(i,j,k) = wt(kz) * (tr_1d(kz) + 0.5*sl_tr*(z2(kz) + z1(kz)))
351 do kz=k_top+1,k_bot-1
352 tr(i,j,k) = tr(i,j,k) + wt(kz)*tr_1d(kz)
355 if (k_bot > k_top)
then 359 if ((kz < nlevs(i,j)) .and. (kz > 1))
then 360 sl_tr = find_limited_slope(tr_1d, z_edges, kz)
363 tr(i,j,k) = tr(i,j,k) + wt(kz) * (tr_1d(kz) + 0.5*sl_tr*(z2(kz) + z1(kz)))
373 if (e_1d(k)-e_1d(k+1) <= eps_z) tr(i,j,k) = tr(i,j,k-1)
379 end subroutine tracer_z_init_array
383 subroutine read_z_edges(filename, tr_name, z_edges, nz_out, has_edges, &
384 use_missing, missing, scale)
385 character(len=*),
intent(in) :: filename
386 character(len=*),
intent(in) :: tr_name
387 real,
dimension(:),
allocatable, &
388 intent(out) :: z_edges
389 integer,
intent(out) :: nz_out
390 logical,
intent(out) :: has_edges
392 logical,
intent(inout) :: use_missing
394 real,
intent(inout) :: missing
395 real,
intent(in) :: scale
399 character(len=32) :: mdl
400 character(len=120) :: dim_name, edge_name, tr_msg, dim_msg
402 integer :: ncid, status, intid, tr_id, layid, k
403 integer :: nz_edge, ndim, tr_dim_ids(NF90_MAX_VAR_DIMS)
405 mdl =
"MOM_tracer_Z_init read_Z_edges: " 406 tr_msg = trim(tr_name)//
" in "//trim(filename)
408 status = nf90_open(filename, nf90_nowrite, ncid)
409 if (status /= nf90_noerr)
then 410 call mom_error(warning,mdl//
" Difficulties opening "//trim(filename)//&
411 " - "//trim(nf90_strerror(status)))
415 status = nf90_inq_varid(ncid, tr_name, tr_id)
416 if (status /= nf90_noerr)
then 417 call mom_error(warning,mdl//
" Difficulties finding variable "//&
418 trim(tr_msg)//
" - "//trim(nf90_strerror(status)))
419 nz_out = -1 ; status = nf90_close(ncid) ;
return 421 status = nf90_inquire_variable(ncid, tr_id, ndims=ndim, dimids=tr_dim_ids)
422 if (status /= nf90_noerr)
then 423 call mom_error(warning,mdl//
" cannot inquire about "//trim(tr_msg))
424 elseif ((ndim < 3) .or. (ndim > 4))
then 425 call mom_error(warning,mdl//
" "//trim(tr_msg)//&
426 " has too many or too few dimensions.")
427 nz_out = -1 ; status = nf90_close(ncid) ;
return 430 if (.not.use_missing)
then 432 status = nf90_get_att(ncid, tr_id,
"missing_value", missing)
433 if (status /= nf90_noerr) use_missing = .true.
437 status = nf90_inquire_dimension(ncid, tr_dim_ids(3), dim_name, len=nz_out)
438 if (status /= nf90_noerr)
then 439 call mom_error(warning,mdl//
" cannot inquire about dimension(3) of "//&
443 dim_msg = trim(dim_name)//
" in "//trim(filename)
444 status = nf90_inq_varid(ncid, dim_name, layid)
445 if (status /= nf90_noerr)
then 446 call mom_error(warning,mdl//
" Difficulties finding variable "//&
447 trim(dim_msg)//
" - "//trim(nf90_strerror(status)))
448 nz_out = -1 ; status = nf90_close(ncid) ;
return 451 status = nf90_get_att(ncid, layid,
"edges", edge_name)
452 if (status /= nf90_noerr)
then 453 call mom_mesg(mdl//
" "//trim(dim_msg)//&
454 " has no readable edges attribute - "//trim(nf90_strerror(status)))
458 status = nf90_inq_varid(ncid, edge_name, intid)
459 if (status /= nf90_noerr)
then 460 call mom_error(warning,mdl//
" Difficulties finding edge variable "//&
461 trim(edge_name)//
" in "//trim(filename)//
" - "//trim(nf90_strerror(status)))
466 nz_edge = nz_out ;
if (has_edges) nz_edge = nz_out+1
467 allocate(z_edges(nz_edge)) ; z_edges(:) = 0.0
469 if (nz_out < 1)
return 473 dim_msg = trim(edge_name)//
" in "//trim(filename)
474 status = nf90_get_var(ncid, intid, z_edges)
475 if (status /= nf90_noerr)
then 476 call mom_error(warning,mdl//
" Difficulties reading variable "//&
477 trim(dim_msg)//
" - "//trim(nf90_strerror(status)))
478 nz_out = -1 ; status = nf90_close(ncid) ;
return 481 status = nf90_get_var(ncid, layid, z_edges)
482 if (status /= nf90_noerr)
then 483 call mom_error(warning,mdl//
" Difficulties reading variable "//&
484 trim(dim_msg)//
" - "//trim(nf90_strerror(status)))
485 nz_out = -1 ; status = nf90_close(ncid) ;
return 489 status = nf90_close(ncid)
490 if (status /= nf90_noerr)
call mom_error(warning, mdl// &
491 " Difficulties closing "//trim(filename)//
" - "//trim(nf90_strerror(status)))
495 if (z_edges(1) < z_edges(2))
then 496 do k=1,nz_edge ; z_edges(k) = -z_edges(k) ;
enddo 500 do k=2,nz_edge ;
if (z_edges(k) >= z_edges(k-1)) monotonic = .false. ;
enddo 501 if (.not.monotonic) &
502 call mom_error(warning,mdl//
" "//trim(dim_msg)//
" is not monotonic.")
504 if (scale /= 1.0)
then ;
do k=1,nz_edge ; z_edges(k) = scale*z_edges(k) ;
enddo ;
endif 506 end subroutine read_z_edges
517 subroutine find_overlap(e, Z_top, Z_bot, k_max, k_start, k_top, k_bot, wt, z1, z2)
518 real,
dimension(:),
intent(in) :: e
519 real,
intent(in) :: Z_top
520 real,
intent(in) :: Z_bot
521 integer,
intent(in) :: k_max
522 integer,
intent(in) :: k_start
523 integer,
intent(out) :: k_top
524 integer,
intent(out) :: k_bot
525 real,
dimension(:),
intent(out) :: wt
526 real,
dimension(:),
intent(out) :: z1
529 real,
dimension(:),
intent(out) :: z2
533 real :: Ih, e_c, tot_wt, I_totwt
536 wt(:) = 0.0 ; z1(:) = 0.0 ; z2(:) = 0.0 ; k_bot = k_max
538 do k=k_start,k_max ;
if (e(k+1) < z_top)
exit ;
enddo 540 if (k_top > k_max)
return 544 if (e(k+1) <= z_bot)
then 545 wt(k) = 1.0 ; k_bot = k
546 ih = 0.0 ;
if (e(k) /= e(k+1)) ih = 1.0 / (e(k)-e(k+1))
547 e_c = 0.5*(e(k)+e(k+1))
548 z1(k) = (e_c - min(e(k), z_top)) * ih
549 z2(k) = (e_c - z_bot) * ih
551 wt(k) = min(e(k),z_top) - e(k+1) ; tot_wt = wt(k)
552 if (e(k) /= e(k+1))
then 553 z1(k) = (0.5*(e(k)+e(k+1)) - min(e(k), z_top)) / (e(k)-e(k+1))
554 else ; z1(k) = -0.5 ;
endif 558 if (e(k+1) <= z_bot)
then 560 wt(k) = e(k) - z_bot ; z1(k) = -0.5
561 if (e(k) /= e(k+1))
then 562 z2(k) = (0.5*(e(k)+e(k+1)) - z_bot) / (e(k)-e(k+1))
563 else ; z2(k) = 0.5 ;
endif 565 wt(k) = e(k) - e(k+1) ; z1(k) = -0.5 ; z2(k) = 0.5
567 tot_wt = tot_wt + wt(k)
571 i_totwt = 0.0 ;
if (tot_wt > 0.0) i_totwt = 1.0 / tot_wt
572 do k=k_top,k_bot ; wt(k) = i_totwt*wt(k) ;
enddo 575 end subroutine find_overlap
579 function find_limited_slope(val, e, k)
result(slope)
580 real,
dimension(:),
intent(in) :: val
581 real,
dimension(:),
intent(in) :: e
582 integer,
intent(in) :: k
588 if ((val(k)-val(k-1)) * (val(k)-val(k+1)) >= 0.0)
then 591 d1 = 0.5*(e(k-1)-e(k+1)) ; d2 = 0.5*(e(k)-e(k+2))
592 if (d1*d2 > 0.0)
then 593 slope = ((d1**2)*(val(k+1) - val(k)) + (d2**2)*(val(k) - val(k-1))) * &
594 (e(k) - e(k+1)) / (d1*d2*(d1+d2))
597 amn = min(abs(slope), 2.0*(max(val(k-1), val(k), val(k+1)) - val(k)))
598 cmn = 2.0*(val(k) - min(val(k-1), val(k), val(k+1)))
599 slope = sign(1.0, slope) * min(amn, cmn)
609 end function find_limited_slope
613 subroutine determine_temperature(temp, salt, R_tgt, p_ref, niter, land_fill, h, k_start, G, US, eos, h_massless)
615 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)), &
616 intent(inout) :: temp
617 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)), &
618 intent(inout) :: salt
619 real,
dimension(SZK_(G)),
intent(in) :: r_tgt
620 real,
intent(in) :: p_ref
621 integer,
intent(in) :: niter
622 integer,
intent(in) :: k_start
623 real,
intent(in) :: land_fill
624 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)), &
629 real,
optional,
intent(in) :: h_massless
632 real,
parameter :: t_max = 31.0, t_min = -2.0
634 real,
dimension(SZI_(G),SZK_(G)) :: &
640 real,
dimension(SZI_(G)) :: press
645 logical :: adjust_salt, old_fit
650 real :: max_t_adj, max_s_adj
651 integer,
dimension(2) :: eosdom
652 integer :: i, j, k, kz, is, ie, js, je, nz, itt
654 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
657 s_min = 0.5 ; s_max = 65.0
658 max_t_adj = 1.0 ; max_s_adj = 0.5
659 tol_t=1.e-4 ; tol_s=1.e-4 ; tol_rho = 1.e-4*us%kg_m3_to_R
669 eosdom(:) = eos_domain(g%HI)
678 iter_loop:
do itt = 1,niter
684 do k=k_start,nz ;
do i=is,ie
686 if (abs(rho(i,k)-r_tgt(k))>tol_rho)
then 688 dt(i,k) = max(min((r_tgt(k)-rho(i,k)) / drho_dt(i,k), max_t_adj), -max_t_adj)
689 t(i,k) = max(min(t(i,k)+dt(i,k), t_max), t_min)
692 i_denom = 1.0 / (drho_ds(i,k)**2 + dt_ds_gauge**2*drho_dt(i,k)**2)
693 ds(i,k) = (r_tgt(k)-rho(i,k)) * drho_ds(i,k) * i_denom
694 dt(i,k) = (r_tgt(k)-rho(i,k)) * dt_ds_gauge**2*drho_dt(i,k) * i_denom
696 t(i,k) = max(min(t(i,k)+dt(i,k), t_max), t_min)
697 s(i,k) = max(min(s(i,k)+ds(i,k), s_max), s_min)
701 if (maxval(abs(dt)) < tol_t)
then 702 adjust_salt = .false.
707 if (adjust_salt .and. old_fit)
then ;
do itt = 1,niter
713 do k=k_start,nz ;
do i=is,ie
715 if (abs(rho(i,k)-r_tgt(k)) > tol_rho)
then 716 ds(i,k) = max(min((r_tgt(k)-rho(i,k)) / drho_ds(i,k), max_s_adj), -max_s_adj)
717 s(i,k) = max(min(s(i,k)+ds(i,k), s_max), s_min)
720 if (maxval(abs(ds)) < tol_s)
exit 727 end subroutine determine_temperature
Used to initialize tracers from a depth- (or z*-) space file.
Ocean grid type. See mom_grid for details.
Calculates density of sea water from T, S and P.
Provides the ocean grid type.
This module contains I/O framework code.
Describes various unit conversion factors.
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Routines for error handling and I/O management.
Calculate the derivatives of density with temperature and salinity from T, S, and P...
Provides subroutines for quantities specific to the equation of state.
Read a data field from a file.
A control structure for the equation of state.