← back to index

src/tracer/MOM_tracer_registry.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> This module contains subroutines that handle registration of tracers
6!! and related subroutines. The primary subroutine, register_tracer, is
7!! called to indicate the tracers advected and diffused.
8!! It also makes public the types defined in MOM_tracer_types.
9module MOM_tracer_registry
10
11! use MOM_diag_mediator, only : diag_ctrl
12use MOM_coms, only : reproducing_sum
13use MOM_debugging, only : hchksum
14use MOM_diag_mediator, only : diag_ctrl, register_diag_field, post_data, safe_alloc_ptr
15use MOM_diag_mediator, only : diag_grid_storage
16use MOM_diag_mediator, only : diag_copy_storage_to_diag, diag_save_grids, diag_restore_grids
17use MOM_error_handler, only : MOM_error, FATAL, WARNING, MOM_mesg, is_root_pe
18use MOM_file_parser, only : get_param, log_version, param_file_type
19use MOM_hor_index, only : hor_index_type
20use MOM_grid, only : ocean_grid_type
21use MOM_io, only : vardesc, query_vardesc, cmor_long_std
22use MOM_restart, only : register_restart_field, MOM_restart_CS
23use MOM_string_functions, only : lowercase
24use MOM_time_manager, only : time_type
25use MOM_unit_scaling, only : unit_scale_type
26use MOM_verticalGrid, only : verticalGrid_type
27use MOM_tracer_types, only : tracer_type, tracer_registry_type
28
29implicit none ; private
30
31#include <MOM_memory.h>
32
33public register_tracer
34public MOM_tracer_chksum, MOM_tracer_chkinv
35public register_tracer_diagnostics
36public post_tracer_diagnostics_at_sync, post_tracer_transport_diagnostics
37public preALE_tracer_diagnostics, postALE_tracer_diagnostics
38public tracer_registry_init, lock_tracer_registry, tracer_registry_end
39public tracer_name_lookup
40public tracer_type, tracer_registry_type
41
42!> Write out checksums for registered tracers
43interface MOM_tracer_chksum
44 module procedure tracer_array_chksum, tracer_Reg_chksum
45end interface MOM_tracer_chksum
46
47!> Calculate and print the global inventories of registered tracers
48interface MOM_tracer_chkinv
49 module procedure tracer_array_chkinv, tracer_Reg_chkinv
50end interface MOM_tracer_chkinv
51
52contains
53
54!> This subroutine registers a tracer to be advected and laterally diffused.
553subroutine register_tracer(tr_ptr, Reg, param_file, HI, GV, name, longname, units, &
56 cmor_name, cmor_units, cmor_longname, net_surfflux_name, &
57 NLT_budget_name, net_surfflux_longname, tr_desc, OBC_inflow, &
58 OBC_in_u, OBC_in_v, ad_x, ad_y, df_x, df_y, ad_2d_x, ad_2d_y, &
59 df_2d_x, df_2d_y, advection_xy, registry_diags, &
60 conc_scale, flux_nameroot, flux_longname, flux_units, flux_scale, &
61 convergence_units, convergence_scale, cmor_tendprefix, diag_form, &
62 restart_CS, mandatory, underflow_conc, Tr_out, advect_scheme)
63 type(hor_index_type), intent(in) :: HI !< horizontal index type
64 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
65 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
66 real, dimension(SZI_(HI),SZJ_(HI),SZK_(GV)), &
67 target :: tr_ptr !< target or pointer to the tracer array [CU ~> conc]
68 type(param_file_type), intent(in) :: param_file !< file to parse for model parameter values
69 character(len=*), optional, intent(in) :: name !< Short tracer name
70 character(len=*), optional, intent(in) :: longname !< The long tracer name
71 character(len=*), optional, intent(in) :: units !< The units of this tracer
72 character(len=*), optional, intent(in) :: cmor_name !< CMOR name
73 character(len=*), optional, intent(in) :: cmor_units !< CMOR physical dimensions of variable
74 character(len=*), optional, intent(in) :: cmor_longname !< CMOR long name
75 character(len=*), optional, intent(in) :: net_surfflux_name !< Name for net_surfflux diag
76 character(len=*), optional, intent(in) :: NLT_budget_name !< Name for NLT_budget diag
77 character(len=*), optional, intent(in) :: net_surfflux_longname !< Long name for net_surfflux diag
78 type(vardesc), optional, intent(in) :: tr_desc !< A structure with metadata about the tracer
79
80 real, optional, intent(in) :: OBC_inflow !< the tracer for all inflows via OBC for which OBC_in_u
81 !! or OBC_in_v are not specified [CU ~> conc]
82 real, dimension(:,:,:), optional, pointer :: OBC_in_u !< tracer at inflows through u-faces of
83 !! tracer cells [CU ~> conc]
84 real, dimension(:,:,:), optional, pointer :: OBC_in_v !< tracer at inflows through v-faces of
85 !! tracer cells [CU ~> conc]
86
87 ! The following are probably not necessary if registry_diags is present and true.
88 real, dimension(:,:,:), optional, pointer :: ad_x !< diagnostic x-advective flux
89 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
90 real, dimension(:,:,:), optional, pointer :: ad_y !< diagnostic y-advective flux
91 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
92 real, dimension(:,:,:), optional, pointer :: df_x !< diagnostic x-diffusive flux
93 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
94 real, dimension(:,:,:), optional, pointer :: df_y !< diagnostic y-diffusive flux
95 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
96 real, dimension(:,:), optional, pointer :: ad_2d_x !< vert sum of diagnostic x-advect flux
97 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
98 real, dimension(:,:), optional, pointer :: ad_2d_y !< vert sum of diagnostic y-advect flux
99 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
100 real, dimension(:,:), optional, pointer :: df_2d_x !< vert sum of diagnostic x-diffuse flux
101 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
102 real, dimension(:,:), optional, pointer :: df_2d_y !< vert sum of diagnostic y-diffuse flux
103 !! [CU H L2 T-1 ~> conc m3 s-1 or conc kg s-1]
104
105 real, dimension(:,:,:), optional, pointer :: advection_xy !< convergence of lateral advective tracer fluxes
106 !! [CU H T-1 ~> conc m s-1 or conc kg m-2 s-1]
107 logical, optional, intent(in) :: registry_diags !< If present and true, use the registry for
108 !! the diagnostics of this tracer.
109 real, optional, intent(in) :: conc_scale !< A scaling factor used to convert the concentration
110 !! of this tracer to its desired units [conc CU-1 ~> 1]
111 character(len=*), optional, intent(in) :: flux_nameroot !< Short tracer name snippet used construct the
112 !! names of flux diagnostics.
113 character(len=*), optional, intent(in) :: flux_longname !< A word or phrase used construct the long
114 !! names of flux diagnostics.
115 character(len=*), optional, intent(in) :: flux_units !< The units for the fluxes of this tracer.
116 real, optional, intent(in) :: flux_scale !< A scaling factor used to convert the fluxes
117 !! of this tracer to its desired units
118 !! [conc m CU-1 H-1 ~> 1] or [conc kg m-2 CU-1 H-1 ~> 1]
119 character(len=*), optional, intent(in) :: convergence_units !< The units for the flux convergence of
120 !! this tracer.
121 real, optional, intent(in) :: convergence_scale !< A scaling factor used to convert the flux
122 !! convergence of this tracer to its desired units.
123 !! [conc m CU-1 H-1 ~> 1] or [conc kg m-2 CU-1 H-1 ~> 1]
124 character(len=*), optional, intent(in) :: cmor_tendprefix !< The CMOR name for the layer-integrated
125 !! tendencies of this tracer.
126 integer, optional, intent(in) :: diag_form !< An integer (1 or 2, 1 by default) indicating the
127 !! character string template to use in
128 !! labeling diagnostics
129 type(MOM_restart_CS), optional, intent(inout) :: restart_CS !< MOM restart control struct
130 logical, optional, intent(in) :: mandatory !< If true, this tracer must be read
131 !! from a restart file.
132 real, optional, intent(in) :: underflow_conc !< A tiny concentration, below which the tracer
133 !! concentration underflows to 0 [CU ~> conc].
134 type(tracer_type), optional, pointer :: Tr_out !< If present, returns pointer into registry
135
136 integer, optional, intent(in) :: advect_scheme !< Advection scheme for this tracer, the default is -1
137 !! indicating to use the scheme from MOM_tracer_advect
138
139 logical :: mand
140 type(tracer_type), pointer :: Tr=>NULL()
141 character(len=256) :: mesg ! Message for error messages.
142
1430 if (.not. associated(Reg)) call tracer_registry_init(param_file, Reg)
144
1453 if (Reg%ntr>=MAX_FIELDS_) then
146 write(mesg,'("Increase MAX_FIELDS_ in MOM_memory.h to at least ",I0," to allow for &
1470 &all the tracers being registered via register_tracer.")') Reg%ntr+1
1480 call MOM_error(FATAL,"MOM register_tracer: "//mesg)
149 endif
1503 Reg%ntr = Reg%ntr + 1
151
1523 Tr => Reg%Tr(Reg%ntr)
1533 if (present(Tr_out)) Tr_out => Reg%Tr(Reg%ntr)
154
1553 if (present(name)) then
1560 Tr%name = name
1570 Tr%longname = name ; if (present(longname)) Tr%longname = longname
1580 Tr%units = "Conc" ; if (present(units)) Tr%units = units
159
1600 Tr%cmor_name = ""
1610 if (present(cmor_name)) Tr%cmor_name = cmor_name
162
1630 Tr%cmor_units = Tr%units
1640 if (present(cmor_units)) Tr%cmor_units = cmor_units
165
1660 Tr%cmor_longname = ""
1670 if (present(cmor_longname)) Tr%cmor_longname = cmor_longname
168
1690 if (present(tr_desc)) call MOM_error(WARNING, "MOM register_tracer: "//&
1700 "It is a bad idea to use both name and tr_desc when registring "//trim(name))
1713 elseif (present(tr_desc)) then
172 call query_vardesc(tr_desc, name=Tr%name, units=Tr%units, &
173 longname=Tr%longname, cmor_field_name=Tr%cmor_name, &
1743 cmor_longname=Tr%cmor_longname, caller="register_tracer")
1753 Tr%cmor_units = Tr%units
176 else
177 call MOM_error(FATAL,"MOM register_tracer: Either name or "//&
1780 "tr_desc must be present when registering a tracer.")
179 endif
180
1813 if (Reg%locked) call MOM_error(FATAL, &
182 "MOM register_tracer was called for variable "//trim(Tr%name)//&
1830 " with a locked tracer registry.")
184
1853 Tr%conc_scale = 1.0
1863 if (present(conc_scale)) Tr%conc_scale = conc_scale
187
1883 Tr%conc_underflow = 0.0
1893 if (present(underflow_conc)) Tr%conc_underflow = underflow_conc
190
1913 Tr%flux_nameroot = Tr%name
1923 if (present(flux_nameroot)) then
1932 if (len_trim(flux_nameroot) > 0) Tr%flux_nameroot = flux_nameroot
194 endif
195
1963 Tr%flux_longname = Tr%longname
1973 if (present(flux_longname)) then
1982 if (len_trim(flux_longname) > 0) Tr%flux_longname = flux_longname
199 endif
200
2013 Tr%net_surfflux_name = "KPP_net"//trim(Tr%name)
2023 if (present(net_surfflux_name)) then
2032 Tr%net_surfflux_name = net_surfflux_name
204 endif
205
2063 Tr%NLT_budget_name = 'KPP_NLT_'//trim(Tr%flux_nameroot)//'_budget'
2073 if (present(NLT_budget_name)) then
2082 Tr%NLT_budget_name = NLT_budget_name
209 endif
210
211 Tr%net_surfflux_longname = 'Effective net surface '//trim(lowercase(Tr%flux_longname))//&
2123 ' flux, as used by [CVMix] KPP'
2133 if (present(net_surfflux_longname)) then
2141 Tr%net_surfflux_longname = net_surfflux_longname
215 endif
216
2173 Tr%flux_units = ""
2183 if (present(flux_units)) Tr%flux_units = flux_units
219
2203 Tr%flux_scale = GV%H_to_MKS*Tr%conc_scale
2213 if (present(flux_scale)) Tr%flux_scale = flux_scale
222
2233 Tr%conv_units = ""
2243 if (present(convergence_units)) Tr%conv_units = convergence_units
225
2263 Tr%cmor_tendprefix = ""
2273 if (present(cmor_tendprefix)) Tr%cmor_tendprefix = cmor_tendprefix
228
2293 Tr%conv_scale = GV%H_to_MKS*Tr%conc_scale
2303 if (present(convergence_scale)) then
2312 Tr%conv_scale = convergence_scale
2321 elseif (present(flux_scale)) then
2331 Tr%conv_scale = flux_scale
234 endif
235
2363 Tr%diag_form = 1
2373 if (present(diag_form)) Tr%diag_form = diag_form
238
2393 Tr%advect_scheme = -1
2403 if (present(advect_scheme)) Tr%advect_scheme = advect_scheme
241
2423 Tr%t => tr_ptr
243
2443 if (present(registry_diags)) Tr%registry_diags = registry_diags
245
2463 if (present(ad_x)) then ; if (associated(ad_x)) Tr%ad_x => ad_x ; endif
2473 if (present(ad_y)) then ; if (associated(ad_y)) Tr%ad_y => ad_y ; endif
2483 if (present(df_x)) then ; if (associated(df_x)) Tr%df_x => df_x ; endif
2493 if (present(df_y)) then ; if (associated(df_y)) Tr%df_y => df_y ; endif
250! if (present(OBC_inflow)) Tr%OBC_inflow_conc = OBC_inflow
251! if (present(OBC_in_u)) then ; if (associated(OBC_in_u)) Tr%OBC_in_u => OBC_in_u ; endif
252! if (present(OBC_in_v)) then ; if (associated(OBC_in_v)) Tr%OBC_in_v => OBC_in_v ; endif
2533 if (present(ad_2d_x)) then ; if (associated(ad_2d_x)) Tr%ad2d_x => ad_2d_x ; endif
2543 if (present(ad_2d_y)) then ; if (associated(ad_2d_y)) Tr%ad2d_y => ad_2d_y ; endif
2553 if (present(df_2d_x)) then ; if (associated(df_2d_x)) Tr%df2d_x => df_2d_x ; endif
256
2573 if (present(advection_xy)) then
2580 if (associated(advection_xy)) Tr%advection_xy => advection_xy
259 endif
260
2613 if (present(restart_CS)) then
262 ! Register this tracer to be read from and written to restart files.
2631 mand = .true. ; if (present(mandatory)) mand = mandatory
264
265 call register_restart_field(tr_ptr, Tr%name, mand, restart_CS, &
2661 longname=Tr%longname, units=Tr%units, conversion=conc_scale)
267 endif
2683end subroutine register_tracer
269
270
271!> This subroutine locks the tracer registry to prevent the addition of more
272!! tracers. After locked=.true., can then register common diagnostics.
2731subroutine lock_tracer_registry(Reg)
274 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
275
2761 if (.not. associated(Reg)) call MOM_error(WARNING, &
2770 "lock_tracer_registry called with an unassociated registry.")
278
2791 Reg%locked = .True.
280
2811end subroutine lock_tracer_registry
282
283!> register_tracer_diagnostics does a set of register_diag_field calls for any previously
284!! registered in a tracer registry with a value of registry_diags set to .true.
2851subroutine register_tracer_diagnostics(Reg, h, Time, diag, G, GV, US, use_ALE, use_KPP)
286 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
287 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
288 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
289 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
290 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
291 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
292 type(time_type), intent(in) :: Time !< current model time
293 type(diag_ctrl), intent(in) :: diag !< structure to regulate diagnostic output
294 logical, intent(in) :: use_ALE !< If true active diagnostics that only
295 !! apply to ALE configurations
296 logical, intent(in) :: use_KPP !< If true active diagnostics that only
297 !! apply to CVMix KPP mixings
298
299 character(len=24) :: name ! A variable's name in a NetCDF file.
300 character(len=24) :: shortnm ! A shortened version of a variable's name for
301 ! creating additional diagnostics.
302 character(len=72) :: longname ! The long name of that tracer variable.
303 character(len=72) :: flux_longname ! The tracer name in the long names of fluxes.
304 character(len=48) :: units ! The dimensions of the tracer.
305 character(len=48) :: flux_units ! The units for fluxes, either
306 ! [units] m3 s-1 or [units] kg s-1.
307 character(len=48) :: conv_units ! The units for flux convergences, either
308 ! [units] m2 s-1 or [units] kg s-1.
309 character(len=48) :: unit2 ! The dimensions of the tracer squared
310 character(len=72) :: cmorname ! The CMOR name of this tracer.
311 character(len=120) :: cmor_longname ! The CMOR long name of that variable.
312 character(len=120) :: var_lname ! A temporary longname for a diagnostic.
313 character(len=120) :: cmor_var_lname ! The temporary CMOR long name for a diagnostic
314 real :: conversion ! Temporary term while we address a bug [conc m CU-1 H-1 ~> 1] or [conc kg m-2 CU-1 H-1 ~> 1]
315 type(tracer_type), pointer :: Tr=>NULL()
316 integer :: i, j, k, is, ie, js, je, nz, m, m2, nTr_in
317 integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB
3181 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
3191 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
3201 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
321
3221 if (.not. associated(Reg)) call MOM_error(FATAL, "register_tracer_diagnostics: "//&
3230 "register_tracer must be called before register_tracer_diagnostics")
324
3251 nTr_in = Reg%ntr
326
3274 do m=1,nTr_in ; if (Reg%Tr(m)%registry_diags) then
3283 Tr => Reg%Tr(m)
329! call query_vardesc(Tr%vd, name, units=units, longname=longname, &
330! cmor_field_name=cmorname, cmor_longname=cmor_longname, &
331! caller="register_tracer_diagnostics")
3323 name = Tr%name ; units=adjustl(Tr%units) ; longname = Tr%longname
3333 cmorname = Tr%cmor_name ; cmor_longname = Tr%cmor_longname
3343 shortnm = Tr%flux_nameroot
3353 flux_longname = Tr%flux_longname
3363 if (len_trim(cmor_longname) == 0) cmor_longname = longname
337
3383 if (len_trim(Tr%flux_units) > 0) then ; flux_units = Tr%flux_units
3391 elseif (GV%Boussinesq) then ; flux_units = trim(units)//" m3 s-1"
3400 else ; flux_units = trim(units)//" kg s-1" ; endif
341
3423 if (len_trim(Tr%conv_units) > 0) then ; conv_units = Tr%conv_units
3431 elseif (GV%Boussinesq) then ; conv_units = trim(units)//" m s-1"
3440 else ; conv_units = trim(units)//" kg m-2 s-1" ; endif
345
3463 if (len_trim(cmorname) == 0) then
347 Tr%id_tr = register_diag_field("ocean_model", trim(name), diag%axesTL, &
3480 Time, trim(longname), trim(units), conversion=Tr%conc_scale)
349 else
350 Tr%id_tr = register_diag_field("ocean_model", trim(name), diag%axesTL, &
351 Time, trim(longname), trim(units), conversion=Tr%conc_scale, &
352 cmor_field_name=cmorname, cmor_long_name=cmor_longname, &
3533 cmor_units=Tr%cmor_units, cmor_standard_name=cmor_long_std(cmor_longname))
354 endif
355 Tr%id_tr_post_horzn = register_diag_field("ocean_model", &
356 trim(name)//"_post_horzn", diag%axesTL, Time, &
357 trim(longname)//" after horizontal transport (advection/diffusion) has occurred", &
3583 trim(units), conversion=Tr%conc_scale)
3593 if (Tr%diag_form == 1) then
360 Tr%id_adx = register_diag_field("ocean_model", trim(shortnm)//"_adx", &
361 diag%axesCuL, Time, trim(flux_longname)//" advective zonal flux" , &
362 trim(flux_units), v_extensive=.true., y_cell_method='sum', &
3631 conversion=Tr%flux_scale*(US%L_to_m**2)*US%s_to_T)
364 Tr%id_ady = register_diag_field("ocean_model", trim(shortnm)//"_ady", &
365 diag%axesCvL, Time, trim(flux_longname)//" advective meridional flux" , &
366 trim(flux_units), v_extensive=.true., x_cell_method='sum', &
3671 conversion=Tr%flux_scale*(US%L_to_m**2)*US%s_to_T)
368 Tr%id_dfx = register_diag_field("ocean_model", trim(shortnm)//"_dfx", &
369 diag%axesCuL, Time, trim(flux_longname)//" diffusive zonal flux" , &
370 trim(flux_units), v_extensive=.true., y_cell_method='sum', &
3711 conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T)
372 Tr%id_dfy = register_diag_field("ocean_model", trim(shortnm)//"_dfy", &
373 diag%axesCvL, Time, trim(flux_longname)//" diffusive meridional flux" , &
374 trim(flux_units), v_extensive=.true., x_cell_method='sum', &
3751 conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T)
376 Tr%id_hbd_dfx = register_diag_field("ocean_model", trim(shortnm)//"_hbd_diffx", &
377 diag%axesCuL, Time, trim(flux_longname)//" diffusive zonal flux " //&
378 "from the horizontal boundary diffusion scheme", trim(flux_units), v_extensive=.true., &
3791 y_cell_method='sum', conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T)
380 Tr%id_hbd_dfy = register_diag_field("ocean_model", trim(shortnm)//"_hbd_diffy", &
381 diag%axesCvL, Time, trim(flux_longname)//" diffusive meridional " //&
382 "flux from the horizontal boundary diffusion scheme", trim(flux_units), &
383 v_extensive=.true., &
3841 x_cell_method='sum', conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T)
385 else
386 Tr%id_adx = register_diag_field("ocean_model", trim(shortnm)//"_adx", &
387 diag%axesCuL, Time, "Advective (by residual mean) Zonal Flux of "//trim(flux_longname), &
3882 flux_units, v_extensive=.true., conversion=Tr%flux_scale*(US%L_to_m**2)*US%s_to_T, y_cell_method='sum')
389 Tr%id_ady = register_diag_field("ocean_model", trim(shortnm)//"_ady", &
390 diag%axesCvL, Time, "Advective (by residual mean) Meridional Flux of "//trim(flux_longname), &
3912 flux_units, v_extensive=.true., conversion=Tr%flux_scale*(US%L_to_m**2)*US%s_to_T, x_cell_method='sum')
392 Tr%id_dfx = register_diag_field("ocean_model", trim(shortnm)//"_diffx", &
393 diag%axesCuL, Time, "Diffusive Zonal Flux of "//trim(flux_longname), &
394 flux_units, v_extensive=.true., conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
3952 y_cell_method='sum')
396 Tr%id_dfy = register_diag_field("ocean_model", trim(shortnm)//"_diffy", &
397 diag%axesCvL, Time, "Diffusive Meridional Flux of "//trim(flux_longname), &
398 flux_units, v_extensive=.true., conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
3992 x_cell_method='sum')
400 Tr%id_hbd_dfx = register_diag_field("ocean_model", trim(shortnm)//"_hbd_diffx", &
401 diag%axesCuL, Time, &
402 "Horizontal Boundary Diffusive Zonal Flux of "//trim(flux_longname), &
403 flux_units, v_extensive=.true., conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
4042 y_cell_method='sum')
405 Tr%id_hbd_dfy = register_diag_field("ocean_model", trim(shortnm)//"_hbd_diffy", &
406 diag%axesCvL, Time, &
407 "Horizontal Boundary Diffusive Meridional Flux of "//trim(flux_longname), &
408 flux_units, v_extensive=.true., conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
4092 x_cell_method='sum')
410 endif
411 Tr%id_zint = register_diag_field("ocean_model", trim(shortnm)//"_zint", &
412 diag%axesT1, Time, &
413 "Thickness-weighted integral of " // trim(longname), &
4143 trim(units) // " m")
415 Tr%id_zint_100m = register_diag_field("ocean_model", trim(shortnm)//"_zint_100m", &
416 diag%axesT1, Time, &
417 "Thickness-weighted integral of "// trim(longname) // " over top 100m", &
4183 trim(units) // " m")
419 Tr%id_surf = register_diag_field("ocean_model", trim(shortnm)//"_SURF", &
4203 diag%axesT1, Time, "Surface values of "// trim(longname), trim(units))
4213 if (Tr%id_adx > 0) call safe_alloc_ptr(Tr%ad_x,IsdB,IedB,jsd,jed,nz)
4223 if (Tr%id_ady > 0) call safe_alloc_ptr(Tr%ad_y,isd,ied,JsdB,JedB,nz)
4233 if (Tr%id_dfx > 0) call safe_alloc_ptr(Tr%df_x,IsdB,IedB,jsd,jed,nz)
4243 if (Tr%id_dfy > 0) call safe_alloc_ptr(Tr%df_y,isd,ied,JsdB,JedB,nz)
4253 if (Tr%id_hbd_dfx > 0) call safe_alloc_ptr(Tr%hbd_dfx,IsdB,IedB,jsd,jed,nz)
4263 if (Tr%id_hbd_dfy > 0) call safe_alloc_ptr(Tr%hbd_dfy,isd,ied,JsdB,JedB,nz)
427
428 Tr%id_adx_2d = register_diag_field("ocean_model", trim(shortnm)//"_adx_2d", &
429 diag%axesCu1, Time, &
430 "Vertically Integrated Advective Zonal Flux of "//trim(flux_longname), &
4313 flux_units, conversion=Tr%flux_scale*(US%L_to_m**2)*US%s_to_T, y_cell_method='sum')
432 Tr%id_ady_2d = register_diag_field("ocean_model", trim(shortnm)//"_ady_2d", &
433 diag%axesCv1, Time, &
434 "Vertically Integrated Advective Meridional Flux of "//trim(flux_longname), &
4353 flux_units, conversion=Tr%flux_scale*(US%L_to_m**2)*US%s_to_T, x_cell_method='sum')
436 Tr%id_dfx_2d = register_diag_field("ocean_model", trim(shortnm)//"_diffx_2d", &
437 diag%axesCu1, Time, &
438 "Vertically Integrated Diffusive Zonal Flux of "//trim(flux_longname), &
439 flux_units, conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
4403 y_cell_method='sum')
441 Tr%id_dfy_2d = register_diag_field("ocean_model", trim(shortnm)//"_diffy_2d", &
442 diag%axesCv1, Time, &
443 "Vertically Integrated Diffusive Meridional Flux of "//trim(flux_longname), &
444 flux_units, conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
4453 x_cell_method='sum')
446 Tr%id_hbd_dfx_2d = register_diag_field("ocean_model", trim(shortnm)//"_hbd_diffx_2d", &
447 diag%axesCu1, Time, "Vertically-integrated zonal diffusive flux from the horizontal boundary diffusion "//&
448 "scheme for "//trim(flux_longname), flux_units, conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
4493 y_cell_method='sum')
450 Tr%id_hbd_dfy_2d = register_diag_field("ocean_model", trim(shortnm)//"_hbd_diffy_2d", &
451 diag%axesCv1, Time, "Vertically-integrated meridional diffusive flux from the horizontal boundary diffusion "//&
452 "scheme for "//trim(flux_longname), flux_units, conversion=(US%L_to_m**2)*Tr%flux_scale*US%s_to_T, &
4533 x_cell_method='sum')
454
4553 if (Tr%id_adx_2d > 0) call safe_alloc_ptr(Tr%ad2d_x,IsdB,IedB,jsd,jed)
4563 if (Tr%id_ady_2d > 0) call safe_alloc_ptr(Tr%ad2d_y,isd,ied,JsdB,JedB)
4573 if (Tr%id_dfx_2d > 0) call safe_alloc_ptr(Tr%df2d_x,IsdB,IedB,jsd,jed)
4583 if (Tr%id_dfy_2d > 0) call safe_alloc_ptr(Tr%df2d_y,isd,ied,JsdB,JedB)
4593 if (Tr%id_hbd_dfx_2d > 0) call safe_alloc_ptr(Tr%hbd_dfx_2d,IsdB,IedB,jsd,jed)
4603 if (Tr%id_hbd_dfy_2d > 0) call safe_alloc_ptr(Tr%hbd_dfy_2d,isd,ied,JsdB,JedB)
461
462 Tr%id_adv_xy = register_diag_field('ocean_model', trim(shortnm)//"_advection_xy", &
463 diag%axesTL, Time, &
464 'Horizontal convergence of residual mean advective fluxes of '//&
465 trim(lowercase(flux_longname)), &
4663 conv_units, v_extensive=.true., conversion=Tr%conv_scale*US%s_to_T)
467 Tr%id_adv_xy_2d = register_diag_field('ocean_model', trim(shortnm)//"_advection_xy_2d", &
468 diag%axesT1, Time, &
469 'Vertical sum of horizontal convergence of residual mean advective fluxes of '//&
4703 trim(lowercase(flux_longname)), conv_units, conversion=Tr%conv_scale*US%s_to_T)
4713 if ((Tr%id_adv_xy > 0) .or. (Tr%id_adv_xy_2d > 0)) &
4720 call safe_alloc_ptr(Tr%advection_xy,isd,ied,jsd,jed,nz)
473
474 Tr%id_tendency = register_diag_field('ocean_model', trim(shortnm)//'_tendency', &
475 diag%axesTL, Time, &
476 'Net time tendency for '//trim(lowercase(longname)), &
4773 trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
478
4793 if (Tr%id_tendency > 0) then
4800 call safe_alloc_ptr(Tr%t_prev,isd,ied,jsd,jed,nz)
4810 do k=1,nz ; do j=js,je ; do i=is,ie
4820 Tr%t_prev(i,j,k) = Tr%t(i,j,k)
483 enddo ; enddo ; enddo
484 endif
485
486 ! Neutral/Horizontal diffusion convergence tendencies
4873 if (Tr%diag_form == 1) then
488 Tr%id_dfxy_cont = register_diag_field("ocean_model", trim(shortnm)//'_dfxy_cont_tendency', &
489 diag%axesTL, Time, "Neutral diffusion tracer content tendency for "//trim(shortnm), &
490 conv_units, conversion=Tr%conv_scale*US%s_to_T, &
4911 x_cell_method='sum', y_cell_method='sum', v_extensive=.true.)
492
493 Tr%id_dfxy_cont_2d = register_diag_field("ocean_model", &
494 trim(shortnm)//'_dfxy_cont_tendency_2d', &
495 diag%axesT1, Time, "Depth integrated neutral diffusion tracer content "//&
496 "tendency for "//trim(shortnm), conv_units, conversion=Tr%conv_scale*US%s_to_T, &
4971 x_cell_method='sum', y_cell_method='sum')
498
499 Tr%id_hbdxy_cont = register_diag_field("ocean_model", trim(shortnm)//'_hbdxy_cont_tendency', &
500 diag%axesTL, Time, "Horizontal boundary diffusion tracer content tendency for "//&
501 trim(shortnm), &
502 conv_units, conversion=Tr%conv_scale*US%s_to_T, &
5031 x_cell_method='sum', y_cell_method='sum', v_extensive=.true.)
504
505 Tr%id_hbdxy_cont_2d = register_diag_field("ocean_model", &
506 trim(shortnm)//'_hbdxy_cont_tendency_2d', &
507 diag%axesT1, Time, "Depth integrated horizontal boundary diffusion tracer content "//&
508 "tendency for "//trim(shortnm), conv_units, conversion=Tr%conv_scale*US%s_to_T, &
5091 x_cell_method='sum', y_cell_method='sum')
510 else
511 cmor_var_lname = 'Tendency of '//trim(lowercase(cmor_longname))//' expressed as '//&
512 trim(lowercase(flux_longname))//&
5132 ' content due to parameterized mesoscale neutral diffusion'
514 Tr%id_dfxy_cont = register_diag_field("ocean_model", trim(shortnm)//'_dfxy_cont_tendency', &
515 diag%axesTL, Time, "Neutral diffusion tracer content tendency for "//trim(shortnm), &
516 conv_units, conversion=Tr%conv_scale*US%s_to_T, &
517 cmor_field_name=trim(Tr%cmor_tendprefix)//'pmdiff', &
518 cmor_long_name=trim(cmor_var_lname), &
519 cmor_standard_name=trim(cmor_long_std(cmor_var_lname)), &
5202 x_cell_method='sum', y_cell_method='sum', v_extensive=.true.)
521
522 cmor_var_lname = 'Tendency of '//trim(lowercase(cmor_longname))//' expressed as '//&
523 trim(lowercase(flux_longname))//&
5242 ' content due to parameterized mesoscale neutral diffusion'
525 Tr%id_dfxy_cont_2d = register_diag_field("ocean_model", &
526 trim(shortnm)//'_dfxy_cont_tendency_2d', &
527 diag%axesT1, Time, "Depth integrated neutral diffusion tracer "//&
528 "content tendency for "//trim(shortnm), conv_units, conversion=Tr%conv_scale*US%s_to_T, &
529 cmor_field_name=trim(Tr%cmor_tendprefix)//'pmdiff_2d', &
530 cmor_long_name=trim(cmor_var_lname), &
531 cmor_standard_name=trim(cmor_long_std(cmor_var_lname)), &
5322 x_cell_method='sum', y_cell_method='sum')
533
534 Tr%id_hbdxy_cont = register_diag_field("ocean_model", trim(shortnm)//'_hbdxy_cont_tendency', &
535 diag%axesTL, Time, &
536 "Horizontal boundary diffusion tracer content tendency for "//trim(shortnm), &
537 conv_units, conversion=Tr%conv_scale*US%s_to_T, &
5382 x_cell_method='sum', y_cell_method='sum', v_extensive=.true.)
539
540 Tr%id_hbdxy_cont_2d = register_diag_field("ocean_model", &
541 trim(shortnm)//'_hbdxy_cont_tendency_2d', &
542 diag%axesT1, Time, "Depth integrated horizontal boundary diffusion of tracer "//&
543 "content tendency for "//trim(shortnm), conv_units, conversion=Tr%conv_scale*US%s_to_T, &
5442 x_cell_method='sum', y_cell_method='sum')
545 endif
546 Tr%id_dfxy_conc = register_diag_field("ocean_model", trim(shortnm)//'_dfxy_conc_tendency', &
547 diag%axesTL, Time, "Neutral diffusion tracer concentration tendency for "//trim(shortnm), &
5483 trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
549
550 Tr%id_hbdxy_conc = register_diag_field("ocean_model", trim(shortnm)//'_hbdxy_conc_tendency', &
551 diag%axesTL, Time, &
552 "Horizontal diffusion tracer concentration tendency for "//trim(shortnm), &
5533 trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
554
5553 var_lname = "Net time tendency for "//lowercase(flux_longname)
5563 if (len_trim(Tr%cmor_tendprefix) == 0) then
557 Tr%id_trxh_tendency = register_diag_field('ocean_model', trim(shortnm)//'h_tendency', &
558 diag%axesTL, Time, var_lname, conv_units, conversion=Tr%conv_scale*US%s_to_T, &
5591 v_extensive=.true.)
560 Tr%id_trxh_tendency_2d = register_diag_field('ocean_model', trim(shortnm)//'h_tendency_2d', &
561 diag%axesT1, Time, "Vertical sum of "//trim(lowercase(var_lname)), &
5621 conv_units, conversion=Tr%conv_scale*US%s_to_T)
563 else
564 cmor_var_lname = "Tendency of "//trim(cmor_longname)//" Expressed as "//&
5652 trim(flux_longname)//" Content"
566 Tr%id_trxh_tendency = register_diag_field('ocean_model', trim(shortnm)//'h_tendency', &
567 diag%axesTL, Time, var_lname, conv_units, conversion=Tr%conv_scale*US%s_to_T, &
568 cmor_field_name=trim(Tr%cmor_tendprefix)//"tend", &
569 cmor_standard_name=cmor_long_std(cmor_var_lname), cmor_long_name=cmor_var_lname, &
5702 v_extensive=.true.)
5712 cmor_var_lname = trim(cmor_var_lname)//" Vertical Sum"
572 Tr%id_trxh_tendency_2d = register_diag_field('ocean_model', trim(shortnm)//'h_tendency_2d', &
573 diag%axesT1, Time, "Vertical sum of "//trim(lowercase(var_lname)), &
574 conv_units, conversion=Tr%conv_scale*US%s_to_T, &
575 cmor_field_name=trim(Tr%cmor_tendprefix)//"tend_2d", &
5762 cmor_standard_name=cmor_long_std(cmor_var_lname), cmor_long_name=cmor_var_lname)
577 endif
5783 if ((Tr%id_trxh_tendency > 0) .or. (Tr%id_trxh_tendency_2d > 0)) then
5790 call safe_alloc_ptr(Tr%Trxh_prev,isd,ied,jsd,jed,nz)
5800 do k=1,nz ; do j=js,je ; do i=is,ie
5810 Tr%Trxh_prev(i,j,k) = Tr%t(i,j,k) * h(i,j,k)
582 enddo ; enddo ; enddo
583 endif
584
585 ! Vertical regridding/remapping tendencies
5863 if (use_ALE .and. Tr%remap_tr) then
5873 var_lname = "Vertical remapping tracer concentration tendency for "//trim(Reg%Tr(m)%name)
588 Tr%id_remap_conc= register_diag_field('ocean_model', &
589 trim(Tr%flux_nameroot)//'_tendency_vert_remap', diag%axesTL, Time, var_lname, &
5903 trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
591
5923 var_lname = "Vertical remapping tracer content tendency for "//trim(Reg%Tr(m)%flux_longname)
593 Tr%id_remap_cont = register_diag_field('ocean_model', &
594 trim(Tr%flux_nameroot)//'h_tendency_vert_remap', &
5953 diag%axesTL, Time, var_lname, conv_units, v_extensive=.true., conversion=Tr%conv_scale*US%s_to_T)
596
597 var_lname = "Vertical sum of vertical remapping tracer content tendency for "//&
5983 trim(Reg%Tr(m)%flux_longname)
599 Tr%id_remap_cont_2d = register_diag_field('ocean_model', &
600 trim(Tr%flux_nameroot)//'h_tendency_vert_remap_2d', &
6013 diag%axesT1, Time, var_lname, conv_units, conversion=Tr%conv_scale*US%s_to_T)
602
603 endif
604
6053 if (use_ALE .and. (Reg%ntr<MAX_FIELDS_) .and. Tr%remap_tr) then
6063 unit2 = trim(units)//"2"
6073 if (index(units(1:len_trim(units))," ") > 0) unit2 = "("//trim(units)//")2"
608 Tr%id_tr_vardec = register_diag_field('ocean_model', trim(shortnm)//"_vardec", diag%axesTL, &
609 Time, "ALE variance decay for "//lowercase(longname), &
6103 trim(unit2)//" s-1", conversion=Tr%conc_scale**2*US%s_to_T)
6113 if (Tr%id_tr_vardec > 0) then
612 ! Set up a new tracer for this tracer squared
6130 m2 = Reg%ntr+1
6140 Tr%ind_tr_squared = m2
6150 call safe_alloc_ptr(Reg%Tr(m2)%t,isd,ied,jsd,jed,nz) ; Reg%Tr(m2)%t(:,:,:) = 0.0
6160 Reg%Tr(m2)%name = trim(shortnm)//"2"
6170 Reg%Tr(m2)%longname = "Squared "//trim(longname)
6180 Reg%Tr(m2)%units = unit2
6190 Reg%Tr(m2)%registry_diags = .false.
6200 Reg%Tr(m2)%ind_tr_squared = -1
621 ! Augment the total number of tracers, including the squared tracers.
6220 Reg%ntr = Reg%ntr + 1
623 endif
624 endif
625
626 ! KPP nonlocal term diagnostics
6273 if (use_KPP) then
628 Tr%id_net_surfflux = register_diag_field('ocean_model', Tr%net_surfflux_name, diag%axesT1, Time, &
6290 Tr%net_surfflux_longname, trim(units)//' m s-1', conversion=Tr%conc_scale*GV%H_to_m*US%s_to_T)
630 Tr%id_NLT_tendency = register_diag_field('ocean_model', "KPP_NLT_d"//trim(shortnm)//"dt", &
631 diag%axesTL, Time, &
632 trim(longname)//' tendency due to non-local transport of '//trim(lowercase(flux_longname))//&
6330 ', as calculated by [CVMix] KPP', trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
6340 if (Tr%conv_scale == 0.001*GV%H_to_kg_m2) then
6350 conversion = GV%H_to_kg_m2
636 else
6370 conversion = Tr%conv_scale
638 endif
639 ! We actually want conversion=Tr%conv_scale for all tracers, but introducing the local variable
640 ! 'conversion' and setting it to GV%H_to_kg_m2 instead of 0.001*GV%H_to_kg_m2 for salt tracers
641 ! keeps changes introduced by this refactoring limited to round-off level; as it turns out,
642 ! there is a bug in the code and the NLT budget term for salinity is off by a factor of 10^3
643 ! so introducing the 0.001 here will fix that bug.
644 Tr%id_NLT_budget = register_diag_field('ocean_model', Tr%NLT_budget_name, &
645 diag%axesTL, Time, &
646 trim(flux_longname)//&
647 ' content change due to non-local transport, as calculated by [CVMix] KPP', &
6480 conv_units, conversion=conversion*US%s_to_T, v_extensive=.true.)
649 endif
650
651 endif ; enddo
652
6531end subroutine register_tracer_diagnostics
654
65512subroutine preALE_tracer_diagnostics(Reg, G, GV)
656 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
657 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
658 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
659
660 integer :: i, j, k, is, ie, js, je, nz, m, m2
66112 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
662
66348 do m=1,Reg%ntr ; if (Reg%Tr(m)%ind_tr_squared > 0) then
6640 m2 = Reg%Tr(m)%ind_tr_squared
665 ! Update squared quantities
6660 do k=1,nz ; do j=js,je ; do i=is,ie
6670 Reg%Tr(m2)%T(i,j,k) = Reg%Tr(m)%T(i,j,k)**2
668 enddo ; enddo ; enddo
669 endif ; enddo
670
67112end subroutine preALE_tracer_diagnostics
672
67312subroutine postALE_tracer_diagnostics(Reg, G, GV, diag, dt)
674 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
675 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
676 type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure
677 type(diag_ctrl), intent(in) :: diag !< regulates diagnostic output
678 real, intent(in) :: dt !< total time interval for these diagnostics [T ~> s]
679
68024 real :: work(SZI_(G),SZJ_(G),SZK_(GV)) ! Variance decay [CU2 T-1 ~> conc2 s-1]
681 real :: Idt ! The inverse of the time step [T-1 ~> s-1]
682 integer :: i, j, k, is, ie, js, je, nz, m, m2
68312 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
684
685 ! The "if" is to avoid NaNs if the diagnostic is called for a zero length interval
68612 Idt = 0.0 ; if (dt /= 0.0) Idt = 1.0 / dt
687
68848 do m=1,Reg%ntr ; if (Reg%Tr(m)%id_tr_vardec > 0) then
6890 m2 = Reg%Tr(m)%ind_tr_squared
6900 if (m2 < 1) call MOM_error(FATAL, "Bad value of Tr%ind_tr_squared for "//trim(Reg%Tr(m)%name))
691 ! Update squared quantities
6920 do k=1,nz ; do j=js,je ; do i=is,ie
6930 work(i,j,k) = (Reg%Tr(m2)%T(i,j,k) - Reg%Tr(m)%T(i,j,k)**2) * Idt
694 enddo ; enddo ; enddo
6950 call post_data(Reg%Tr(m)%id_tr_vardec, work, diag)
696 endif ; enddo
697
69812end subroutine postALE_tracer_diagnostics
699
700!> Post tracer diganostics when that should only be posted when MOM's state
701!! is self-consistent (also referred to as 'synchronized')
70212subroutine post_tracer_diagnostics_at_sync(Reg, h, diag_prev, diag, G, GV, dt)
703 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
704 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
705 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
706 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
707 intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
708 type(diag_grid_storage), intent(in) :: diag_prev !< Contains diagnostic grids from previous timestep
709 type(diag_ctrl), intent(inout) :: diag !< structure to regulate diagnostic output
710 real, intent(in) :: dt !< total time step for tracer updates [T ~> s]
711
71224 real :: work3d(SZI_(G),SZJ_(G),SZK_(GV)) ! The time tendency of a diagnostic [CU T-1 ~> conc s-1]
71324 real :: work2d(SZI_(G),SZJ_(G)) ! The vertically integrated time tendency of a diagnostic
714 ! in [CU H T-1 ~> conc m s-1 or conc kg m-2 s-1]
715 real :: Idt ! The inverse of the time step [T-1 ~> s-1]
716 type(tracer_type), pointer :: Tr=>NULL()
717 integer :: i, j, k, is, ie, js, je, nz, m
71812 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
719
72012 Idt = 0. ; if (dt/=0.) Idt = 1.0 / dt ! The "if" is in case the diagnostic is called for a zero length interval
721
722 ! Tendency diagnostics need to be posted on the grid from the last call to this routine
72312 call diag_save_grids(diag)
72412 call diag_copy_storage_to_diag(diag, diag_prev)
72548 do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
72636 Tr => Reg%Tr(m)
72736 if (Tr%id_tr > 0) call post_data(Tr%id_tr, Tr%t, diag)
72836 if (Tr%id_tendency > 0) then
7290 work3d(:,:,:) = 0.0
7300 do k=1,nz ; do j=js,je ; do i=is,ie
7310 work3d(i,j,k) = (Tr%t(i,j,k) - Tr%t_prev(i,j,k))*Idt
7320 tr%t_prev(i,j,k) = Tr%t(i,j,k)
733 enddo ; enddo ; enddo
7340 call post_data(Tr%id_tendency, work3d, diag, alt_h=diag_prev%h_state)
735 endif
73636 if ((Tr%id_trxh_tendency > 0) .or. (Tr%id_trxh_tendency_2d > 0)) then
7370 do k=1,nz ; do j=js,je ; do i=is,ie
7380 work3d(i,j,k) = (Tr%t(i,j,k)*h(i,j,k) - Tr%Trxh_prev(i,j,k)) * Idt
7390 Tr%Trxh_prev(i,j,k) = Tr%t(i,j,k) * h(i,j,k)
740 enddo ; enddo ; enddo
7410 if (Tr%id_trxh_tendency > 0) call post_data(Tr%id_trxh_tendency, work3d, diag, &
7420 alt_h=diag_prev%h_state)
7430 if (Tr%id_trxh_tendency_2d > 0) then
7440 work2d(:,:) = 0.0
7450 do k=1,nz ; do j=js,je ; do i=is,ie
7460 work2d(i,j) = work2d(i,j) + work3d(i,j,k)
747 enddo ; enddo ; enddo
7480 call post_data(Tr%id_trxh_tendency_2d, work2d, diag)
749 endif
750 endif
751 endif ; enddo
75212 call diag_restore_grids(diag)
753
75412end subroutine post_tracer_diagnostics_at_sync
755
756!> Post the advective and diffusive tendencies
75712subroutine post_tracer_transport_diagnostics(G, GV, Reg, h_diag, diag)
758 type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure
759 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
760 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
761 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), &
762 intent(in) :: h_diag !< Layer thicknesses on which to post fields [H ~> m or kg m-2]
763 type(diag_ctrl), intent(in) :: diag !< structure to regulate diagnostic output
764
765 integer :: i, j, k, is, ie, js, je, nz, m, khi
76624 real :: work2d(SZI_(G),SZJ_(G)) ! The vertically integrated convergence of lateral advective
767 ! tracer fluxes [CU H T-1 ~> conc m s-1 or conc kg m-2 s-1]
76824 real :: frac_under_100m(SZI_(G),SZJ_(G),SZK_(GV)) ! weights used to compute 100m vertical integrals [nondim]
76924 real :: ztop(SZI_(G),SZJ_(G)) ! position of the top interface [H ~> m or kg m-2]
77024 real :: zbot(SZI_(G),SZJ_(G)) ! position of the bottom interface [H ~> m or kg m-2]
771 type(tracer_type), pointer :: Tr=>NULL()
772
77312 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
774
775 ! If any tracers are posting 100m vertical integrals, compute weights
7767895712 frac_under_100m(:,:,:) = 0.0
777 ! khi will be the largest layer index corresponding where ztop < 100m and ztop >= 100m
778 ! in any column (we can reduce computation of 100m integrals by only looping through khi
779 ! rather than GV%ke)
78012 khi = 0
78148 do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
78236 Tr => Reg%Tr(m)
78336 if (Tr%id_zint_100m > 0) then
7840 zbot(:,:) = 0.0
7850 do k=1, nz
7860 do j=js,je ; do i=is,ie
7870 ztop(i,j) = zbot(i,j)
7880 zbot(i,j) = ztop(i,j) + h_diag(i,j,k)*GV%H_to_m
7890 if (zbot(i,j) <= 100.0) then
7900 frac_under_100m(i,j,k) = 1.0
7910 elseif (ztop(i,j) < 100.0) then
7920 frac_under_100m(i,j,k) = (100.0 - ztop(i,j)) / (zbot(i,j) - ztop(i,j))
793 else
7940 frac_under_100m(i,j,k) = 0.0
795 endif
796 ! frac_under_100m(i,j,k) = max(0, min(1.0, (100.0 - ztop(i,j)) / (zbot(i,j) - ztop(i,j))))
797 enddo ; enddo
7980 if (any(frac_under_100m(:,:,k) > 0)) khi = k
799 enddo
8000 exit
801 endif
802 endif ; enddo
803
80448 do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
80536 Tr => Reg%Tr(m)
80636 if (Tr%id_tr_post_horzn> 0) call post_data(Tr%id_tr_post_horzn, Tr%t, diag)
80736 if (Tr%id_adx > 0) call post_data(Tr%id_adx, Tr%ad_x, diag, alt_h=h_diag)
80836 if (Tr%id_ady > 0) call post_data(Tr%id_ady, Tr%ad_y, diag, alt_h=h_diag)
80936 if (Tr%id_dfx > 0) call post_data(Tr%id_dfx, Tr%df_x, diag, alt_h=h_diag)
81036 if (Tr%id_dfy > 0) call post_data(Tr%id_dfy, Tr%df_y, diag, alt_h=h_diag)
81136 if (Tr%id_adx_2d > 0) call post_data(Tr%id_adx_2d, Tr%ad2d_x, diag)
81236 if (Tr%id_ady_2d > 0) call post_data(Tr%id_ady_2d, Tr%ad2d_y, diag)
81336 if (Tr%id_dfx_2d > 0) call post_data(Tr%id_dfx_2d, Tr%df2d_x, diag)
81436 if (Tr%id_dfy_2d > 0) call post_data(Tr%id_dfy_2d, Tr%df2d_y, diag)
81536 if (Tr%id_adv_xy > 0) call post_data(Tr%id_adv_xy, Tr%advection_xy, diag, alt_h=h_diag)
81636 if (Tr%id_adv_xy_2d > 0) then
8170 work2d(:,:) = 0.0
8180 do k=1,nz ; do j=js,je ; do i=is,ie
8190 work2d(i,j) = work2d(i,j) + Tr%advection_xy(i,j,k)
820 enddo ; enddo ; enddo
8210 call post_data(Tr%id_adv_xy_2d, work2d, diag)
822 endif
823
824 ! A few diagnostics introduce with MARBL driver
825 ! Compute full-depth vertical integral
82636 if (Tr%id_zint > 0) then
8270 work2d(:,:) = 0.0
8280 do k=1,nz ; do j=js,je ; do i=is,ie
8290 work2d(i,j) = work2d(i,j) + (h_diag(i,j,k)*GV%H_to_m)*tr%t(i,j,k)
830 enddo ; enddo ; enddo
8310 call post_data(Tr%id_zint, work2d, diag)
832 endif
833
834 ! Compute 100m vertical integral
83536 if (Tr%id_zint_100m > 0) then
8360 work2d(:,:) = 0.0
8370 do k=1,khi ; do j=js,je ; do i=is,ie
8380 work2d(i,j) = work2d(i,j) + frac_under_100m(i,j,k)*((h_diag(i,j,k)*GV%H_to_m)*tr%t(i,j,k))
839 enddo ; enddo ; enddo
8400 call post_data(Tr%id_zint_100m, work2d, diag)
841 endif
842
843 ! Surface values of tracers
84436 if (Tr%id_SURF > 0) call post_data(Tr%id_SURF, Tr%t(:,:,1), diag)
845 endif ; enddo
846
84712end subroutine post_tracer_transport_diagnostics
848
849!> This subroutine writes out chksums for the first ntr registered tracers.
8500subroutine tracer_array_chksum(mesg, Tr, ntr, G)
851 character(len=*), intent(in) :: mesg !< message that appears on the chksum lines
852 type(tracer_type), intent(in) :: Tr(:) !< array of all of registered tracers
853 integer, intent(in) :: ntr !< number of registered tracers
854 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
855
856 integer :: m
857
8580 do m=1,ntr
8590 call hchksum(Tr(m)%t, mesg//trim(Tr(m)%name), G%HI, unscale=Tr(m)%conc_scale)
860 enddo
861
8620end subroutine tracer_array_chksum
863
864!> This subroutine writes out chksums for all the registered tracers.
8650subroutine tracer_Reg_chksum(mesg, Reg, G)
866 character(len=*), intent(in) :: mesg !< message that appears on the chksum lines
867 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
868 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
869
870 integer :: m
871
8720 if (.not.associated(Reg)) return
873
8740 do m=1,Reg%ntr
8750 call hchksum(Reg%Tr(m)%t, mesg//trim(Reg%Tr(m)%name), G%HI, unscale=Reg%Tr(m)%conc_scale)
876 enddo
877
8780end subroutine tracer_Reg_chksum
879
880!> Calculates and prints the global inventory of the first ntr tracers in the registry.
8810subroutine tracer_array_chkinv(mesg, G, GV, h, Tr, ntr)
882 character(len=*), intent(in) :: mesg !< message that appears on the chksum lines
883 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
884 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
885 type(tracer_type), dimension(:), intent(in) :: Tr !< array of all of registered tracers
886 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
887 integer, intent(in) :: ntr !< number of registered tracers
888
889 ! Local variables
890 real :: vol_scale ! The dimensional scaling factor to convert volumes to m3 [m3 H-1 L-2 ~> 1] or cell
891 ! masses to kg [kg H-1 L-2 ~> 1], depending on whether the Boussinesq approximation is used
8920 real :: tr_inv(SZI_(G),SZJ_(G),SZK_(GV)) ! Volumetric or mass-based tracer inventory in
893 ! each cell [conc m3] or [conc kg]
894 real :: total_inv ! The total amount of tracer [conc m3] or [conc kg]
895 integer :: is, ie, js, je, nz
896 integer :: i, j, k, m
897
8980 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
8990 vol_scale = GV%H_to_MKS*G%US%L_to_m**2
9000 do m=1,ntr
9010 do k=1,nz ; do j=js,je ; do i=is,ie
902 tr_inv(i,j,k) = Tr(m)%conc_scale*Tr(m)%t(i,j,k) * &
9030 (vol_scale * h(i,j,k) * G%areaT(i,j)*G%mask2dT(i,j))
904 enddo ; enddo ; enddo
9050 total_inv = reproducing_sum(tr_inv, is+(1-G%isd), ie+(1-G%isd), js+(1-G%jsd), je+(1-G%jsd))
9060 if (is_root_pe()) write(0,'(A,1X,A5,1X,ES25.16,1X,A)') &
9070 "h-point: inventory", Tr(m)%name, total_inv, mesg
908 enddo
909
9100end subroutine tracer_array_chkinv
911
912
913!> Calculates and prints the global inventory of all tracers in the registry.
9140subroutine tracer_Reg_chkinv(mesg, G, GV, h, Reg)
915 character(len=*), intent(in) :: mesg !< message that appears on the chksum lines
916 type(ocean_grid_type), intent(in) :: G !< ocean grid structure
917 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure
918 type(tracer_registry_type), pointer :: Reg !< pointer to the tracer registry
919 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
920
921 ! Local variables
922 real :: vol_scale ! The dimensional scaling factor to convert volumes to m3 [m3 H-1 L-2 ~> 1] or cell
923 ! masses to kg [kg H-1 L-2 ~> 1], depending on whether the Boussinesq approximation is used
9240 real :: tr_inv(SZI_(G),SZJ_(G),SZK_(GV)) ! Volumetric or mass-based tracer inventory in
925 ! each cell [conc m3] or [conc kg]
926 real :: total_inv ! The total amount of tracer [conc m3] or [conc kg]
927 integer :: is, ie, js, je, nz
928 integer :: i, j, k, m
929
9300 if (.not.associated(Reg)) return
931
9320 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
9330 vol_scale = GV%H_to_MKS*G%US%L_to_m**2
9340 do m=1,Reg%ntr
9350 do k=1,nz ; do j=js,je ; do i=is,ie
936 tr_inv(i,j,k) = Reg%Tr(m)%conc_scale*Reg%Tr(m)%t(i,j,k) * &
9370 (vol_scale * h(i,j,k) * G%areaT(i,j)*G%mask2dT(i,j))
938 enddo ; enddo ; enddo
9390 total_inv = reproducing_sum(tr_inv, is+(1-G%isd), ie+(1-G%isd), js+(1-G%jsd), je+(1-G%jsd))
9400 if (is_root_pe()) write(0,'(A,1X,A5,1X,ES25.16,1X,A)') &
9410 "h-point: inventory", Reg%Tr(m)%name, total_inv, mesg
942 enddo
943
9440end subroutine tracer_Reg_chkinv
945
946
947!> Find a tracer in the tracer registry by name.
9480subroutine tracer_name_lookup(Reg, n, tr_ptr, name)
949 type(tracer_registry_type), pointer :: Reg !< pointer to tracer registry
950 type(tracer_type), pointer :: tr_ptr !< target or pointer to the tracer array
951 character(len=32), intent(in) :: name !< tracer name
952 integer, intent(out) :: n !< index to tracer registery
953
9540 do n=1,Reg%ntr
9550 if (lowercase(Reg%Tr(n)%name) == lowercase(name)) then
9560 tr_ptr => Reg%Tr(n)
9570 return
958 endif
959 enddo
960
9610 call MOM_error(FATAL,"MOM cannot find registered tracer: "//name)
962
963end subroutine tracer_name_lookup
964
965!> Initialize the tracer registry.
9661subroutine tracer_registry_init(param_file, Reg)
967 type(param_file_type), intent(in) :: param_file !< open file to parse for model parameters
968 type(tracer_registry_type), pointer :: Reg !< pointer to tracer registry
969
970 integer, save :: init_calls = 0
971
972! This include declares and sets the variable "version".
973#include "version_variable.h"
974 character(len=40) :: mdl = "MOM_tracer_registry" ! This module's name.
975 character(len=256) :: mesg ! Message for error messages.
976
97751 if (.not.associated(Reg)) then ; allocate(Reg)
9780 else ; return ; endif
979
980 ! Read all relevant parameters and write them to the model log.
9811 call log_version(param_file, mdl, version, "", all_default=.true.)
982
9831 init_calls = init_calls + 1
9841 if (init_calls > 1) then
985 write(mesg,'("tracer_registry_init called ",I0, &
9860 &" times with different registry pointers.")') init_calls
9870 if (is_root_pe()) call MOM_error(WARNING,"MOM_tracer "//mesg)
988 endif
989
990end subroutine tracer_registry_init
991
992
993!> This routine closes the tracer registry module.
9941subroutine tracer_registry_end(Reg)
995 type(tracer_registry_type), pointer :: Reg !< The tracer registry that will be deallocated
9961 if (associated(Reg)) deallocate(Reg)
9971end subroutine tracer_registry_end
998
9990end module MOM_tracer_registry