ROMS
Loading...
Searching...
No Matches
hypoxia_srm.h
Go to the documentation of this file.
1 MODULE biology_mod
2!
3!git $Id$
4!================================================== Hernan G. Arango ===
5! Copyright (c) 2002-2025 The ROMS Group !
6! Licensed under a MIT/X style license !
7! See License_ROMS.md !
8!=======================================================================
9! !
10! This routine computes the biological sources and sinks for the !
11! Hypoxia Simple Respiration Model for dissolved oxygen. Then, it !
12! adds those terms to the global biological fields. !
13! !
14! This simple formulation is based on the work of Malcolm Scully. The !
15! dissolved oxygen (DO) is respired at a constant rate. A constant !
16! magnitude of DO is respired during each time step and the DO is not !
17! allowed to go negative. !
18! !
19! Dissolved Oxygen can either be added to the surface as a flux or !
20! the surface layer DO can be set to saturation based on the layer !
21! temperature and salinity. !
22! !
23! The surface oxygen saturation formulation is the same as in the !
24! Fennel ecosystem model. The surface oxygen flux is different from !
25! the method used by the below citations. Setting the surface DO to !
26! saturation is very similar to the ChesROMS-CRM method in Irby et. !
27! al. (2016). !
28! !
29! Scully, M.E., 2010: Wind Modulation of Dissolved Oxygen in !
30! Chesapeake Bay, Estuaries and Coasts, 33, 1164-1175. !
31! !
32! Scully, M.E., 2013: Physical control on hypoxia in Chesapeake !
33! Bay: A numerical modeling study, J. Geophys. Res., 118, !
34! 1239-1256. !
35! !
36! Irby, I. et al., 2016: Challenges associated with modeling low- !
37! oxygen waters in Chesapeake Bay: a multiple model comparison, !
38! Biogeosciences, 13, 2011-2028 !
39! !
40!=======================================================================
41!
42 implicit none
43!
44 PRIVATE
45 PUBLIC :: biology
46!
47 CONTAINS
48!
49!***********************************************************************
50 SUBROUTINE biology (ng,tile)
51!***********************************************************************
52!
53 USE mod_param
54#ifdef DIAGNOSTICS_BIO
55 USE mod_diags
56#endif
57 USE mod_forces
58 USE mod_grid
59 USE mod_ncparam
60 USE mod_ocean
61 USE mod_stepping
62!
63 implicit none
64!
65! Imported variable declarations.
66!
67 integer, intent(in) :: ng, tile
68!
69! Local variable declarations.
70!
71 character (len=*), parameter :: MyFile = &
72 & __FILE__
73!
74#include "tile.h"
75!
76! Set header file name.
77!
78#ifdef DISTRIBUTE
79 IF (lbiofile(inlm)) THEN
80#else
81 IF (lbiofile(inlm).and.(tile.eq.0)) THEN
82#endif
83 lbiofile(inlm)=.false.
84 bioname(inlm)=myfile
85 END IF
86!
87#ifdef PROFILE
88 CALL wclock_on (ng, inlm, 15, __line__, myfile)
89#endif
90 CALL hypoxia_srm_tile (ng, tile, &
91 & lbi, ubi, lbj, ubj, n(ng), nt(ng), &
92 & imins, imaxs, jmins, jmaxs, &
93 & nstp(ng), nnew(ng), &
94#ifdef MASKING
95 & grid(ng) % rmask, &
96# if defined WET_DRY && defined DIAGNOSTICS_BIO
97 & grid(ng) % rmask_full, &
98# endif
99#endif
100 & grid(ng) % Hz, &
101#ifdef BULK_FLUXES
102 & forces(ng) % Uwind, &
103 & forces(ng) % Vwind, &
104#else
105 & forces(ng) % sustr, &
106 & forces(ng) % svstr, &
107#endif
108 & ocean(ng) % respiration, &
109#ifdef DIAGNOSTICS_BIO
110 & diags(ng) % DiaBio2d, &
111#endif
112 & ocean(ng) % t)
113
114#ifdef PROFILE
115 CALL wclock_off (ng, inlm, 15, __line__, myfile)
116#endif
117!
118 RETURN
119 END SUBROUTINE biology
120!
121!-----------------------------------------------------------------------
122 SUBROUTINE hypoxia_srm_tile (ng, tile, &
123 & LBi, UBi, LBj, UBj, UBk, UBt, &
124 & IminS, ImaxS, JminS, JmaxS, &
125 & nstp, nnew, &
126#ifdef MASKING
127 & rmask, &
128# if defined WET_DRY && defined DIAGNOSTICS_BIO
129 & rmask_full, &
130# endif
131#endif
132 & Hz, &
133#ifdef BULK_FLUXES
134 & Uwind, Vwind, &
135#else
136 & sustr, svstr, &
137#endif
138 & respiration, &
139#ifdef DIAGNOSTICS_BIO
140 & DiaBio2d, &
141#endif
142 & t)
143!-----------------------------------------------------------------------
144!
145 USE mod_param
146 USE mod_biology
147 USE mod_ncparam
148 USE mod_scalars
149!
150! Imported variable declarations.
151!
152 integer, intent(in) :: ng, tile
153 integer, intent(in) :: LBi, UBi, LBj, UBj, UBk, UBt
154 integer, intent(in) :: IminS, ImaxS, JminS, JmaxS
155 integer, intent(in) :: nstp, nnew
156
157#ifdef ASSUMED_SHAPE
158# ifdef MASKING
159 real(r8), intent(in) :: rmask(LBi:,LBj:)
160# if defined WET_DRY && defined DIAGNOSTICS_BIO
161 real(r8), intent(in) :: rmask_full(LBi:,LBj:)
162# endif
163# endif
164 real(r8), intent(in) :: Hz(LBi:,LBj:,:)
165# ifdef BULK_FLUXES
166 real(r8), intent(in) :: Uwind(LBi:,LBj:)
167 real(r8), intent(in) :: Vwind(LBi:,LBj:)
168# else
169 real(r8), intent(in) :: sustr(LBi:,LBj:)
170 real(r8), intent(in) :: svstr(LBi:,LBj:)
171# endif
172 real(r8), intent(in) :: respiration(LBi:,LBj:,:)
173# ifdef DIAGNOSTICS_BIO
174 real(r8), intent(inout) :: DiaBio2d(LBi:,LBj:,:)
175# endif
176 real(r8), intent(inout) :: t(LBi:,LBj:,:,:,:)
177#else
178# ifdef MASKING
179 real(r8), intent(in) :: rmask(LBi:UBi,LBj:UBj)
180# if defined WET_DRY && defined DIAGNOSTICS_BIO
181 real(r8), intent(in) :: rmask_full(LBi:UBi,LBj:UBj)
182# endif
183# endif
184 real(r8), intent(in) :: Hz(LBi:UBi,LBj:UBj,UBk)
185# ifdef BULK_FLUXES
186 real(r8), intent(in) :: Uwind(LBi:UBi,LBj:UBj)
187 real(r8), intent(in) :: Vwind(LBi:UBi,LBj:UBj)
188# else
189 real(r8), intent(in) :: sustr(LBi:UBi,LBj:UBj)
190 real(r8), intent(in) :: svstr(LBi:UBi,LBj:UBj)
191# endif
192 real(r8), intent(inout) :: repiration(LBi:UBi,LBj:UBj,UBk)
193# ifdef DIAGNOSTICS_BIO
194 real(r8), intent(inout) :: DiaBio2d(LBi:UBi,LBj:UBj,NDbio2d)
195# endif
196 real(r8), intent(inout) :: t(LBi:UBi,LBj:UBj,UBk,3,UBt)
197#endif
198!
199! Local variable declarations.
200!
201 integer :: Iter, i, ibio, itrc, j, k
202
203 real(r8) :: u10squ
204
205 real(r8), parameter :: OA0 = 2.00907_r8 ! Oxygen
206 real(r8), parameter :: OA1 = 3.22014_r8 ! saturation
207 real(r8), parameter :: OA2 = 4.05010_r8 ! coefficients
208 real(r8), parameter :: OA3 = 4.94457_r8
209 real(r8), parameter :: OA4 =-0.256847_r8
210 real(r8), parameter :: OA5 = 3.88767_r8
211 real(r8), parameter :: OB0 =-0.00624523_r8
212 real(r8), parameter :: OB1 =-0.00737614_r8
213 real(r8), parameter :: OB2 =-0.0103410_r8
214 real(r8), parameter :: OB3 =-0.00817083_r8
215 real(r8), parameter :: OC0 =-0.000000488682_r8
216 real(r8), parameter :: rOxNO3= 8.625_r8 ! 138/16
217 real(r8), parameter :: rOxNH4= 6.625_r8 ! 106/16
218 real(r8) :: l2mol = 1000.0_r8/22.3916_r8 ! liter to mol
219
220 real(r8) :: TS, AA
221
222 real(r8) :: cff, cff1, cff2, cff3, cff4
223 real(r8) :: dtdays
224
225#ifdef DIAGNOSTICS_BIO
226 real(r8) :: fiter
227#endif
228
229 real(r8) :: SchmidtN_Ox, O2satu, O2_Flux
230
231 real(r8), dimension(IminS:ImaxS,N(ng),NT(ng)) :: Bio
232 real(r8), dimension(IminS:ImaxS,N(ng),NT(ng)) :: Bio_old
233
234 real(r8), dimension(IminS:ImaxS,N(ng)) :: Hz_inv
235
236#include "set_bounds.h"
237
238#ifdef DIAGNOSTICS_BIO
239!
240!-----------------------------------------------------------------------
241! If appropriate, initialize time-averaged diagnostic arrays.
242!-----------------------------------------------------------------------
243!
244 IF (((iic(ng).gt.ntsdia(ng)).and. &
245 & (mod(iic(ng),ndia(ng)).eq.1)).or. &
246 & ((iic(ng).ge.ntsdia(ng)).and.(ndia(ng).eq.1)).or. &
247 & ((nrrec(ng).gt.0).and.(iic(ng).eq.ntstart(ng)))) THEN
248 DO ivar=1,ndbio2d
249 DO j=jstr,jend
250 DO i=istr,iend
251 diabio2d(i,j,ivar)=0.0_r8
252 END DO
253 END DO
254 END DO
255 END IF
256#endif
257!
258!-----------------------------------------------------------------------
259! Add biological Source/Sink terms.
260!-----------------------------------------------------------------------
261!
262! Avoid computing source/sink terms if no biological iterations.
263!
264 IF (bioiter(ng).le.0) RETURN
265!
266! Set time-stepping according to the number of iterations.
267!
268 dtdays=dt(ng)*sec2day/real(bioiter(ng),r8)
269
270#ifdef DIAGNOSTICS_BIO
271!
272! A factor to account for the number of iterations in accumulating
273! diagnostic rate variables.
274!
275 fiter=1.0_r8/real(bioiter(ng),r8)
276#endif
277!
278! Compute inverse thickness to avoid repeated divisions.
279!
280 j_loop : DO j=jstr,jend
281 DO k=1,n(ng)
282 DO i=istr,iend
283 hz_inv(i,k)=1.0_r8/hz(i,j,k)
284 END DO
285 END DO
286!
287! Extract biological variables from tracer arrays, place them into
288! scratch arrays, and restrict their values to be positive definite.
289! At input, all tracers (index nnew) from predictor step have
290! transport units (m Tunits) since we do not have yet the new
291! values for zeta and Hz. These are known after the 2D barotropic
292! time-stepping.
293!
294 DO itrc=1,nbt
295 ibio=idbio(itrc)
296 DO k=1,n(ng)
297 DO i=istr,iend
298 bio_old(i,k,ibio)=max(0.0_r8,t(i,j,k,nstp,ibio))
299 bio(i,k,ibio)=bio_old(i,k,ibio)
300 END DO
301 END DO
302 END DO
303!
304! Extract potential temperature and salinity.
305!
306 DO k=1,n(ng)
307 DO i=istr,iend
308 bio(i,k,itemp)=min(t(i,j,k,nstp,itemp),35.0_r8)
309 bio(i,k,isalt)=max(t(i,j,k,nstp,isalt), 0.0_r8)
310 END DO
311 END DO
312!
313!=======================================================================
314! Start internal iterations to achieve convergence of the nonlinear
315! backward-implicit solution.
316!=======================================================================
317!
318! The iterative loop below is to iterate toward an universal Backward-
319! Euler treatment of all terms. So if there are oscillations in the
320! system, they are only physical oscillations. These iterations,
321! however, do not improve the accuaracy of the solution.
322!
323 iter_loop: DO iter=1,bioiter(ng)
324!
325!-----------------------------------------------------------------------
326! Total biological respiration.
327!-----------------------------------------------------------------------
328!
329! The 3D respiration rate (millimole/m3/day) is processed as an input
330! field elsewhere. It can be read from a forcing NetCDF file and
331! interpolate between time snapshots or set with analytical functions.
332! It is assumed that has zero values in places with no respiration.
333!
334 DO k=1,n(ng)
335 DO i=istr,iend
336 cff1=dtdays*respiration(i,j,k)
337 bio(i,k,ioxyg)=bio(i,k,ioxyg)-cff1
338 bio(i,k,ioxyg)=max(bio(i,k,ioxyg),0.0_r8)
339 END DO
340 END DO
341
342#ifdef SURFACE_DO_SATURATION
343!
344!-----------------------------------------------------------------------
345! Setting surface layer O2 to saturation.
346!-----------------------------------------------------------------------
347!
348! Calculate O2 saturation concentration using Garcia and Gordon
349! L and O (1992) formula, (EXP(AA) is in ml/l).
350!
351 k=n(ng)
352 DO i=istr,iend
353 ts=log((298.15_r8-bio(i,k,itemp))/ &
354 & (273.15_r8+bio(i,k,itemp)))
355 aa=oa0+ts*(oa1+ts*(oa2+ts*(oa3+ts*(oa4+ts*oa5))))+ &
356 & bio(i,k,isalt)*(ob0+ts*(ob1+ts*(ob2+ts*ob3)))+ &
357 & oc0*bio(i,k,isalt)*bio(i,k,isalt)
358 o2satu=l2mol*exp(aa) ! convert from ml/l to mmol/m3
359 bio(i,k,ioxyg)=o2satu
360 END DO
361#else
362!
363!-----------------------------------------------------------------------
364! Surface O2 gas exchange (same as Fennel model).
365!-----------------------------------------------------------------------
366!
367! Compute surface O2 gas exchange.
368!
369 cff2=rho0*550.0_r8
370 cff3=dtdays*0.31_r8*24.0_r8/100.0_r8
371 k=n(ng)
372 DO i=istr,iend
373!
374! Compute O2 transfer velocity : u10squared (u10 in m/s)
375!
376# ifdef BULK_FLUXES
377 u10squ=uwind(i,j)*uwind(i,j)+vwind(i,j)*vwind(i,j)
378# else
379 u10squ=cff2*sqrt((0.5_r8*(sustr(i,j)+sustr(i+1,j)))**2+ &
380 & (0.5_r8*(svstr(i,j)+svstr(i,j+1)))**2)
381# endif
382# ifdef OCMIP_OXYGEN_SC
383!
384! Alternative formulation for Schmidt number (Sc will be slightly
385! smaller up to about 35 C): Compute the Schmidt number of oxygen
386! in seawater using the formulation proposed by Keeling et al.
387! (1998, Global Biogeochem. Cycles, 12, 141-163). Input temperature
388! in Celsius.
389!
390 schmidtn_ox=1638.0_r8- &
391 & bio(i,k,itemp)*(81.83_r8- &
392 & bio(i,k,itemp)* &
393 & (1.483_r8- &
394 & bio(i,k,itemp)*0.008004_r8))
395# else
396!
397! Calculate the Schmidt number for O2 in sea water (Wanninkhof, 1992).
398!
399 schmidtn_ox=1953.4_r8- &
400 & bio(i,k,itemp)*(128.0_r8- &
401 & bio(i,k,itemp)* &
402 & (3.9918_r8- &
403 & bio(i,k,itemp)*0.050091_r8))
404# endif
405 cff4=cff3*u10squ*sqrt(660.0_r8/schmidtn_ox)
406!
407! Calculate O2 saturation concentration using Garcia and Gordon
408! L and O (1992) formula, (EXP(AA) is in ml/l).
409!
410 ts=log((298.15_r8-bio(i,k,itemp))/ &
411 & (273.15_r8+bio(i,k,itemp)))
412 aa=oa0+ts*(oa1+ts*(oa2+ts*(oa3+ts*(oa4+ts*oa5))))+ &
413 & bio(i,k,isalt)*(ob0+ts*(ob1+ts*(ob2+ts*ob3)))+ &
414 & oc0*bio(i,k,isalt)*bio(i,k,isalt)
415!
416! Convert from ml/l to mmol/m3.
417!
418 o2satu=l2mol*exp(aa)
419!
420! Add in O2 gas exchange.
421!
422 o2_flux=cff4*(o2satu-bio(i,k,ioxyg))
423 bio(i,k,ioxyg)=bio(i,k,ioxyg)+ &
424 & o2_flux*hz_inv(i,k)
425# ifdef DIAGNOSTICS_BIO
426 diabio2d(i,j,io2fx)=diabio2d(i,j,io2fx)+ &
427# ifdef WET_DRY
428 & rmask_full(i,j)* &
429# endif
430 & o2_flux*fiter
431# endif
432 END DO
433#endif
434 END DO iter_loop
435!
436!-----------------------------------------------------------------------
437! Update global tracer variables: Add increment due to BGC processes
438! to tracer array in time index "nnew". Index "nnew" is solution after
439! advection and mixing and has transport units (m Tunits) hence the
440! increment is multiplied by Hz. Notice that we need to subtract
441! original values "Bio_old" at the top of the routine to just account
442! for the concentractions affected by BGC processes. This also takes
443! into account any constraints (non-negative concentrations) specified
444! before entering BGC kernel. If "Bio" were unchanged by BGC processes,
445! the increment would be exactly zero. Notice that final tracer values,
446! t(:,:,:,nnew,:) are not bounded >=0 so that we can preserve total
447! inventory of nutrients even when advection causes tracer
448! concentration to go negative.
449!-----------------------------------------------------------------------
450!
451 DO itrc=1,nbt
452 ibio=idbio(itrc)
453 DO k=1,n(ng)
454 DO i=istr,iend
455 cff=bio(i,k,ibio)-bio_old(i,k,ibio)
456 t(i,j,k,nnew,ibio)=t(i,j,k,nnew,ibio)+cff*hz(i,j,k)
457 END DO
458 END DO
459 END DO
460 END DO j_loop
461!
462 RETURN
463 END SUBROUTINE hypoxia_srm_tile
464
465 END MODULE biology_mod
subroutine hypoxia_srm_tile(ng, tile, lbi, ubi, lbj, ubj, ubk, ubt, imins, imaxs, jmins, jmaxs, nstp, nnew, rmask, rmask_full, hz, uwind, vwind, sustr, svstr, respiration, diabio2d, t)
subroutine, public biology(ng, tile)
Definition ecosim.h:57
integer, dimension(:), allocatable bioiter
Definition ecosim_mod.h:343
integer, dimension(:), allocatable idbio
Definition ecosim_mod.h:256
integer ioxyg
Definition fennel_mod.h:98
integer io2fx
Definition fennel_mod.h:110
type(t_diags), dimension(:), allocatable diags
Definition mod_diags.F:100
type(t_forces), dimension(:), allocatable forces
Definition mod_forces.F:554
type(t_grid), dimension(:), allocatable grid
Definition mod_grid.F:365
type(t_ocean), dimension(:), allocatable ocean
Definition mod_ocean.F:351
integer, parameter inlm
Definition mod_param.F:662
integer, dimension(:), allocatable n
Definition mod_param.F:479
integer nbt
Definition mod_param.F:509
integer, dimension(:), allocatable nt
Definition mod_param.F:489
integer, dimension(:), allocatable nrrec
integer, dimension(:), allocatable iic
real(dp), dimension(:), allocatable dt
real(dp), parameter sec2day
integer isalt
integer itemp
real(dp) rho0
integer, dimension(:), allocatable ntstart
integer, dimension(:), allocatable ndia
integer, dimension(:), allocatable ntsdia
integer, dimension(:), allocatable nnew
integer, dimension(:), allocatable nstp
recursive subroutine wclock_off(ng, model, region, line, routine)
Definition timers.F:148
recursive subroutine wclock_on(ng, model, region, line, routine)
Definition timers.F:3