← 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.
550subroutine 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
1450 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
1500 Reg%ntr = Reg%ntr + 1
151
1520 Tr => Reg%Tr(Reg%ntr)
1530 if (present(Tr_out)) Tr_out => Reg%Tr(Reg%ntr)
154
1550 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))
1710 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, &
1740 cmor_longname=Tr%cmor_longname, caller="register_tracer")
1750 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
1810 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
1850 Tr%conc_scale = 1.0
1860 if (present(conc_scale)) Tr%conc_scale = conc_scale
187
1880 Tr%conc_underflow = 0.0
1890 if (present(underflow_conc)) Tr%conc_underflow = underflow_conc
190
1910 Tr%flux_nameroot = Tr%name
1920 if (present(flux_nameroot)) then
1930 if (len_trim(flux_nameroot) > 0) Tr%flux_nameroot = flux_nameroot
194 endif
195
1960 Tr%flux_longname = Tr%longname
1970 if (present(flux_longname)) then
1980 if (len_trim(flux_longname) > 0) Tr%flux_longname = flux_longname
199 endif
200
2010 Tr%net_surfflux_name = "KPP_net"//trim(Tr%name)
2020 if (present(net_surfflux_name)) then
2030 Tr%net_surfflux_name = net_surfflux_name
204 endif
205
2060 Tr%NLT_budget_name = 'KPP_NLT_'//trim(Tr%flux_nameroot)//'_budget'
2070 if (present(NLT_budget_name)) then
2080 Tr%NLT_budget_name = NLT_budget_name
209 endif
210
211 Tr%net_surfflux_longname = 'Effective net surface '//trim(lowercase(Tr%flux_longname))//&
2120 ' flux, as used by [CVMix] KPP'
2130 if (present(net_surfflux_longname)) then
2140 Tr%net_surfflux_longname = net_surfflux_longname
215 endif
216
2170 Tr%flux_units = ""
2180 if (present(flux_units)) Tr%flux_units = flux_units
219
2200 Tr%flux_scale = GV%H_to_MKS*Tr%conc_scale
2210 if (present(flux_scale)) Tr%flux_scale = flux_scale
222
2230 Tr%conv_units = ""
2240 if (present(convergence_units)) Tr%conv_units = convergence_units
225
2260 Tr%cmor_tendprefix = ""
2270 if (present(cmor_tendprefix)) Tr%cmor_tendprefix = cmor_tendprefix
228
2290 Tr%conv_scale = GV%H_to_MKS*Tr%conc_scale
2300 if (present(convergence_scale)) then
2310 Tr%conv_scale = convergence_scale
2320 elseif (present(flux_scale)) then
2330 Tr%conv_scale = flux_scale
234 endif
235
2360 Tr%diag_form = 1
2370 if (present(diag_form)) Tr%diag_form = diag_form
238
2390 Tr%advect_scheme = -1
2400 if (present(advect_scheme)) Tr%advect_scheme = advect_scheme
241
2420 Tr%t => tr_ptr
243
2440 if (present(registry_diags)) Tr%registry_diags = registry_diags
245
2460 if (present(ad_x)) then ; if (associated(ad_x)) Tr%ad_x => ad_x ; endif
2470 if (present(ad_y)) then ; if (associated(ad_y)) Tr%ad_y => ad_y ; endif
2480 if (present(df_x)) then ; if (associated(df_x)) Tr%df_x => df_x ; endif
2490 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
2530 if (present(ad_2d_x)) then ; if (associated(ad_2d_x)) Tr%ad2d_x => ad_2d_x ; endif
2540 if (present(ad_2d_y)) then ; if (associated(ad_2d_y)) Tr%ad2d_y => ad_2d_y ; endif
2550 if (present(df_2d_x)) then ; if (associated(df_2d_x)) Tr%df2d_x => df_2d_x ; endif
256
2570 if (present(advection_xy)) then
2580 if (associated(advection_xy)) Tr%advection_xy => advection_xy
259 endif
260
2610 if (present(restart_CS)) then
262 ! Register this tracer to be read from and written to restart files.
2630 mand = .true. ; if (present(mandatory)) mand = mandatory
264
265 call register_restart_field(tr_ptr, Tr%name, mand, restart_CS, &
2660 longname=Tr%longname, units=Tr%units, conversion=conc_scale)
267 endif
2680end 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
3271 do m=1,nTr_in ; if (Reg%Tr(m)%registry_diags) then
3280 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")
3320 name = Tr%name ; units=adjustl(Tr%units) ; longname = Tr%longname
3330 cmorname = Tr%cmor_name ; cmor_longname = Tr%cmor_longname
3340 shortnm = Tr%flux_nameroot
3350 flux_longname = Tr%flux_longname
3360 if (len_trim(cmor_longname) == 0) cmor_longname = longname
337
3380 if (len_trim(Tr%flux_units) > 0) then ; flux_units = Tr%flux_units
3390 elseif (GV%Boussinesq) then ; flux_units = trim(units)//" m3 s-1"
3400 else ; flux_units = trim(units)//" kg s-1" ; endif
341
3420 if (len_trim(Tr%conv_units) > 0) then ; conv_units = Tr%conv_units
3430 elseif (GV%Boussinesq) then ; conv_units = trim(units)//" m s-1"
3440 else ; conv_units = trim(units)//" kg m-2 s-1" ; endif
345
3460 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, &
3530 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", &
3580 trim(units), conversion=Tr%conc_scale)
3590 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', &
3630 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', &
3670 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', &
3710 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', &
3750 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., &
3790 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., &
3840 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), &
3880 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), &
3910 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, &
3950 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, &
3990 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, &
4040 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, &
4090 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), &
4140 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", &
4180 trim(units) // " m")
419 Tr%id_surf = register_diag_field("ocean_model", trim(shortnm)//"_SURF", &
4200 diag%axesT1, Time, "Surface values of "// trim(longname), trim(units))
4210 if (Tr%id_adx > 0) call safe_alloc_ptr(Tr%ad_x,IsdB,IedB,jsd,jed,nz)
4220 if (Tr%id_ady > 0) call safe_alloc_ptr(Tr%ad_y,isd,ied,JsdB,JedB,nz)
4230 if (Tr%id_dfx > 0) call safe_alloc_ptr(Tr%df_x,IsdB,IedB,jsd,jed,nz)
4240 if (Tr%id_dfy > 0) call safe_alloc_ptr(Tr%df_y,isd,ied,JsdB,JedB,nz)
4250 if (Tr%id_hbd_dfx > 0) call safe_alloc_ptr(Tr%hbd_dfx,IsdB,IedB,jsd,jed,nz)
4260 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), &
4310 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), &
4350 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, &
4400 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, &
4450 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, &
4490 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, &
4530 x_cell_method='sum')
454
4550 if (Tr%id_adx_2d > 0) call safe_alloc_ptr(Tr%ad2d_x,IsdB,IedB,jsd,jed)
4560 if (Tr%id_ady_2d > 0) call safe_alloc_ptr(Tr%ad2d_y,isd,ied,JsdB,JedB)
4570 if (Tr%id_dfx_2d > 0) call safe_alloc_ptr(Tr%df2d_x,IsdB,IedB,jsd,jed)
4580 if (Tr%id_dfy_2d > 0) call safe_alloc_ptr(Tr%df2d_y,isd,ied,JsdB,JedB)
4590 if (Tr%id_hbd_dfx_2d > 0) call safe_alloc_ptr(Tr%hbd_dfx_2d,IsdB,IedB,jsd,jed)
4600 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)), &
4660 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 '//&
4700 trim(lowercase(flux_longname)), conv_units, conversion=Tr%conv_scale*US%s_to_T)
4710 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)), &
4770 trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
478
4790 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
4870 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, &
4910 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, &
4970 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, &
5030 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, &
5090 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))//&
5130 ' 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)), &
5200 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))//&
5240 ' 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)), &
5320 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, &
5380 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, &
5440 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), &
5480 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), &
5530 trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
554
5550 var_lname = "Net time tendency for "//lowercase(flux_longname)
5560 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, &
5590 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)), &
5620 conv_units, conversion=Tr%conv_scale*US%s_to_T)
563 else
564 cmor_var_lname = "Tendency of "//trim(cmor_longname)//" Expressed as "//&
5650 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, &
5700 v_extensive=.true.)
5710 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", &
5760 cmor_standard_name=cmor_long_std(cmor_var_lname), cmor_long_name=cmor_var_lname)
577 endif
5780 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
5860 if (use_ALE .and. Tr%remap_tr) then
5870 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, &
5900 trim(units)//' s-1', conversion=Tr%conc_scale*US%s_to_T)
591
5920 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', &
5950 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 "//&
5980 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', &
6010 diag%axesT1, Time, var_lname, conv_units, conversion=Tr%conv_scale*US%s_to_T)
602
603 endif
604
6050 if (use_ALE .and. (Reg%ntr<MAX_FIELDS_) .and. Tr%remap_tr) then
6060 unit2 = trim(units)//"2"
6070 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), &
6100 trim(unit2)//" s-1", conversion=Tr%conc_scale**2*US%s_to_T)
6110 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
6270 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
6550subroutine 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
6610 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
662
6630 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
6710end subroutine preALE_tracer_diagnostics
672
6730subroutine 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
6800 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
6830 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
6860 Idt = 0.0 ; if (dt /= 0.0) Idt = 1.0 / dt
687
6880 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
6980end 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')
702360subroutine 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
712720 real :: work3d(SZI_(G),SZJ_(G),SZK_(GV)) ! The time tendency of a diagnostic [CU T-1 ~> conc s-1]
713720 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
718360 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
719
720360 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
723360 call diag_save_grids(diag)
724360 call diag_copy_storage_to_diag(diag, diag_prev)
725360 do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
7260 Tr => Reg%Tr(m)
7270 if (Tr%id_tr > 0) call post_data(Tr%id_tr, Tr%t, diag)
7280 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
7360 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
752360 call diag_restore_grids(diag)
753
754360end subroutine post_tracer_diagnostics_at_sync
755
756!> Post the advective and diffusive tendencies
757360subroutine 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
766720 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]
768720 real :: frac_under_100m(SZI_(G),SZJ_(G),SZK_(GV)) ! weights used to compute 100m vertical integrals [nondim]
769720 real :: ztop(SZI_(G),SZJ_(G)) ! position of the top interface [H ~> m or kg m-2]
770720 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
773360 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
7761832760 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)
780360 khi = 0
781360 do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
7820 Tr => Reg%Tr(m)
7830 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
804360 do m=1,Reg%ntr ; if (Reg%Tr(m)%registry_diags) then
8050 Tr => Reg%Tr(m)
8060 if (Tr%id_tr_post_horzn> 0) call post_data(Tr%id_tr_post_horzn, Tr%t, diag)
8070 if (Tr%id_adx > 0) call post_data(Tr%id_adx, Tr%ad_x, diag, alt_h=h_diag)
8080 if (Tr%id_ady > 0) call post_data(Tr%id_ady, Tr%ad_y, diag, alt_h=h_diag)
8090 if (Tr%id_dfx > 0) call post_data(Tr%id_dfx, Tr%df_x, diag, alt_h=h_diag)
8100 if (Tr%id_dfy > 0) call post_data(Tr%id_dfy, Tr%df_y, diag, alt_h=h_diag)
8110 if (Tr%id_adx_2d > 0) call post_data(Tr%id_adx_2d, Tr%ad2d_x, diag)
8120 if (Tr%id_ady_2d > 0) call post_data(Tr%id_ady_2d, Tr%ad2d_y, diag)
8130 if (Tr%id_dfx_2d > 0) call post_data(Tr%id_dfx_2d, Tr%df2d_x, diag)
8140 if (Tr%id_dfy_2d > 0) call post_data(Tr%id_dfy_2d, Tr%df2d_y, diag)
8150 if (Tr%id_adv_xy > 0) call post_data(Tr%id_adv_xy, Tr%advection_xy, diag, alt_h=h_diag)
8160 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
8260 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
8350 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
8440 if (Tr%id_SURF > 0) call post_data(Tr%id_SURF, Tr%t(:,:,1), diag)
845 endif ; enddo
846
847360end 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