Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 77 additions & 1 deletion src/tracer/MARBL_tracers.F90
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ module MARBL_tracers
!! so currently using temp_MARBL_diag for that.
integer, allocatable :: id_surface_flux_out(:) !< register_diag indices for surface_flux output
integer, allocatable :: id_surface_flux_from_salt_flux(:) !< register_diag indices for surface_flux from salt_flux
integer, allocatable :: id_boundary_forcing_content_tend(:) !< register_diag indices for tracer content tendency
!! from boundary forcing
integer, allocatable :: id_diabatic_content_tend(:) !< register_diag indices for tracer content tendency
!! from diabatic diffusion
integer, allocatable :: id_interior_content_tend(:) !< register_diag indices for tracer content tendency
!! from MARBL interior tendencies
type(temp_MARBL_diag), allocatable :: interior_tendency_out(:) !< collect interior tendencies for diagnostic output
type(temp_MARBL_diag), allocatable :: interior_tendency_out_zint(:) !< vertical integral of interior tendencies
!! (full column)
Expand Down Expand Up @@ -958,6 +964,9 @@ subroutine initialize_MARBL_tracers(restart, day, G, GV, US, h, param_file, diag
! Register per-tracer diagnostics computed from MARBL surface flux / interior tendency values
allocate(CS%id_surface_flux_out(CS%ntr))
allocate(CS%id_surface_flux_from_salt_flux(CS%ntr))
allocate(CS%id_boundary_forcing_content_tend(CS%ntr))
allocate(CS%id_diabatic_content_tend(CS%ntr))
allocate(CS%id_interior_content_tend(CS%ntr))
allocate(CS%interior_tendency_out(CS%ntr))
allocate(CS%interior_tendency_out_zint(CS%ntr))
allocate(CS%interior_tendency_out_zint_100m(CS%ntr))
Expand All @@ -970,11 +979,39 @@ subroutine initialize_MARBL_tracers(restart, day, G, GV, US, h, param_file, diag
day, trim(longname), trim(units), conversion=US%Z_to_m*US%s_to_T)

write(name, "(2A)") "STF_SALT_", trim(MARBL_instances%tracer_metadata(m)%short_name)
write(longname, "(2A)") trim(MARBL_instances%tracer_metadata(m)%long_name), " Surface Flux from Salt Flux"
write(longname, "(2A)") trim(MARBL_instances%tracer_metadata(m)%long_name), &
" Surface Flux from Salt Flux"
CS%id_surface_flux_from_salt_flux(m) = register_diag_field("ocean_model", trim(name), &
diag%axesT1, & ! T => tracer grid? 1 => no vertical grid
day, trim(longname), trim(units), conversion=US%Z_to_m*US%s_to_T)

write(name, "(3A)") "boundary_forcing_", trim(MARBL_instances%tracer_metadata(m)%short_name), &
"_cont_tendency"
write(longname, "(3A)") "Boundary forcing ", &
trim(MARBL_instances%tracer_metadata(m)%short_name), " Layer Content Tendency"
write(units, "(2A)") trim(MARBL_instances%tracer_metadata(m)%units), " m/s"
CS%id_boundary_forcing_content_tend(m) = register_diag_field("ocean_model", trim(name), &
diag%axesTL, day, trim(longname), trim(units), v_extensive=.true., &
conversion=GV%H_to_m*US%s_to_T)

write(name, "(3A)") "diabatic_", trim(MARBL_instances%tracer_metadata(m)%short_name), &
"_cont_tendency"
write(longname, "(3A)") "Diabatic ", &
trim(MARBL_instances%tracer_metadata(m)%short_name), " Layer Content Tendency"
write(units, "(2A)") trim(MARBL_instances%tracer_metadata(m)%units), " m/s"
CS%id_diabatic_content_tend(m) = register_diag_field("ocean_model", trim(name), &
diag%axesTL, day, trim(longname), trim(units), v_extensive=.true., &
conversion=GV%H_to_m*US%s_to_T)

write(name, "(3A)") "interior_", trim(MARBL_instances%tracer_metadata(m)%short_name), &
"_cont_tendency"
write(longname, "(2A)") trim(MARBL_instances%tracer_metadata(m)%short_name), &
" Layer Content Tendency from Interior Source-Sink"
write(units, "(2A)") trim(MARBL_instances%tracer_metadata(m)%units), " m/s"
CS%id_interior_content_tend(m) = register_diag_field("ocean_model", trim(name), &
diag%axesTL, day, trim(longname), trim(units), v_extensive=.true., &
conversion=GV%H_to_m*US%s_to_T)

write(name, "(2A)") "J_", trim(MARBL_instances%tracer_metadata(m)%short_name)
write(longname, "(2A)") trim(MARBL_instances%tracer_metadata(m)%long_name), " Source Sink Term"
write(units, "(2A)") trim(MARBL_instances%tracer_metadata(m)%units), "/s"
Expand Down Expand Up @@ -1511,15 +1548,19 @@ subroutine MARBL_tracers_column_physics(h_old, ea, eb, fluxes, dt, G, GV, US, CS
real, dimension(SZI_(G),SZJ_(G)) :: ref_mask ! Mask for 2D MARBL diags using ref_depth [1]
real, dimension(SZI_(G),SZJ_(G)) :: riv_flux_loc ! Local copy of CS%RIV_FLUXES*dt [conc H ~> mmol m-2]
real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: h_work ! Used so that h can be modified [H ~> m or kg m-2]
real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: tr_work ! tracer value before process is applied [conc]
real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: cont_tend ! tracer content tend [conc m s-1 or conc kg m-2 s-1]
real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: bot_flux_to_tend ! Conversion factor for bottom tlux -> tend
! [Z-1 ~> m-1]
real :: cum_bftt_dz ! sum of bot_flux_to_tend * dz from the bottom layer to current layer [1]
real, dimension(0:GV%ke) :: zi ! z-coordinate interface depth [Z ~> m]
real, dimension(GV%ke) :: zc ! z-coordinate layer center depth [Z ~> m]
real, dimension(GV%ke) :: dz ! z-coordinate cell thickness [H ~> m]
real :: Idt ! The inverse of the timestep [T-1 ~> s-1]
integer :: i, j, k, is, ie, js, je, nz, m

is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
Idt = 0.0 ; if (dt > 0.0) Idt = 1. / dt

if (.not.associated(CS)) return

Expand Down Expand Up @@ -1953,14 +1994,37 @@ subroutine MARBL_tracers_column_physics(h_old, ea, eb, fluxes, dt, G, GV, US, CS
if (CS%debug) &
call hchksum(riv_flux_loc(:,:), &
trim(MARBL_instances%tracer_metadata(m)%short_name)//' riv flux', G%HI, unscale=GV%H_to_m)
if (CS%id_boundary_forcing_content_tend(m) > 0) then
do k=1,nz ;do j=js,je ; do i=is,ie
tr_work(i,j,k) = CS%tracer_data(m)%tr(i,j,k)
enddo ; enddo ; enddo
endif
call applyTracerBoundaryFluxesInOut(G, GV, CS%tracer_data(m)%tr(:,:,:) , dt, fluxes, h_work, &
evap_CFL_limit, minimum_forcing_depth, in_flux_optional=riv_flux_loc)
if (CS%id_boundary_forcing_content_tend(m) > 0) then
do k=1,nz ;do j=js,je ; do i=is,ie
cont_tend(i,j,k) = Idt * &
(h_work(i,j,k) * CS%tracer_data(m)%tr(i,j,k) - h_old(i,j,k) * tr_work(i,j,k))
enddo ; enddo ; enddo
call post_data(CS%id_boundary_forcing_content_tend(m), cont_tend, CS%diag, alt_h=h_old)
endif
if (CS%id_diabatic_content_tend(m) > 0) then
do k=1,nz ;do j=js,je ; do i=is,ie
tr_work(i,j,k) = CS%tracer_data(m)%tr(i,j,k)
enddo ; enddo ; enddo
endif
else
call applyTracerBoundaryFluxesInOut(G, GV, CS%tracer_data(m)%tr(:,:,:) , dt, fluxes, h_work, &
evap_CFL_limit, minimum_forcing_depth)
endif
call tracer_vertdiff(h_work, ea, eb, dt, CS%tracer_data(m)%tr(:,:,:), G, GV, &
sfc_flux=GV%Rho0 * CS%STF(:,:,m))
if (CS%id_diabatic_content_tend(m) > 0) then
do k=1,nz ;do j=js,je ; do i=is,ie
cont_tend(i,j,k) = Idt * (h_work(i,j,k) * (CS%tracer_data(m)%tr(i,j,k) - tr_work(i,j,k)))
enddo ; enddo ; enddo
call post_data(CS%id_diabatic_content_tend(m), cont_tend, CS%diag, alt_h=h_work)
endif
enddo
else
! TODO: do we want to support these options? does not apply river fluxes!
Expand Down Expand Up @@ -2028,6 +2092,12 @@ subroutine MARBL_tracers_column_physics(h_old, ea, eb, fluxes, dt, G, GV, US, CS
if (allocated(CS%interior_tendency_out(m)%field_3d)) &
call post_data(CS%interior_tendency_out(m)%id, &
CS%interior_tendency_out(m)%field_3d(:,:,:), CS%diag)
if (CS%id_interior_content_tend(m) > 0) then
do k=1,nz ;do j=js,je ; do i=is,ie
cont_tend(i,j,k) = h_old(i,j,k) * CS%interior_tendency_out(m)%field_3d(i,j,k)
enddo ; enddo ; enddo
call post_data(CS%id_interior_content_tend(m), cont_tend, CS%diag, alt_h=h_old)
endif
if (allocated(CS%interior_tendency_out_zint(m)%field_2d)) &
call post_data(CS%interior_tendency_out_zint(m)%id, &
CS%interior_tendency_out_zint(m)%field_2d(:,:), CS%diag)
Expand Down Expand Up @@ -2364,6 +2434,12 @@ subroutine MARBL_tracers_end(CS)
endif
if (allocated(CS%ind_tr)) deallocate(CS%ind_tr)
if (allocated(CS%id_surface_flux_out)) deallocate(CS%id_surface_flux_out)
if (allocated(CS%id_surface_flux_from_salt_flux)) &
deallocate(CS%id_surface_flux_from_salt_flux)
if (allocated(CS%id_boundary_forcing_content_tend)) &
deallocate(CS%id_boundary_forcing_content_tend)
if (allocated(CS%id_diabatic_content_tend)) deallocate(CS%id_diabatic_content_tend)
if (allocated(CS%id_interior_content_tend)) deallocate(CS%id_interior_content_tend)
if (allocated(CS%interior_tendency_out)) deallocate(CS%interior_tendency_out)
if (allocated(CS%interior_tendency_out_zint)) deallocate(CS%interior_tendency_out_zint)
if (allocated(CS%interior_tendency_out_zint_100m)) &
Expand Down
133 changes: 123 additions & 10 deletions src/tracer/MOM_tracer_registry.F90
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ subroutine register_tracer_diagnostics(Reg, h, Time, diag, G, GV, US, use_ALE, u
! [units] m3 s-1 or [units] kg s-1.
character(len=48) :: conv_units ! The units for flux convergences, either
! [units] m s-1 or [units] kg m-2 s-1.
character(len=48) :: content_units ! The units of layer content, either
! [units] m or [units] kg m-2.
character(len=48) :: unit2 ! The dimensions of the tracer squared
character(len=72) :: cmorname ! The CMOR name of this tracer.
character(len=120) :: cmor_longname ! The CMOR long name of that variable.
Expand Down Expand Up @@ -346,6 +348,25 @@ subroutine register_tracer_diagnostics(Reg, h, Time, diag, G, GV, US, use_ALE, u
elseif (GV%Boussinesq) then ; conv_units = trim(units)//" m s-1"
else ; conv_units = trim(units)//" kg m-2 s-1" ; endif

! content_units = conv_units + " s"
! Basing content_units on conv_units instead of units yields content values
! more easily relatable to convergence values when conv_units is specified,
! e.g. with "salt" where conv_units = "kg m-2 s-1".
! If conv_units ends with " s-1", remove that substring to construct content_units.
! Otherwise, just append " s", not attempting other simplifications.
i = index(conv_units, " s-1")
if ((i > 0) .and. (i + len(" s-1") == len_trim(conv_units) + 1)) then
! If there are only spaces before "s-1", the result is dimensionless, i.e. units= "1".
! Otherwise, take substring before " s-1".
if (len_trim(conv_units(1:i)) == 0) then
content_units = "1"
else
content_units = conv_units(1:i-1)
endif
else
content_units = trim(conv_units) // " s"
endif

if (len_trim(cmorname) == 0) then
Tr%id_tr = register_diag_field("ocean_model", trim(name), diag%axesTL, &
Time, trim(longname), trim(units), conversion=Tr%conc_scale)
Expand All @@ -359,6 +380,12 @@ subroutine register_tracer_diagnostics(Reg, h, Time, diag, G, GV, US, use_ALE, u
trim(name)//"_post_horzn", diag%axesTL, Time, &
trim(longname)//" after horizontal transport (advection/diffusion) has occurred", &
trim(units), conversion=Tr%conc_scale)
Tr%id_trxh = register_diag_field('ocean_model', trim(shortnm)//'_content', &
diag%axesTL, Time, "Layer integrated "//lowercase(longname), &
content_units, conversion=Tr%conv_scale, v_extensive=.true.)
Tr%id_trxh_2d = register_diag_field('ocean_model', trim(shortnm)//'_content_2d', &
diag%axesT1, Time, "Vertical sum of layer integrated "//lowercase(longname), &
content_units, conversion=Tr%conv_scale)
if (Tr%diag_form == 1) then
Tr%id_adx = register_diag_field("ocean_model", trim(shortnm)//"_adx", &
diag%axesCuL, Time, trim(flux_longname)//" advective zonal flux" , &
Expand Down Expand Up @@ -443,6 +470,14 @@ subroutine register_tracer_diagnostics(Reg, h, Time, diag, G, GV, US, use_ALE, u
diag%axesT1, Time, &
"Thickness-weighted integral of " // trim(longname), &
trim(units) // " m", conversion=Tr%conc_scale*US%Z_to_m)
Tr%id_mean_150m = register_diag_field("ocean_model", trim(shortnm)//"_mean_150m", &
diag%axesT1, Time, &
"Thickness-weighted average of "// trim(longname) // " over top 150m", &
trim(units), conversion=Tr%conc_scale)
Tr%id_zint_150m = register_diag_field("ocean_model", trim(shortnm)//"_zint_150m", &
diag%axesT1, Time, &
"Thickness-weighted integral of "// trim(longname) // " over top 150m", &
trim(units) // " m", conversion=Tr%conc_scale*US%Z_to_m)
Tr%id_zint_100m = register_diag_field("ocean_model", trim(shortnm)//"_zint_100m", &
diag%axesT1, Time, &
"Thickness-weighted integral of "// trim(longname) // " over top 100m", &
Expand Down Expand Up @@ -752,6 +787,19 @@ subroutine post_tracer_diagnostics_at_sync(Reg, h, diag_prev, diag, G, GV, dt)
do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
Tr => Reg%Tr(m)
if (Tr%id_tr > 0) call post_data(Tr%id_tr, Tr%t, diag)
if ((Tr%id_trxh > 0) .or. (Tr%id_trxh_2d > 0)) then
do k=1,nz ; do j=js,je ; do i=is,ie
work3d(i,j,k) = Tr%t(i,j,k)*h(i,j,k)
enddo ; enddo ; enddo
if (Tr%id_trxh > 0) call post_data(Tr%id_trxh, work3d, diag)
if (Tr%id_trxh_2d > 0) then
work2d(:,:) = 0.0
do k=1,nz ; do j=js,je ; do i=is,ie
work2d(i,j) = work2d(i,j) + work3d(i,j,k)
enddo ; enddo ; enddo
call post_data(Tr%id_trxh_2d, work2d, diag)
endif
endif
if (Tr%id_tendency > 0) then
work3d(:,:,:) = 0.0
do k=1,nz ; do j=js,je ; do i=is,ie
Expand Down Expand Up @@ -835,39 +883,89 @@ subroutine post_tracer_integral_diagnostics(G, GV, US, Reg, h_diag, tv, diag)
!! thermodynamic variables.
type(diag_ctrl), intent(in) :: diag !< structure to regulate diagnostic output

integer :: i, j, k, is, ie, js, je, nz, m, khi
real :: work2d(SZI_(G),SZJ_(G)) ! The vertically integrated tracer amounts [CU Z T-1 ~> conc m]
integer :: i, j, k, is, ie, js, je, nz, m, khi_150, khi_100
real :: I_denom_mean_150m(SZI_(G),SZJ_(G)) ! Inverse of denominator for computing mean over top 150m [Z ~> m]
real :: work2d(SZI_(G),SZJ_(G)) ! The vertically integrated tracer amounts [CU Z ~> conc m]
real :: dz(SZI_(G),SZJ_(G),SZK_(GV)) !< Geometric layer thicknesses in height units [Z ~> m]
real :: frac_under_150m(SZI_(G),SZJ_(G),SZK_(GV)) ! weights used to compute 150m vertical integrals [nondim]
real :: frac_under_100m(SZI_(G),SZJ_(G),SZK_(GV)) ! weights used to compute 100m vertical integrals [nondim]
real :: ztop(SZI_(G),SZJ_(G)) ! position of the top interface [Z ~> m]
real :: zbot(SZI_(G),SZJ_(G)) ! position of the bottom interface [Z ~> m]
real :: Z_150 ! 150 m in depth units [Z ~> m]
real :: Z_100 ! 100 m in depth units [Z ~> m]
logical :: dz_needed, dz100_used
logical :: dz_needed, comp_mean_150m_denom, dz150_used, dz100_used
type(tracer_type), pointer :: Tr=>NULL()

is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke

dz_needed = .false.
comp_mean_150m_denom = .false.
dz150_used = .false.
dz100_used = .false.
do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
if (Reg%Tr(m)%id_zint_100m > 0) dz100_used = .true.
if (Reg%Tr(m)%id_zint > 0) dz_needed = .true.
if (Reg%Tr(m)%id_mean_150m > 0) then
comp_mean_150m_denom = .true.
dz150_used = .true.
endif
if (Reg%Tr(m)%id_zint_150m > 0) dz150_used = .true.
if (Reg%Tr(m)%id_zint_100m > 0) dz100_used = .true.
endif ; enddo
if (dz100_used) dz_needed = .true.
if (dz150_used .or. dz100_used) dz_needed = .true.

if (dz_needed) then
! Convert the layer thicknesses into geometric depths, using the pre-stored layer-mean specific
! volumes when in non-Boussinesq mode.
call thickness_to_dz(h_diag, tv, dz, G, GV, US)
endif

if (dz150_used) then
! If any tracers are posting 150m vertical integrals, compute weights
frac_under_150m(:,:,:) = 0.0
! khi_150 will be the largest layer index corresponding where ztop < 150m and ztop >= 150m
! in any column (we can reduce computation of 150m integrals by only looping through khi_150
! rather than GV%ke)
khi_150 = 0

Z_150 = 150.0*US%m_to_Z
I_denom_mean_150m(:,:) = 0.0
zbot(:,:) = 0.0
do k=1,nz
do j=js,je ; do i=is,ie
ztop(i,j) = zbot(i,j)
zbot(i,j) = ztop(i,j) + dz(i,j,k)
if (zbot(i,j) <= Z_150) then
frac_under_150m(i,j,k) = 1.0
elseif (ztop(i,j) < Z_150) then
frac_under_150m(i,j,k) = (Z_150 - ztop(i,j)) / (zbot(i,j) - ztop(i,j))
else
frac_under_150m(i,j,k) = 0.0
endif
! frac_under_150m(i,j,k) = max(0, min(1.0, (Z_150 - ztop(i,j)) / (zbot(i,j) - ztop(i,j))))
enddo ; enddo
if (any(frac_under_150m(:,:,k) > 0)) then
khi_150 = k
if (comp_mean_150m_denom) then
do j=js,je ; do i=is,ie
I_denom_mean_150m(i,j) = I_denom_mean_150m(i,j) + frac_under_150m(i,j,k) * dz(i,j,k)
enddo ; enddo
endif
endif
enddo
if (comp_mean_150m_denom) then
do j=js,je ; do i=is,ie
if (I_denom_mean_150m(i,j) > 0.0) I_denom_mean_150m(i,j) = 1.0 / I_denom_mean_150m(i,j)
enddo ; enddo
endif
endif

if (dz100_used) then
! If any tracers are posting 100m vertical integrals, compute weights
frac_under_100m(:,:,:) = 0.0
! khi will be the largest layer index corresponding where ztop < 100m and ztop >= 100m
! in any column (we can reduce computation of 100m integrals by only looping through khi
! khi_100 will be the largest layer index corresponding where ztop < 100m and ztop >= 100m
! in any column (we can reduce computation of 100m integrals by only looping through khi_100
! rather than GV%ke)
khi = 0
khi_100 = 0

Z_100 = 100.0*US%m_to_Z
zbot(:,:) = 0.0
Expand All @@ -884,7 +982,7 @@ subroutine post_tracer_integral_diagnostics(G, GV, US, Reg, h_diag, tv, diag)
endif
! frac_under_100m(i,j,k) = max(0, min(1.0, (Z_100 - ztop(i,j)) / (zbot(i,j) - ztop(i,j))))
enddo ; enddo
if (any(frac_under_100m(:,:,k) > 0)) khi = k
if (any(frac_under_100m(:,:,k) > 0)) khi_100 = k
enddo
endif

Expand All @@ -900,10 +998,25 @@ subroutine post_tracer_integral_diagnostics(G, GV, US, Reg, h_diag, tv, diag)
call post_data(Tr%id_zint, work2d, diag)
endif

! Compute 150m vertical mean and integral
if ((Tr%id_mean_150m > 0) .or. (Tr%id_zint_150m > 0)) then
work2d(:,:) = 0.0
do k=1,khi_150 ; do j=js,je ; do i=is,ie
work2d(i,j) = work2d(i,j) + frac_under_150m(i,j,k) * dz(i,j,k)*Tr%t(i,j,k)
enddo ; enddo ; enddo
if (Tr%id_zint_150m > 0) call post_data(Tr%id_zint_150m, work2d, diag)
if (Tr%id_mean_150m > 0) then
do j=js,je ; do i=is,ie
work2d(i,j) = I_denom_mean_150m(i,j) * work2d(i,j)
enddo ; enddo
call post_data(Tr%id_mean_150m, work2d, diag)
endif
endif

! Compute 100m vertical integral
if (Tr%id_zint_100m > 0) then
work2d(:,:) = 0.0
do k=1,khi ; do j=js,je ; do i=is,ie
do k=1,khi_100 ; do j=js,je ; do i=is,ie
work2d(i,j) = work2d(i,j) + frac_under_100m(i,j,k) * dz(i,j,k)*Tr%t(i,j,k)
enddo ; enddo ; enddo
call post_data(Tr%id_zint_100m, work2d, diag)
Expand Down
Loading
Loading